Using DataView as a dynamic search result table in Wicket 1.4 - ajax

I'm fairly new to Wicket but I've already run into a very strange problem.
I'm creating a page with a pretty basic search form and a results table (a DataView) which is initially empty. When the user enters data into the fields and clicks "Search", the app calls some backend services which are then used to populate the DataView.
However the user has to click "Search" twice for the data to be displayed.
I finally tracked this down, and it's because Wicket is using zero for the number of items to be displayed for the first "Search" click. At the second click, the rows have already been added and Wicket has already calculated the proper number of rows to display, so it decides it will show the data.
In AbstractPageableView.getItemModels(), the size of the results to display is initially zero, because I don't load the table with any initial data probably.
I got around this problem by loading the DataView with empty rows on page load. This seems to trick the DataView into using the displaying the data for the first "Search" click.
My question is: am I doing this right? Is there another repeater that is better for this task? Is this a bug or something?

Finally cracked it: it was because I was loading the data in my data provider only in the iterator() method, and the data provider's size() method is usually called before the iterator() method is. I should have been loading the data in its own method and calling that method from iterator() and size(). Doing that fixed it.
Data Provider before (Splc is the DTO):
SearchResultsDataProvider implements IDataProvider<Splc> {
/**
* The list of search results
*/
private List<Splc> models;
#Override
public void detach() {
// Do nothing
}
#Override
public Iterator<Splc> iterator(int first, int count) {
// load the data into the list of models
models = service.getSplcModels();
return models.subList(....).iterator();
}
#Override
public IModel<Splc> model(Splc object) {
return new Model<Splc>(object);
}
#Override
public int size() {
return models.size();
}
}
Data Provider after:
SearchResultsDataProvider implements IDataProvider<Splc> {
private List<Splc> getModels() {
// load the data into the list of models
return service.getSplcModels();
}
#Override
public void detach() {
// Do nothing
}
#Override
public Iterator<Splc> iterator(int first, int count) {
return getModels().subList(....).iterator();
}
#Override
public IModel<Splc> model(Splc object) {
return new Model<Splc>(object);
}
#Override
public int size() {
return getModels().size();
}
}

Related

Call several different JavaScript within AjaxLink one after the other

When I click on an AjaxLink, I would like to have a validation via JavaScript on the client side first (because the LocalStorage is queried) and then depending on the result, further JavaScript calls are made. How can i achieve this?
In a pseudo code it would look like this:
new AjaxLink<>("myId", myModel) {
#Override
public void onClick(AjaxRequestTarget target) {
boolean isCounterValid = target.appendJavaScript(checkCounter()); // i know that this is not possible, therefore pseudo code
if(isCounterValid) {
target.appendJavaScript(someOtherJavaScript());
}
else {
target.appendJavaScript(anotherJavaScript());
}
}
private String checkCounter() {
return "var count = window.localStorage.getItem('myCounter'); return count !== 1;";
}
private String someOtherJavaScript() {
return "change something";
}
private String anotherJavaScript() {
return "change other thing";
}
};
You need to send extra request parameters with the Ajax call when the link is clicked. For that you should override updateAjaxAttributes(AjaxRequestAttributes attributes) method of AjaxLink:
#Override
protected void updateAjaxAttributes(AjaxRequestAttributes attributes)
{
attributes.getDynamicExtraParameters().add("var count = window.localStorage.getItem('myCounter'); return [{\"name\":\"count\", \"value\": count}]");
}
This way inside AjaxLink#onClick() you can read the count via:
int count = getRequest().getRequestParameters().getParameterValue("count").toInt();
AJAX components and behaviors can customize AJAX attributes overriding updateAjaxAttributes and using a custom implementation of AjaxCallListener which exposes different method to hook into the AJAX request cycle. In you case you could use AjaxCallListener#getBeforeSendHandler.
For a full introduction to this topic (with examples) see user guide:
https://ci.apache.org/projects/wicket/guide/8.x/single.html#_ajax_request_attributes_and_call_listeners

How to get selected items from a SWT Table using RxJava?

