How to fill Primefaces Autocomplete from Managed Bean - primefaces

I have a primefaces autocomplete which is shown below:
<p:autoComplete value="#{consultaEventoCargoMBean.idUaSelecionada}" completeMethod="#{consultaEventoCargoMBean.completeUADestino}" var="ua" itemLabel="#{ua.name}" itemValue="#{ua.id}" forceSelection="false" appendTo="#this" />
Where idUaSelecionada and ua.id are Integer and ua.name is a String.
My issue is when I load my page to update the data. My idUaSelecionada is already filled and a object ready to be passed as suggestion (it would be a list of a only element). I was not able to find a way to fill this autocomplete - at least the text component.
Reading the Autocomplete souce code, I saw that there is a List called suggestions. Although there is not a setter for this list, it has a getter. But if I try something like autocomplete.getSuggestions().add(...) I get a NullPointerException. The only way I found to fill this list is through the method indicated by completeMethod tag attribute, who receives a String and returns a List.
I was not able to find in internet how to fill this List. That's why I am asking this to see if anyone has any answers.
Thanks,
Rafael Afonso

You can use JavaScript to trigger execution of method defined in completeMethod tag attribute and load suggestion(s).
Supposing that you want to do it when page loads, add this script to your page
<script>
function triggerSuggestionLoading() {
//finds autocomplete input text field
var autocomplete = document.getElementsByClassName('ui-autocomplete-input')[0];
//sets autocomplete query, for example
autocomplete.value="test";
//simulates key press triggering completeMethod to be executed
autocomplete.dispatchEvent(new Event('keydown'));
autocomplete.dispatchEvent(new Event('input'));
autocomplete.dispatchEvent(new Event('keyup'));
//sets focus on autocomplete
autocomplete.dispatchEvent(new Event('focus'));
//clears autocomplete text
autocomplete.value="";
}
window.onload = function() {
triggerSuggestionLoading();
};
</script>
Actually whenever you refresh your suggestion list and want to load suggestions into p:autocomplete just call triggerSuggestionLoading() method at the end (after all procedures are finished).
I've tested it in Chrome and Edge.

Related

How to fix Angular bug requiring user to click a separate element before choosing a second mat chip

