Is there a way to select multiple values in a dropdown in Apcahe Wicket using PropertyModel?
You can use ListMultipleChoice.
Say you have a list of users to which you want to populate in the multiselect drop down.
You can do something like this:
ListMultipleChoice<?> multiChoice = new ListMultipleChoice<Object>
("usermultiSelect",
(IModel<? extends Collection<Object>>) new PropertyModel<Object>(properties,"selectedUsers"),
users);
Furthermore you can register on change listener to do some stuff with the selected data
multichoice.add(new AjaxFormComponentUpdatingBehavior("onchange") {
private static final long serialVersionUID = 1L;
#Override
protected void onUpdate(AjaxRequestTarget target) {
List<User> users = (List<User>) properties.get("selectedUsers");
// do whatever you want to do with the users list
}
};
You could use wicket Palette or use Select2. https://github.com/ivaynberg/wicket-select2/tree/master/wicket-select2-examples
Related
I am trying to get the Id of my selected choice on a DropDownChoice but I get an error..
I know that when I choose a value I just update the model and not the object (reflection).
I expected to get all the values of object "User" through getModelObject() but all i get is a NullPointerException..
I have tried many things according to tutorials and Wicket 8 documentation but nothing seems to work..
My code is like:
// POJO
class User {
private Integer id;
private String name;
[...]
}
// Main.class
private User selected;
ChoiceRenderer<User> choiceRenderer = new ChoiceRenderer<User>("id", "name");
List<User> list = getUsers();
final DropDownChoice<User> dropdown1 = new DropDownChoice<User>("dropdown",
new PropertyModel<User>(this, "selected"), list, choiceRenderer);
Button btn = new Button("btn") {
private static final long serialVersionUID = 1L;
#Override
public void onSubmit() {
RecrRemoteOperations recr = new RecrRemoteOperations();
try {
// NullPointerException!
// Integer id = dropdown.getModel().getObject().getId();
// id: the id of the selected "User" value on dropdown
recr.updateCommand(id);
} catch (Throwable e) {
e.printStackTrace();
}
}
}.setDefaultFormProcessing(false);
private static List<User> getUsers() {
List<User> allUsers = new ArrayList<User>();
[...]
return list;
}
The problem is in button.setDefaultFormProcessing(false). This tells Wicket to not use the submitted values and to not update the models of the FormComponents, i.e. the DropDownChoice won't have model object and thus won't set selected.
.setDefaultFormProcessing(false) is usually used for Cancel buttons, where you just want to leave the form.
I am not sure but my problem is very similar to this question
I was told that I don't need to use Ajax but I will try to see if it works
I have vaadin grid, and it's great that it has lazy data loading from the box. But for some reasons I have custom filters, which I use via
CallbackDataProvider<> dataProvider.fetch(Query query)
Query object has parameters for loading by portions (offset and limit), so I need to set it dynamically (?) and somehow listen grid scrolling event to load next part of data when user scrolls down (?)
Grid.dataComunicator has field Range pushRows but there no public methods to get it. And all i have is grid with lazy loading without filtered data or grid with eager loading with filtered data.
So, is there any way to implement filtering data with lazy loading in vaadin grid element?
ok, problem solved by using ConfigurableFilterDataProvider<> as wrapper over CallbackDataProvider<>.
so, when i filter table, this wrapper adds filtering conditions to all queries, and data loads lazy as usual.
I arrived here using vaadin 22. The answer probably isn't in the same context as the question but given I arrived here I suspect others will.
To create a grid that uses lazy loading and is able to inject a filter into the query use:
class SearchableGrid<E> {
Grid<E> entityGrid = new Grid<>();
private SearchableGrid(DaoDataProvider daoProvider)
{
var view = entityGrid.setItems(query ->
{
// add the filter to the query
var q = new Query<E, String>(query.getOffset(), query.getLimit(), query.getSortOrders(), null,
getSearchField().getValue());
return daoProvider.fetchFromBackEnd(q);
});
view.setItemCountCallback(query ->
{
// add the filter to the query
var q = new Query<E, String>(query.getOffset(), query.getLimit(), query.getSortOrders(), null,
getSearchField().getValue());
return daoProvider.sizeInBackEnd(q);
});
}
I've packaged the methods into a BackEndDataProvider as the same class
can be used to as a provider for comboboxes.
public class DaoDataProvider<E extends CrudEntity>
extends AbstractBackEndDataProvider<E, String>
{
JpaBaseDao<E> dao;
GetFilterBuilder<E> getFilterBuilder;
public DaoDataProvider(JpaBaseDao<E> daoProvider, GetFilterBuilder<E> getFilterBuilder)
{
this.dao = daoProvider;
this.getFilterBuilder = getFilterBuilder;
}
#Override
public int sizeInBackEnd(Query<E, String> query)
{
var q = getFilterBuilder.builderFilter(query);
return (int) q.count().intValue();
}
#Override
public Stream<E> fetchFromBackEnd(Query<E, String> query)
{
var q = getFilterBuilder.builderFilter(query);
q.startPosition(query.getOffset()).limit(query.getLimit());
return q.getResultList().stream();
}
}
The filterBuilder is where you construct your query for your back end data provider.
I'm trying to create a dynamic table component in wicket to show a list of Objects. The component will receive a List of Objects and render it in a table form.
Assuming the following Product entity:
public class Product {
public static final long serialVersionUID = 1L;
//
private String id;
private String name;
private String description;
private Double price;
// getters and setters omitted for brevity
}
The following html/code pair will work:
(Note: Product entity will later be genericized so we can pass it a list of any POJO)
<table>
<tbody>
<tr wicket:id="row">
<td wicket:id="cell">
cell
</td>
</tr>
</tbody>
</table>
----------------------------------------------------------------
parameters:
final String[] fieldNames = new String[]{"id", "name", "description", "price"};
List<Product> productList = ....
----------------------------------------------------------------
ListView lvRows = new ListView("row", productList) {
#Override
protected void populateItem(ListItem item) {
Product product = (Product)item.getModelObject();
CompoundPropertyModel cpm = new CompoundPropertyModel(product);
//
RepeatingView cell = new RepeatingView("cell", cpm);
item.add(cell);
//
for (String fn : fieldNames) {
Label label = new Label(fn);
cell.add(label);
}
}
};
this.add(lvRows);
The problem is that the above code will result in a bunch of warnings (one for each Product in the list):
00:57:52.339 [http-apr-8080-exec-108] WARN
o.a.w.m.repeater.AbstractRepeater - Child component of repeater
org.apache.wicket.markup.repeater.RepeatingView:cell has a non-safe
child id of id. Safe child ids must be composed of digits only.
So my questions are:
am i doing it wrong?
if not, how do i get rid of the warnings?
Why does wicket require numeric child id in this instance? CompoundPropertyModel works fine in other situations, with the id linked to object attributes...
if i'm doing it wrong, what's the "proper" way to do it? Should i create the child ids uniquely, forfeit using CompoundPropertyModel, and feed the values directly via reflection? Not sure about the performance impact though, doing a reflection call for each cell can't be cheap. Something like this:
for(String fn:fieldNames} {
String s = ...; //find the value of Object O, property fn via Reflection
Label label = new Label(cell.newChildId(), s);
cell.add(label);
}
Thanks in advance.
You can avoid the warnings by adding an intermediate child in the RepeatingView, as explained by Igor Vaynberg in this post at the Wicket users list.
In other words, don't add the Labels with the non-numeric ids directly to the repeater, add a container with a numeric id instead, and add the Labels to the container:
RepeatingView cell = new RepeatingView("cell");
WebMarkupContainer container = new WebMarkupContainer(cell.newChildId(), cpm);
for (String fn : fieldNames) {
Label label = new Label(fn);
container.add(label);
}
cell.add(container);
item.add(cell);
The reasons for those warnings are also outlined in that post.
EDIT:
It seems that you'd need to model a ProductPanel and use it inside the RepeatingView instead of just a WebMarkupContainer. This means that the ProductPanel would probbaly have to explicitly enumerate properties (or wicket:ids) in its HTML.
You could also keep your current code and just do Label label = new Label(cell.newChildId(), new PropertyModel(product, fn)); and drop the CPM, if you want the HTML to be independent from the specific properties.
im new in java component base framework, especially vaadin.
before use this framework, im using struts 2.
so when i want to query some table, i have a search box, contains many textfield. when user click Search Button, then the parameters from the texfield will be sent into my hibernate directly using http post.
my question, how to filter the output using vaadin?
Just update your BeanContainer with new data. Here is an example of my code
public void refreshTableData() {
getBeanContainer().removeAllItems();
List<Customer> customers = customerDao.getByCustomerFilter(getCustomerFilterForm().getFilterItem().getBean());
getBeanContainer().addAll(customers);
}
Where CustomerFilter is a bean that has all the search criteria data, that I fill it within a form earlier (e.g with comboboxes), and beanContainer is my table container data source.
filterString = checkBox.getValue().toString();
Filterable f = (Filterable)(table.getContainerDataSource());
if(filters==null)
filters=new TreeMap<Object, SimpleStringFilter>();
SimpleStringFilter filter=filters.remove(propertyId);
if (filter != null){
f.removeContainerFilter(filter);
}
filter = new SimpleStringFilter(propertyId, filterString, ignoreCase, onlyMatchPrefix);
filters.put(propertyId, filter);
f.addContainerFilter(filter);
This is my solution to filter rows using a text that user inputs using textfield:
textField.addTextChangeListener(new TextChangeListener() {
#Override
public void textChange(TextChangeEvent event) {
Filterable filter= (Filterable) (table.getContainerDataSource());
filter.removeAllContainerFilters();
String filterString = event.getText();
if (filterString.length() > 0) {
filter.addContainerFilter(new Like("columnName", "%"+filterString +"%"));
}
}
});
I hope code is selfexplanatory.
In Silverlight, a frequently used pattern is:
Request data
Get back an empty container for the data
Asynchronously fire off a query to fill the container
When the query returns, fire an event on the container
Update the UI according to the container's contents
Can this be done in GWT?
The reason I ask is that I'm trying to make a SuggestBox that contains a list of group names and icons. First, I query Facebook to get a list of groups IDs that are close to the current String in the SuggestBox. Then, I fire off queries to get icons for each group id. The problem is that I have to return the suggestions before those queries are done. I'm not sure how to go back and insert the data after I have it. I don't want to block until the calls are complete, and there's no real way to know in advance what data to load.
I could return a widget for the suggestion that loads an image, but the suggestion must be a plain String.
What is the right approach here?
Let's assume you're using GWT RPC. You'll have some service interface that lets you fetch the groupIds for a suggestion and the icon for a specific group id.
public interface FacebookService extends RemoteService {
List<String> getFacebookGroupIds(String suggestion);
Icon getIconForGroup(String groupId);
}
You should build your own implementation of Suggestion that can display itself with either just a groupId or a groupId and an Icon.
public class FacebookGroupSuggestion implements Suggestion {
private String groupId;
private Icon icon;
public FacebookGroupSuggestion(String groupId) {
this.groupId = groupId;
}
public String getDisplayString() {
StringBuilder builder = new StringBuilder();
builder.append("<b>");
builder.append(this.groupId);
builder.append("</b>");
if (this.icon != null) {
builder.append(this.icon.toSafeHtml());
}
return builder.toString();
}
}
I'm using Icon as your own implementation of an icon, it's not a standard class.
Then, you can make your implementation of SuggestOracle to fetch the groupIds and icons asynchronously. The SuggestOracle uses a callback to inform the suggestBox that some response to a request is available. So fetch your results, and call the callback when you get them. It'll look something like this.
public class FacebookSuggestOracle extends SuggestOracle {
private FacebookServiceAsync service = GWT.create(FacebookService.class);
private Request currentRequest;
private Callback currentCallback;
#Override
public void requestSuggestions(Request request, Callback callback) {
// Save request & callback for future use.
this.currentRequest = request;
this.currentCallback = callback;
// Fetch the groupIds
service.getFacebookGroupIds(request.getQuery(), new AsyncCallback<List<String>>() {
public void onSuccess(List<String> result) {
createSuggestionsForGroupIds(result);
}
});
}
private void createSuggestionsForGroupIds(List<String> groupIds) {
List<FacebookGroupSuggestion> suggestions = new ArrayList<FacebookGroupSuggestion>();
for (String groupId : groupIds) {
suggestions.add(new FacebookGroupSuggestion(groupId));
}
Response response = new Response(suggestions);
// Tell the suggestBox to display some new suggestions
currentCallback.onSuggestionsReady(currentRequest, response);
// Fetch the icons
for (String groupId : groupIds) {
service.getIconForGroup(groupId, new AsyncCallback<Icon>() {
public void onSuccess(Icon result) {
// match the icon to the groupId in the suggestion list
// use the callback again to tell the display to update itself
}
});
}
}
}