I have a table and a button and I want to emit an event ItemsSelected with the selected items of the table when the button is clicked.
The button should not know the table and it should remain only as a stream of clicks.
So this solution is discarded:
final ETable table = ...
PublishSubject<ItemSelected> selected = PublishSubject.create();
button.addSelectionListener(new SelectionListener(){
#Override
public void widgetSelected(SelectionEvent e) {
for (TableItem item : table.getSelection()) {
selected.onNext(new ItemSelected(item));
}
}
});
I would prefer a way to compose the click stream of the button with the item selection stream of the table in order to keep loose coupling between this two elements.
Because the table allows multiple selection I must first scan the items selected in order to emit an event with all the items. Something like:
public static class ItemsSelected<T> {
final List<T> items = new ArrayList<T>();
}
public abstract static class ItemSelection<T> {
public abstract void apply(ItemsSelected<T> selection);
}
public static class ItemUnselected<T> extends ItemSelection<T> {
final T item;
public ItemUnselected(T item) {
this.item = item;
}
public void apply(ItemsSelected<T> selection) {
selection.items.remove(item);
}
}
public static class ItemSelected<T> extends ItemSelection<T> {
final T item;
public ItemSelected(T item) {
this.item = item;
}
public void apply(ItemsSelected<T> selection) {
selection.items.add(item);
}
}
public static class ObservableTable<T> extends Table {
private PublishSubject<ItemSelection<T>> clicks = PublishSubject.create();
public Observable<ItemsSelected<T>> selection = clicks.scan(new ItemsSelected<T>(),
new Func2<ItemsSelected<T>, ItemSelection<T>, ItemsSelected<T>>() {
#Override
public ItemsSelected<T> call(ItemsSelected<T> t1, ItemSelection<T> t2) {
// breaking events immutability
t2.apply(t1);
return t1;
}
});
public ObservableTable(Composite parent, int style) {
super(parent, style);
this.addSelectionListener(new SelectionListener() {
#SuppressWarnings("unchecked")
#Override
public void widgetSelected(SelectionEvent e) {
if (((TableItem) e.item).getChecked())
clicks.onNext(new ItemSelected<T>((T) e.item.getData()));
else
clicks.onNext(new ItemUnselected<T>((T) e.item.getData()));
}
#Override
public void widgetDefaultSelected(SelectionEvent e) {
}
});
}
}
Then, I must combine the table.selection stream with the button.clicks stream in a selectionForAction stream. The idea is that when a ButtonClick is emitted, an SelectionForAction will be emitted if and only if an ItemSelected was previously emitted.
-------S1--U1-----S2---S3--------- table.clicks
(scan)
-------(1)--()---(2)---(2,3)------ table.selection
----O----------O-------------O---- button.clicks
(?)
-----------------------------(2,3) selectionForAction
So, wich operation should I use?
Zip: It doesn't work because if I click the button and later select an item, it should not do nothing, but with zip it will emit an event.
Join: I end up with a "solution" using join but it doesn't seem to be a good one. Somethinkg like:
table.selection.join(button.clicks, new Func1<ItemsSelected,Observable<Long>>() {
#Override
public Observable<Long> call(ItemsSelected t) {
// it doesn't seem a good idea
return Observable.timer(1, TimeUnit.DAYS);
}
}, new Func1<ClickEvent, Observable<Long>>() {
#Override
public Observable<Long> call(ClickEvent t) {
// this makes the ClickEvent be dropped if there is no previous ItemsSelected event emitted
return Observable.timer(1, TimeUnit.MILLISECONDS);
}
}, new Func2<ItemsSelected, ClickEvent, SelectionForAction>() {
#Override
public SelectionForActioncall(ItemsSelected t1, ClickEvent t2) {
return new SelectionForAction(t1.items);
}
});
Any idea?
I've found the operator that I needed to achieve the join behaviour with a very large time unit (DAYS in the example) and a very small one (MILLISECONDS).
With a variant of sample that takes another Observable as the sampler I could emit an event A only after an event of B would be emitted.
In my example the click acts as the sampler and the stream selection emits the events that I'm interested in. (This also requires to ignore the last event that is being emitted when the stream completes).
Another possible solution will be use the buffer(boundary):
The clicks stream would act as the boundary and I could avoid the scan operator because the list of items selected is created by the buffer operator. However with this solution I would not be considering unselection.
So, with sample I've achieved my original goal, however, I'm not happy with the way I handle items unselection and the final list of items selected.
In this case I need to maintain the state of the items selected in order to perform some operation on all of them when a ClickEvent occurs.
I could subscribe to the items selection/unselection and maintain a List of the items selected but then I'll have lost the possibility of compose the clicks observable with the selection observable.
With scan I maintain state and also keep the composability of observables, but representing the list of current selection as an event seems a little forced, in fact this represents a new issue: if I select x items and then click the button, an event with the selection is being emitted as expected, but if neither the items are unselected nor a new one is selected and then click again the button, nothing happens. So, it seems that selection doesn't fit as an event.

Ajax Pagination Wicket. Retrieve new data from db when next page is clicked