Here is the link for an example of the issue I will attempt to describe. In the chips autocomplete example, click the text box to select a new fruit.
Now, before clicking anywhere else, click again on the text box as you did before.
This should result in no options showing up. The issue here is that the user must either begin keying in a new selection or first click another element in the window before matchip will show the options to choose from. I am wondering if there is a way to fix this issue. I would like a user to be able to choose a selection from the list and then immediately click the text box as they had before and make a new selection.
I'm using mat-chip-list inside an outer *ngFor iterating over a FormArray.
Here is what I'have done. It's pretty efficient :
<input
#validatorInput
#operationTrigger="matAutocompleteTrigger"
[formControl]="contactCtrl"
[matAutocomplete]="auto"
[matChipInputFor]="chipList"
(blur)="contactCtrl.setValue(''); validatorInput.value='';"
(click)="contactCtrl.setValue(''); validatorInput.value=''; operationTrigger.openPanel()">
The trick is
Always clear your html input and your (shared) formControl with an empty and not null value each time the blur and click events occur.
Do NOT do this 'clear' on the input focus event. (Because when you delete the last chip, the input is auto-focus and you will have the famous Expression has changed after it was checked.
Call operationTrigger.openPanel(); when the user click on the input
Setting contactCtrl.setValue(''); allows your autocomplete panel to be automatically opened when you call operationTrigger.openPanel()
Setting validatorInput.value=''; is just a way to properly sync your formControl with the html input to ensure a good UX/UI behavior.
Inside my formArray, the formControl is the same for all the inputs but it does not matter since the user can only manipulate one input at a given time
Since you didn't post your code and you mention the example on the material site I'm going to do it as a fork of the stackblitz example they have on their site.
But this will allow you to open the autocomplete panel again despite having had the cursor there and choosing an option previously.
// Using MatAutocompleteTrigger will give you access to the API that will allow you to
// to open the panel or keep it open
...
#ViewChild(MatAutocompleteTrigger, {static: false}) trigger: MatAutocompleteTrigger;
...
ngAfterViewInit() {
fromEvent(this.fruitInput.nativeElement, 'click')
.pipe(
tap(() => {
this.trigger.openPanel()
})
).subscribe()
}
Link to the full stackblitz:
https://stackblitz.com/edit/angular-sb38ig

Angular2 function call from html element with no load event (or similiar)

I am new to Angular and have run into a problem that seems to have a javascript work around but they aren't very elegant.
I have a model with an array property. I ngfor the list property to build some html selection options. This is all working nicely. The problem comes when I am trying to set default value...the html elements don't have a load event.
I tried numerous html elements and they don't appear to have a load event either but I certainly could be doing it wrong.
I have seen a solution to put javascript tag right after the html and I could do that but I was really looking for a more elegant way in Angular.
I saw this SO post and thought that was my answer but there is a warning given that I agree with and thus it doesn't appear to be a good solution.
Regardless I tried it just to see if it would work and I got:
Failed to execute 'setAttribute' on 'Element': '{{loadDefaults()}}' is not a valid attribute name
<span {{loadDefaults()}} ></span>
So how can I fire an AS2 function in the component to load the default values?
HTML (btw this is NOT a full page load so there is no body tag):
<tr>
<td *ngFor="let loc of locOptions;">
<span>{{loc.text}}</span>
<input type="radio" name="radiogroup" [value]="loc.value" (change)="onSelectionChange(loc.value)">
</td>
</tr>
Edit
I thought perhaps mistakenly that ngoninit would fire too soon...before the html elements are rendered.
So perhaps what is being suggested is that I add a boolean is default to the model and bind THAT as the element is rendered.
In your ngonit function set this.locOptions to your default values. The value can be changed later on in any function and the change will be reflected in the view. Hope this helps you.
You should use ngOnInit to init you data, and call retrieve your data from your component :
defaults : any;
ngOnInit {
this.defaults = loadDefaults();
}
loadDefaults() {
//get data
}
HTML :
<span>{{defaults}}</span>

How can I customize the filter function for SelectOneMenu

I tried to find on Primefaces Documentation but I have not found how can I customize the filter function for SelectOneMenu.
I add filterMatchMode="custom" filterFunction="#{mainRandevuBean.ilFilter()}"
But I don't know how can I write bean filterFunction.
The filter is a javascript (client-side) function. It all IS in the PrimeFaces documentation, which you should always look into first, carefully, thouroughly.
So use filterFunction="myFilter"
and create a javascript function like
function myFilter(itemLabel, filterValue) {
// return true if this label matches, false otherwise
}
Just as a sidenote: primefaces documentation doesn't say anything semantically about the parameters. It also does not mention where the label comes from (in fact, the docs mention "the item value" which is not very clear).
In fact I used the JavaScript function to debug this in order to figure out what was provided by default as a label.
function filterList(label, filter){
alert("label="+label+" and filter="+filter);
return false;
}
At first I thought it would be anything like the text inside the HTML generated for each list item. But when debugging it I saw the alert said that the label was something like my.package.SomeValueObject#123456 (which is obvously the Java object toString on each item in the list).
You need to define the itemLabel property on the selectItems which is inside the selectManyMenu to generate a proper text value used by the standard filtering mechanisme. As far as I could figure out that is the only reason why you have to put itemLabel there. In the documentation itemLabel is specified before explaining filtering which is confusing.
And as far as I know the itemValue defaults anyhow to just the object value, so I believe following from the documentation is redundant.
itemValue="#{player}"
Hope it helps anyone :.)
I resolve this problem with autocomplete component. Primefaces autocomplete component with dropdown="true" property works like one menu.

in igCombo - How to display in the combo's input the selectedItem's tepmlate

