AutoComplete with GlazedLists - autocomplete

I'm using glazedlists for auto-completion but i want to ask something in this point. I have an arraylist for friendlist. People can be added to friendlist or can be removed from friendlist by clicking add or remove button. Data of friendlist is written to friend.txt and is read from friend.txt ,by using Gson and Json.
When user add a person to the list or remove a person from the list the selected person is removed from or added to friend.txt.
...
Object[] elements = new Object[holdSizeValue];
for( int i = 0 ; i < holdSizeValue ; i++ ){
elements[i] = sendFriendNameFromList(i);
}
searchBox = new JComboBox<Object>();
final EventList<Object> eventList = GlazedLists.eventList(Arrays.asList(elements));
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
AutoCompleteSupport.install(searchBox, eventList);
}
});
...
As you might understand from the code, i used glazedlist for search operation.I want to update elements[]. How can i update ? When i call the method ,which includes the code above , from controller of add or remove button i am getting an exception which is about invokeAndWait.

The major advantage of the EventList is that once it's initialised you simply add objects to it and everything that is observing that data, e.g., the ComboBox will magically update thanks to all the plumbing GlazedLists provides.
So, move the EventList to be an instance variable:
private EventList<Object> eventList = new BasicList<Object>();
Perform the AutoCompleteSupport once, after the searchBox has been created and set up.
Then, when you need to add items call eventList.addAll(...). Don't reinstantiate that list, nor reinstanstiate the searchBox each time you want to do an update. Add/remove with the event list and the rest will follow automatically.

Related

Register commandHandler with EventList in a Nattable with Filters

As a suggestion from question Nattable add row command I tried to register a command handler with an EventList.
But since the Eventlist is wrapped by a FilterList which acts as BodyDataProvider I could not create a DataLayer based in EventList from which I could register my CommmandHandler.
The starting point is the previous question to define a BodyLayerStack with the following:
class BodyLayerStack extends AbstractLayerTransform {
//AncolabMaterial is the model to show at the natTable
private final EventList<AncolabMaterial> filterList;
private final IDataProvider bodyDataProvider;
private final SelectionLayer selectionLayer;
public BodyLayerStack(List<AncolabMaterial> input_values_list, IColumnPropertyAccessor<AncolabMaterial> columnPropertyAccessor) {
EventList<AncolabMaterial> eventList = GlazedLists.eventList(input_values_list);
TransformedList<?, ?> rowObjectsGlazedList = GlazedLists.threadSafeList(eventList);
SortedList<?> sortedList = new SortedList<>(rowObjectsGlazedList, null);
this.filterList = new FilterList<AncolabMaterial>((EventList<AncolabMaterial>) sortedList);
this.bodyDataProvider = new ListDataProvider<AncolabMaterial>(filterList, columnPropertyAccessor);
DataLayer bodyDataLayer = new DataLayer(getBodyDataProvider());
//Other layers stacked
GlazedListsEventLayer<?> glazedListsEventLayer = new GlazedListsEventLayer<AncolabMaterial>(bodyDataLayer, this.filterList);
this.selectionLayer = new SelectionLayer(glazedListsEventLayer, false);
//...
}
}
I have tried the following:
DataLayer dataLayer = new DataLayer(
new ListDataProvider<AncolabMaterial>(eventList, columnPropertyAccessor));
But since DataLayer wraps the IDataProvider, and serves as the data source for all other layers, If I sets the EvenList as the IDataProvider of the DataLayer then filterlList is not working properly.
i.e. this.filterList is not the base of the bodyDataProvider.
I have not find at nattable_examples -> tutorial examples -> GlazedLists -> Filter any other BodyLayerStack configuration different than the above.
There seems to be a big misunderstanding with regards to the list instances. If you want to use the filter functionality the shown BodyLayerStack is correct. You have to use the FilterList in the IDataProvider. There is absolutely now reason to change that!
For the command handler you need to use the base EventList instance. Of course that does not work if you use the list that you get from the IDataProvider. You need to provide the access to the EventList in another way. From the snippets you show in this and in the other related post, I don't see a reason why you access the underlying list via IDataProvider, but as you already noticed, that does not work. You need to change your code structure.

DropDownChoice gets duplicate choices