I want to paginate my results. First I've tried the classic way and it worked, my dataView retrieves a list with results from database and displays the number of results I want per page.
(Looks like this << <1 2 3 4 5> >> )
final DataView<RequestEntity> dataView = new MyDataView();
dataView.setItemsPerPage(10);
linksContainer.add(new PagingNavigator("pageNavigator", dataView));
<a wicket:id="pageNavigator"></a>
Now I want to retrieve data from database only when the next page is clicked (kind of lazy loading/ lazy pagination). So I modified my DAOObject like this:
query.setMaxResults(entriesPerPage);
It is the same query like before but this time it will take the amount of results I want per page.
And it works, it retrieves as much entries as I want per page. The problem is that I don't know how to display another page. It appears just one page with the first entries from the query. (Looks like this << 1 >>)
My idea is to use links instead of AjaxPagingNavigator to display pages from 1 to 5 and when the link is clicked the query is executed. I don't think my idea is good. Can you help me? I hope my question isn't too stupid.
Thanks
Done! All I needed to do is to create IDataProvider that knows everything. If you create it you don't need to worry about the size( about tricking it to show more pages).
IDataProvider dataProvider = new IDataProvider<RequestEntity>() {
RequestEntityDAOExtra requestEntityDAOExtra =
((MyApp) getApplication()).getMyRequestDAO();
#Override
public Iterator<? extends RequestEntity> iterator(long first, long count) {
List<RequestEntity> entitiesByDomainList = requestEntityDAOExtra.getEntityByDomain(
domainInput.getModelObject(), (int) count, (int) first);
return entitiesByDomainList.iterator();
}
#Override
public long size() {
return requestEntityDAOExtra.getEntityByDomainCount(domainInput.getModelObject());
}
#Override
public IModel<RequestEntity> model(final RequestEntity requestEntity) {
return new LoadableDetachableModel() {
#Override
protected RequestEntity load() {
return requestEntity;
}
};
}
#Override
public void detach() {
}
};
final DataView<RequestEntity> dataView = new MyDataView(dataProvider, 10);
private class MyDataView extends DataView<RequestEntity> {
public MyDataView(IDataProvider dataProvider, int i) {
super("linksList", dataProvider, i);
}
#Override
protected void populateItem(final Item<RequestEntity> item) {
.....
}
}

Paging with PagedList

This is my repository layer:
public List<Section> GetAllSections()
{
return context.Sections.Include("Groups").Include("Groups.Classes").ToList();
}
This is my application layer:
public List<Section> GetAllSections()
{
return cacheSectionRepo.GetAllSections();
}
And in controller I have:
SectionApplication sectionApp = new SectionApplication();
public ActionResult Index()
{
return View(sectionApp.GetAllSections());
}
Now I want to make my Index view paged.I want to use from PagedList.How should I do this?
For example I want every page shows 5 records.
You could pass a page number and page size to the repository layer, and then use the Skip and Take Linq statements to filter out the rows you need:
public List<Section> GetAllSections(int pageSize=5, int pageNo)
{
return cacheSectionRepo.GetAllSections().Skip(pageSize * pageNo).Take(pageSize);;
}
alternatively you could perform that filtering in you r repository layer. Then with every request to the controller, you would send the pageNo to the controller, preferably through an AJAX request.

wicket - Implement Ajax add/remove items ListView

Im getting crazy about this issue. I implemented a ListView which you can add/remove TextField dinamically, but only the last TextField is removed.
An example:
// Object type which is used in the list
public class ExampleObject implements Serializable{
private String keyword;
public String getKeyword() {
return this.keyword;
}
public void setKeyword(String s) {
keyword = s;
}
}
//ListView
List<ExampleObject> keywordList = new ArrayList<ExampleObject>();
keywordList.add(new ExampleObject());
ListView keywordView = new ListView("keywordView", keywordList) {
#Override
protected void populateItem(final ListItem item) {
ExampleObject model = (ExampleObject) item.getModelObject();
item.add(new TextField("subKeyword", new PropertyModel(model, "keyword")));
// keyword remove link
AjaxSubmitLink removeKeyword = new AjaxSubmitLink("removeKeyword", myForm)
{
#Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
ExampleObject selected = (ExampleObject) item.getModelObject();
// I also tried deleting by index. println shows the
// selected object is the element I want to remove, so why always
// remove last object of the list?
keywordList.remove(selected);
if (target != null) {
target.addComponent(myForm);
}
}
};
item.add(removeKeyword);
// keyword add link
AjaxSubmitLink addKeyword = new AjaxSubmitLink("addKeyword", metadataForm)
{
#Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
keywordList.add(new ExampleObject());
if (target != null) {
target.addComponent(myForm);
}
}
};
item.add(addKeyword);
}
keywordView.setReuseItems(true);
metadataForm.add(keywordView);
Any help would be very appreciate, because I thing this issue is really a very stupid mistake but I cant get it!
Thanks
It might be as simple as getting rid of the line
keywordView.setReuseItems(true);
The reuseItems flag is an efficiency so that the page does not rebuild the ListView items unnecessarily, but it can lead to confusion such as what you're seeing.
ListView really wasn't made for use with forms though, and you'll probably be better off with another tactic entirely.
This blog entry on building a list editor form component might be useful. It will need some changes if you're not on Wicket 1.4, but similar stuff is definitely possible in Wicket 1.3, and the comments have some hints.
Read the javadoc of ListView#setReuseItems():
"But if you modify the listView model object, than you must manually call listView.removeAll() in order to rebuild the ListItems."
You can not use a ListView this way. Either use the members of ListView provided:
removeLink(java.lang.String id, ListItem<T> item)
and
newItem(int index)
but, I never used those. If I have to display a List and be able to add remove Items dynamically, I prefer the RefreshingView.
If you do use FormComponents inside a RefreshingView, make sure you set a Reusestartegy (setItemReuseStrategy())
Bert

Resources