I have an igCombo in durandal project. I load the igCombo through the date-bind property at the dom. I created an itemTemplate for the select element options. I want that where I select any item, the combo's input will show the selectedItem template. Here is my code, but it doesn't work well; it shows in the inpute the follow thing:
[object object]
here is my code:
<span id="combo" data-bind="igCombo: { dataSource: data, textKey: 'name',
valueKey: 'id', width: '400px',
itemTemplate: '${name} | ${id}',
allowCustomValue: true,
selectionChanged: function (evt, ui) {
var concatenatedValue = ui.items.template
ui.owner.text(concatenatedValue);}
}">
</span>
(Please don't answer me that I can simply write in the selectionChanged function the sane piece of code that I wrote in the itemTemplate property, becouse now it is small piece of code, but when it will be longer code- it is not nice to write it twice!!!)
can you help me?
I could try to explain why the combo input would not intentionally use the itemTemplate - the template is meant to be mostly rich HTML content (images, links and whatnot as in this sample http://www.infragistics.com/products/jquery/sample/combo-box/templating) and you can't put that in an input field.
However, in your case you are just using text so it is doable - first the ui.items provided to the event (as the name suggests) is a collection, so take the first one and the items don't have template property unless that is part of your model that I can't see.
Like other Ignite UI controls, the Combo uses the Templating Engine and so can you! Take the itemTemplate from the control and the item from the data source like in this snippet:
function (evt, ui) {
var templatedValue = $.ig.tmpl(ui.owner.options.itemTemplate, ui.owner.options.dataSource[ui.items[0].index]);
ui.owner.text(templatedValue);
}
JSFiddle: http://jsfiddle.net/damyanpetev/tB7Ds/
The templating API is much like the old jQUery templating if you are familiar with that - taking a template and then data object.Using the values from the control itself means you can make them as complicated as you want and write them in one place only, this code doesn't need to change at all.

Difference between HTML event and JavaScript events

There are many ways by which we can attach an event on an HTML element.
The first way is: HTML attribute
<div id="foo" onclick="print2()> My event is attached as HTML attribute</div>
The second way is using some library (say jQuery)
<div id="bar"> My event is attached using jQuery </div>
<script>
$("#bar").click(function() {
alert("Hi this is Bar");
}
</script>
I earlier thought that jQuery might be internally converting the event to corresponding HTML attribute but this does not happen. Check this http://jsfiddle.net/blunderboy/wp4RU/3/
I am logging all the attributes of foo and bar and see bar does not have onclick attribute.
Please explain.
There is nothing called HTML Event! The two types of events you have described are, inline events and unobtrusive events, and both are javascript events.
Inline Events
When you declare javascript code on the elements itself, then it becomes an inline event. You have a few common events (as attributes to HTML Elements) like onclick, onkeydown, onkeypress, onkeyup, and all of them start with on. One such example is:
Click Me!
Unobtrusive Events
We need to assign something to be performed when the event is triggered. The = symbol is always used in JavaScript to assign the value on the right to the method or property on the left.
The window is not the only object we can attach events to. We can attach events to any object within the web page provided that we have a way of uniquely identifying that object. One way of identifying an object is by giving it an ID and referencing it by document.getElementById("id_of_the_element").
Lets take the same example.
Click Me!
Instead of the onclick attribute, I have an ID in the same place, which uniquely identifies the HTML element <a>. Now I can get the ID inside javascript this way:
document.getElementById('clickme');
For this, I can attach an event handler, which doesn't differ much from the way we use the attributes. It just doesn't have the on in the front. In our previous example, we used onclick, but now we are just going to use click.
document.getElementById('clickme').click = functionName;
Here, the functionName refers to any javascript's function name, or an anonymous function. So, for the alert, if we create a function named alertme(), we can define this way:
function alertme()
{
alert('You clicked me!');
}
Now to attach the function to the element can be done this way:
document.getElementById('clickme').click = alertme;
Still feeling lazy, you can do it using the anonymous function way, which takes no name:
document.getElementById('clickme').click = function () {
alert('You clicked me!');
}
Hope you understood. :) Let me know for further clarification.