i have an issue with a DropDownChoice (the DDC) component. The situation is as following: I want to create a simple registry page for a contest. So i need a team with participants. I have created a form (which is a composition of different panels/forms) on which you can enter the name, age and 'position in the game'. Then you press the 'add participant' button and the participant should appear in the DropDownChoice.
I am new to Apache Wicket and actually i am glad i get the form to show on the screen and to see that the participants are actually added to the DDC. But here comes the issue: All of the participants in the DDC are 'turned into' the last one added. In other words: suppose i create the participant Jeff. Jeff gets added to the DDC, no problem. Then i create Mike. When i add Mike to the DDC and look at the available participants, Jeff seems to have been turned into Mike. So at this point, i do have 2 participants in my team, but the first one,Jeff, i suddenly Mike as well. And it is not only the property which is displayed that has changed. It is the complete content of the Participant object that turns into Mike.
Now if i would like to add Janine, Jeff and Mike would both turn into Janine and i would have 3 'Janine participants' in my DDC. I ll add the code of the 'TeamForm', which i believe is the most relevant. If needed, i can put more code on.
package com.tvh.tournamentregistry.form;
import com.tvh.tournamentregistry.model.Participant;
import com.tvh.tournamentregistry.model.Team;
import com.tvh.tournamentregistry.panel.ParticipantPanel;
import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.form.ChoiceRenderer;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.Model;
public class TeamForm extends Form {
public TeamForm(String id){
super(id);
final Team team = new Team();
CompoundPropertyModel<Team> teamModel = new CompoundPropertyModel<Team>(team);
setModel(teamModel);
add(new TextField("name"));
Model<Participant> participantModel = new Model<Participant>();
ChoiceRenderer<Participant> teamViewRenderer = new ChoiceRenderer<Participant>("firstname");
final DropDownChoice<Participant> teamView = new DropDownChoice<Participant>("players",participantModel, team.getPlayers(), teamViewRenderer){
#Override
protected boolean wantOnSelectionChangedNotifications() {
return true;
}
};
add(teamView);
final ParticipantPanel participantPanel = new ParticipantPanel("participantpanel");
add(participantPanel);
Button addParticipant = new Button("addparticipant"){
#Override
public void onSubmit() {
Participant participant = (Participant) participantPanel.getModel().getObject();
team.getPlayers().add(participant);
teamView.setChoices(team.getPlayers());
teamView.render();
participantPanel.clear();
}
};
addParticipant.setDefaultFormProcessing(false);
add(addParticipant);
}
#Override
protected void onSubmit() {
super.onSubmit(); //To change body of generated methods, choose Tools | Templates.
}
}
I have debugged the little application and what i saw was quite disturbing. i put a breakpoint on
Participant participant = (Participant) participantPanel.getModel().getObject();
after adding 2 participants, so i could have a look at the
team.getPlayers()
method which returns a list of participants. The model that gets returned by the paricipantspanel (which is a custom method, passing the model from the form in that panel) is correct. It returns the participant that i have entered in my form. But when i look in the team list, even before my debugger get to that line, i can see that all the other participants have 'changed' already. And i am not touching the list, only adding new participants.
Any thoughs anybody? Thanks! If this was absolutely not clear, please ask!
Each time you add a new participant, the ParticipantPanel must have its model "re-initialized" otherwise its model object references the same object all the time.
In detail:
Participant a.
On first render, your panel uses this participant so on add, it adds it to you list.
After that, while re-rendering, the model object of the panel is still point to participant a. So changes affect the old object. That's why your dropdown has your single participant repeated.
Try the following:
Instead of using getter, use PropertyModel
new DropDownChoice<Participant>("players",participantModel, team.getPlayers(), teamViewRenderer)
change to
new DropDownChoice<Participant>("players",participantModel, new PropertyModel(team, "players"), teamViewRenderer)
Specify idExpression in ChoiceRenderer
new ChoiceRenderer<Participant>("firstname");
change to
new ChoiceRenderer<Participant>("firstname", "id");

Wicket not updating page after form submit

I have this code in my page constructor:
private String selectedAwsId;
private String selectedIsReal;
//these two are actually outside the constructor, and getters and setters for these two strings not shown
List<AwsCredentials> awsCredentials = (List<AwsCredentials>)getAwsCredentials();
List<String> awsIds = new ArrayList<String>();
for (AwsCredentials cred : awsCredentials){
awsIds.add(cred.getAwsId());
}
selectedAwsId = awsIds.get(0);
List<String> yesOrNo = Arrays.asList(new String[] { "sandbox", "real"});
selectedIsReal = "sandbox";
Form selectAwsCredentialsForm = new Form("selectAwsCredentialsForm"){
#Override
public void onSubmit() {
super.onSubmit();
//TODO: why isn't this updating the form?
}
};
add(selectAwsCredentialsForm);
selectAwsCredentialsForm.add(new DropDownChoice("selectAwsCredentialsDropdown", new PropertyModel(this, "selectedAwsId"), awsIds));
selectAwsCredentialsForm.add(new DropDownChoice("selectRealOrSandboxHitsDropdown", new PropertyModel(this, "selectedIsReal"), yesOrNo));
The first time I render the page, this works fine. But when I change the selection in either of the DropDownChoices and submit the form, the page doesn't change (values in the selectedAwsId and selectedIsReal aren't altered accordingly). Is there something I'm missing in my understanding of how forms work? Does the entire page get refreshed when a form gets submitted (does the constructor get run again?)
You might want to construct a model and set this as your form's model. (This is what I do.) Submitting the form (and if all is coded appropriately) will then result in the form's model being updated.

