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.
Related
I have written a custom https://mindbug.in/vaadin/vaadin-dataprovider-example/ CallBackDataProvider that I based on this link here, which is used for a multi-select combo box (an addon https://github.com/bonprix/vaadin-combobox-multiselect from Vaadin's addon directory) for the purpose of providing a item lazy loading.
According to the addon's clear() and selectAll(), it expects a ListDataProvider. I've already set the component's data provider to used the custom data provider above. Whenever a clear or selectAll function is triggered, the Class Cast Exception is being thrown. It is expecting a ListDataProvider.
The very straightforward workaround for this case is to disable the clear and selectAll method by setting the boolean flag to false, but from the user's point of view, this will not be flexible.
Another step attempted is to to convert the stream into a Collection List, yet, it didn't work. It still throws an error.
This is the custom CallbackDataProvider, extended from the AbstractBackendDataProvider:
public ItemDataProvider(ReceiptService receiptService) {
if(receiptService != null){
this.receiptService = receiptService;
}else {
this.receiptService = new ReceiptService();
}
}
#Override
protected Stream<SkusSelectBox> fetchFromBackEnd(Query<SkusSelectBox, String> query) {
stream = receiptService.fetchSkus(query.getFilter().orElse(null), query.getLimit(), query.getOffset(), query.getSortOrders()).stream();
return stream;
}
#Override
protected int sizeInBackEnd(Query<SkusSelectBox, String> query) {
return receiptService.countSkus(query.getFilter().orElse(null));
}
#Override
public Object getId(SkusSelectBox item) {
return item.getItemId();
}
public Stream<SkusSelectBox> getStream(){
return stream;
}
The SkuSelectBox is a simple two string attribute object that retrieves the id and the name.
For this component, I have set the following at the view page:
ItemDataProvider itemDataProvider = new ItemDataProvider(receiptService);
ComboBoxMultiselect<SkusSelectBox> skuSelect = new ComboBoxMultiselect<>("Items");
skuSelect.setPlaceholder("Choose Items");
skuSBox.add(new SkusSelectBox("0", "No data found"));
skuSelect.setWidth(80, Unit.PERCENTAGE);
skuSelect.setRequiredIndicatorVisible(true);
skuSelect.setItemCaptionGenerator(SkusSelectBox::getItemName);
skuSelect.setSelectAllButtonCaption("Select All");
skuSelect.setClearButtonCaption("Clear");
skuSelect.showSelectAllButton(true);
skuSelect.showClearButton(true);
skuSelect.setDataProvider(itemDataProvider);
skuSelect.getDataProvider().refreshAll();
skuSelect.isReadOnly();
skuSelect.setPageLength(20);
if(skuSBox.size() <=1 ){
skuSelect.showSelectAllButton(false);
//skuSelect.showClearButton(false);
}
skuSelect.setResponsive(true);
The selectAll and clear methods are very similar except for the very end of the method:
#Override
public void selectAll(final String filter) {
final ListDataProvider<T> listDataProvider = ((ListDataProvider) getDataProvider());
final Set<String> addedItems = listDataProvider.getItems()
.stream()
.filter(t -> {
final String caption = getItemCaptionGenerator().apply(t);
if (t == null) {
return false;
}
return caption.toLowerCase()
.contains(filter.toLowerCase());
})
.map(t -> itemToKey(t))
.collect(Collectors.toSet());
updateSelection(addedItems, new HashSet<>(), true);
updateSelection(new HashSet<>(), removedItems, true); (this is for clear method)
}
Basically the class cast exception is shown in this error message, referring to either the clear or selectAll, whichever method I was invoking:
java.lang.ClassCastException: com.igi.sycarda.dashboard.hib.utils.ItemDataProvider cannot be cast to com.vaadin.data.provider.ListDataProvider
at org.vaadin.addons.ComboBoxMultiselect$1.clear(ComboBoxMultiselect.java:224)
I'm looking at the selectAll or clear method, when invoked to work as usual as if not using a CallbackDataProvider.
Until the next patch release for the addon is released, I need to put in a workaround for this problem, how can I convert a custom provider to a ListDataProvider either in a quick dirty way or a cleaner way if required?
UPDATE: Normally, I would do a direct fetch from the service class, but when tested with a tenant that has about 20K of item records, the loading of the page and the specific component box is quite slow to load. That CallbackDataProvider is to test this will work for those big amount of records.
The idea with a list data provider is that all items are loaded into memory. It is possible to load all items from a database into memory and then use that to create a list data provider. This does on the other hand defeat the purpose of having a callback data provider.
It's probably more straightforward for you to fetch the items into a list directly from your receiptService rather than going through the existing data provider.
Since there are restrictions or blocks that cause error to approach I was doing, someone just suggested to me to create a view derived from the tables / columns required and used them instead of the normal tables.
After creating a view, I just reverted and removed these lines below to the usual implementation:
skuSelect.setDataProvider(itemDataProvider);
skuSelect.getDataProvider().refreshAll();
skuSelect.isReadOnly();
skuSelect.setPageLength(20);
if(skuSBox.size() <=1 ){
skuSelect.showSelectAllButton(false);
//skuSelect.showClearButton(false);
}
At the time of writing this, we've tested it an hour ago and it solves the problem without sacrificing the performance time taken and creating an additional component. In terms of time measurement, a 20K result set in a view loads in less than 10 seconds vs 7-9 minutes previously.
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.
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
I can use fl=fld1,fld2,fld3 tor return specific fields from solr. But sometimes i generate dynamic field names like ".*_attribute_group1" and want solr to return all group.
Is it posible to extend solr 'fl' field with regexp? Where to look in solr codebase?
Solr doesn't support wildcard patterns in field names ( "fl" param ). But you could write your own component to process the request & identify the list of fileds present in the index that you want.
Pesudo Code of extending search component to implement custom fields..
// PSUEDO CODE
public class FLPatternCustomComponent extends SearchComponent {
#Override
//Gauranteed to be called before any other SearchComponent.process
public void prepare(ResponseBuilder rb) throws IOException {
SolrParams params = rb.req.getParams();
//Input fl=field_*
String[] inputFl = params.getParams(CommonParams.FL);
Collection<String> existingFl = rb.req.getSearcher().getFieldNames();
//process & find matching fields{
SolrQuery newFields = new SolrQuery();
newFields.set(CommonParams.FL, "field_1,field_2,field_3,field_4");
AppendedSolrParams appendedParams = new AppendedSolrParams(params, q);
rb.req.setParams(appendedParams);
super.prepare(rb);
}
#Override
public void process(ResponseBuilder rb) throws IOException {
//Process request
super.process(rb);
}
}
You could have this a component chained to your existing request handler or create your request handler & perhaps you could also add any additional invariants.
You may want to consider any additional performance overhead of custom component & its processing. I have created couple of custom components for custom ranking & custom request handlers & use it without much issues.
You might want to check Solr Plugin Development.
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
}
});
}
}
}