GWT RequestFactory + CellTable

Does anyone know for an example of GWT's CellTable using RequestFactory and that table is being edited? I would like to list objects in a table (each row is one object and each column is one property), be able to easily add new objects and edit. I know for Google's DynaTableRf example, but that one doesn't edit.
I searched Google and stackoverflow but wasn't able to find one. I got a bit confused with RF's context and than people also mentioned some "driver".
To demonstrate where I currently arrived, I attach code for one column:
// Create name column.
Column<PersonProxy, String> nameColumn = new Column<PersonProxy, String>(
new EditTextCell()) {
#Override
public String getValue(PersonProxy person) {
String ret = person.getName();
return ret != null ? ret : "";
}
};
nameColumn.setFieldUpdater(new FieldUpdater<PersonProxy, String>() {
#Override
public void update(int index, PersonProxy object, String value) {
PersonRequest req = FaceOrgFactory.getInstance().requestFactory().personRequest();
PersonProxy eObject = req.edit(object);
eObject.setName(value);
req.persist().using(eObject).fire();
}
});
and my code for data provider:
AsyncDataProvider<PersonProxy> personDataProvider = new AsyncDataProvider<PersonProxy>() {
#Override
protected void onRangeChanged(HasData<PersonProxy> display) {
final Range range = display.getVisibleRange();
fetch(range.getStart());
}
};
personDataProvider.addDataDisplay(personTable);
...
private void fetch(final int start) {
lastFetch = start;
requestFactory.personRequest().getPeople(start, numRows).fire(new Receiver<List<PersonProxy>>() {
#Override
public void onSuccess(List<PersonProxy> response) {
if (lastFetch != start){
return;
}
int responses = response.size();
if (start >= (personTable.getRowCount()-numRows)){
PersonProxy newP = requestFactory.personRequest().create(PersonProxy.class);
response.add(newP);
responses++;
}
personTable.setRowData(start, response);
personPager.setPageStart(start);
}
});
requestFactory.personRequest().countPersons().fire(new Receiver<Integer>() {
#Override
public void onSuccess(Integer response) {
personTable.setRowCount(response+1, true);
}
});
}
I try to insert last object a new empty object. And when user would fill it, I'd insert new one after it. But the code is not working. I says that user is "attempting" to edit a object previously edited by another RequestContext.
Dilemmas:
* am I creating too many context'es?
* how to properly insert new object into celltable, created on the client side?
* on fieldUpdater when I get an editable object - should I insert it back to table or forget about it?
Thanks for any help.
am I creating too many context'es?
Yes.
You should have one context per HTTP request (per fire()), and a context that is not fire()d is useless (only do that if you/the user change your/his mind and don't want to, e.g., save your/his changes).
You actually have only one context to remove here (see below).
Note that your approach of saving on each field change can lead to "race conditions", because a proxy can be edit()ed by at most one context at a time, and it remains attached to a context until the server responds (and once a context is fired, the proxy is frozen –read-only– also until the server responds).
(this is not true in all cases: when onConstraintViolation is called, the context and its proxies are unfrozen so you can "fix" the constraint violations and fire the context again; this should be safe because validation is done on the server-side before any service method is called).
how to properly insert new object into celltable, created on the client side?
Your code looks OK, except that you should create your proxy in the same context as the one you'll use to persist it.
on fieldUpdater when I get an editable object - should I insert it back to table or forget about it?
I'm not 100% certain but I think you should refresh the table (something like setRowData(index, Collections.singletonList(object)))
BTW, the driver people mention is probably the RequestFactoryEditorDriver from the Editor framework. It won't help you here (quite the contrary actually).

How can I use RequestFactory to create an object and initialize a collection whithin it with objects retrieved from another ReqFactory?

I am struggling with an issue using RequestFactory in GWT.
I have a User object : this object has login and password fields and other fields which are of collection type.
public class User {
private String login;
private String password;
private Set<Ressource> ressources;
// Getters and setters removed for brievety
}
I need to persist this object in db so I used RequestFactory because it seems like a CRUD-type operation to me.
Now for the RequestFactory part of the code, this is how I have tried to do it :
I create a UserRequestContext object to create a request object for the new User. Which gives something like :
public interface MyRequestFactory extends RequestFactory {
UserRequestContext userRequestContext();
RessourceRequestContext ressourceRequestContext();
}
and to create the user object I have something like this :
public class UserAddScreen extends Composite {
private UserProxy userProxy;
EventBus eventBus = new SimpleEventBus();
MyRequestFactory requestFactory = GWT.create(MyRequestFactory.class);
...
public UserAddScreen() {
...
requestFactory.initialize(eventBus);
}
public showUserAddScreen() {
// Textbox for password and login
// Listbox for ressources
}
}
I have tried to implement it as a wizard. So at the beginning of the UserAddScreen, I have a
a userProxy object.
This object fields are initialized at each step of the wizard :
the first step is adding the login and password
the second step is adding ressources to the userProxy object.
for this last step, I have two list boxes the first one containing the list of all the ressources i have in my DB table Ressources that I got from RessourceRequestContext.getAllRessource (I have a loop to display them as listbox item with the RessourceId as the value) and the second allows me to add the selected Ressources from this first listbox. Here is the first listbox :
final ListBox userRessourcesListBox = new ListBox(true);
Receiver<List<RessourceProxy>> receiver = new Receiver<List<RessourceProxy>>() {
#Override
public void onSuccess(List<RessourceProxy> response) {
for(RessourceProxy ressourceProxy : response) {
ressourcesListBox.addItem(ressourceProxy.getNom() + " " + ressourceProxy.getPrenom(), String.valueOf(ressourceProxy.getId()));
}
}
};
RessourceRequestContext request = requestFactory.ressourceRequestContext();
request.getAllRessource().fire(receiver);
So, as you can see, my code loops over the retrieved proxies from DB and initializes the items within the listbox.
Here are the control buttons :
final Button addButton = new Button(">");
addButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
for (int i = 0; i < ressourcesListBox.getItemCount(); i++) {
boolean foundInUserRessources = false;
if (ressourcesListBox.isItemSelected(i)) {
for (int j = 0; j < userRessourcesListBox
.getItemCount(); j++) {
if (ressourcesListBox.getValue(i).equals(
userRessourcesListBox.getValue(j)))
foundInUserRessources = true;
}
if (foundInUserRessources == false)
userRessourcesListBox.addItem(ressourcesListBox
.getItemText(i), ressourcesListBox
.getValue(i));
}
}
}
});
So when somebody selects one or more users and click on a ">" button, all the selected items go to the second listbox which is named userRessourceListBox
userRessourcesListBox.setWidth("350px");
userRessourcesListBox.setHeight("180px");
After that, I have a FINISH button, which loops over the items in the second listbox (which are the ones I have selected from the first one) and I try to make a request (again) with RequestFactory to retrieve the ressourceProxy object and initialize the userProxy ressources collection with the result
final Button nextButton = new Button("Finish");
nextButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
RessourceRequestContext request = requestFactory.ressourceRequestContext();
for(int i = 0; i < userRessourcesListBox.getItemCount(); i++) {
Receiver<RessourceProxy> receiver = new Receiver<RessourceProxy>() {
#Override
public void onSuccess(RessourceProxy response) {
userProxy.getRessource().add(response);
}
};
request.find(Long.parseLong(userRessourcesListBox.getValue(i))).fire(receiver);
}
creationRequest.save(newUserProxy).fire(new Receiver<Void>() {
#Override
public void onSuccess(Void response) {
Window.alert("Saved");
}
});
}
});
Finally, (in the code above) I try to save the UserProxy object (with the initial request context I have created userProxy with)... but it doesn't work
creationRequest.save(newUserProxy).fire(...)
It seems like when looping over the result in the onSuccess method :
userProxy.getRessource().add(response);
I retrieve the response (of type RessourceProxy) but beyond this method, for example when I try to save the userProxy object AFTER THE LOOP, there are no RessourceProxy objects in the ressourceProxy collection of userProxy...
Have you guys ever experienced something like this ?
Perhaps I am not doing it right : do I have to get the ressource with the UserRequestContext ? so that my newUser object and ressources are managed by the same request Context ?
if yes then I think it's a little bit weird to have something mixed together : I mean what is the benefit of having a Ressource-related operation in the User-related request context.
any help would be really really ... and I mean really appreciated ;-)
Thanks a lot
The message "… has been frozen" means that the object has been either edit()ed or passed as an argument to a service method, in another RequestContext instance (it doesn't matter whether it's of the same sub-type –i.e. UserRequestContext vs. RessourceRequestContext– or not) which hasn't yet been fire()d and/or the response has not yet come back from the server (or it came back with violations: when the receiver's onViolation is called, the objects are still editable, contrary to onSuccess and onFailure).
UPDATE: you have a race condition: you loop over the resource IDs and spawn as many requests as the number of items selected by the user, and then, without waiting for their response (remember: it's all asynchronous), you save the user proxy. As soon as you fire() that last request, the user proxy is no longer mutable (i.e. frozen).
IMO, you'd better keep the RessourceProxys retrieved initially and use them directly in the user proxy before saving it (i.e. no more find() request in the "finish" phase). Put them in a map by ID and get them from the map instead of finding them back from the server again.