Updating a Wicket WebMarkupContainer - java

Inside my Wicket webpage, I have a WebMarkupContainer which contains a ListView:
notifications = new ArrayList<Notification>(...);
ListView listView = new ListView("notification", notifications) {
#Override
protected void populateItem(ListItem item) {
...
}
};
container = new WebMarkupContainer("container");
container.setOutputMarkupId(true);
container.add(listView);
this.add(container);
The WebMarkupContainer is in place in order to let me dynamically update the list of items shown to the user onscreen. This is possible when the user clicks on a link or by adding the container to incoming AjaxRequestTarget.
Now I'm required to update the list without having an Ajax request:
public void refresh() {
List<Notification> newNotifications = ...
notifications.addAll(0, newNotifications);
}
This method is called in a run-time environment and the list of notifications, which is a private field of my webpage (same one as last code), will contain new objects. I want these new items displayed to the user. Is it possible to update (or re-render) the container?
I'm new to Wicket so if you have a better way to achieve the same results, I would appreciate if you could share it with me.

You would have to do it on a timer. Use AjaxSelfUpdatingTimerBehavior to do so. Just set some sensible duration and add your container to target in 'onTimer()' method.
EDIT:
If your 'refresh()' function is only called when new notifications appear, you could set a flag on your page (define boolean variable on page and change it to true when new notification appears and to false once listView is refreshed). Then you can set short duration on the behavior and 'onTimer()' would look something like that:
onTimer(AjaxRequestTarget target) {
if(newNotifications) {
target.add(container);
newNotifications = false;
}
}
And refresh
public void refresh() {
List<Notification> newNotifications = ...
notifications.addAll(0, newNotifications);
newNotifiactions = true;
}
That way container won't be refreshed too often (which might cause strange effects) and will refresh every time new notification appears.

Related

Wicket's AjaxSelfUpdatingTimerBehavior stops updating after leaving and re-entering the page

I have this behavior added to a component(MarkupContainer)
AjaxSelfUpdatingTimerBehavior updateBehavior = new AjaxSelfUpdatingTimerBehavior(Duration.seconds(3))
{
#Override
public void onEvent(Component component, IEvent<?> event) {
// some business logic
}
};
Somewhere , on the same page I have an AjaxLink which redirects to another page(in whom constructor I pass the actual page as a parameter) and on that page I have a "Back" AjaxLink which redirects me back , calling setResponsePage(myFirstPage) .
The problem is that even though , when rendering the page the behavior updates once , it stops updating once at 3 seconds , as was constructed for.No problem faced with the behavior until leaving the page.
Probably not the best solution , but I managed to fix it by removing the behavior onBeforeRender() of the page and adding again . I declared a field on the page private int autoUpdateBehaviorId = -1;
public void addUpdateBehavior(Component c)
{
if(autoUpdateBehaviorId >= 0)
c.remove(c.getBehaviorById(autoUpdateBehaviorId));
AjaxSelfUpdatingTimerBehavior updateBehavior = new AjaxSelfUpdatingTimerBehavior(Duration.seconds(3))
{
#Override
public void onEvent(Component component, IEvent<?> event) {
// bussines logic
}
};
c.add(updateBehavior);
autoUpdateBehaviorId = c.getBehaviorId(updateBehavior);
}
#Override
protected void onBeforeRender() {
super.onBeforeRender();
addUpdateBehavior(myContainer);
}
Not necessarily the solution to your problem; but I have implemented the behavior by overriding onConfigure method of the AjaxSelfUpdatingTimerBehavior as below.
In my case, I had to update label with a count of current records in queue every 10 seconds.
Following is code snippet:
labelToBeUpdated.add(new AjaxSelfUpdatingTimerBehavior(Duration.seconds(configurableDelay)) {
#Override
public void onConfigure(Component component) {
String inProgressOutOfTotal = "10/100"; //Business logic to get total count and inprogress count
labelToBeUpdated.setDefaultModel(Model.of(inProgressOutOfTotal));
//Set visibility of the component if needed
}
}
labelToBeUpdated.setOutputMarkupId(true);
Just curious; is it that onEvent is waiting on an event on the component in order to refresh? Since onConfigure is called before the rendering cycle has begun, it is working for me.
But as Sven Meier has mentioned, you might still want to work on his advise to get your code with onEvent.

How can i refresh all the page without one component using timer of zk

I'm using timer to refresh all my page zul. Is it possible to refresh all the page without one component ("progressmeter")?
<progressmeter style="span.z-progressmeter-image"
value="#load(item.progres)" width="110px" height="16px">
</progressmeter>
Note: Assuming that you want to refresh the whole page(Desktop).
You can refresh the whole page without loosing the state of the <progressmeter>, just maintain the Item var on your session and every time the page is reloaded you can recover the last state in the ModelView class, for example:
public class MyVm{
....
private Item item;
#Init
public void initMyVM(){
Item item = Sessions.getCurrent().getAttribute("myItem");
if(item == null)
//do normal intialization
}
public Item getItem() {
return item;
}
public void setItem(Item item) { // or wherever it changes
this.item = item;
Session session = Sessions.getCurrent();
session.setAttribute("myItem", item); // or wherever it changes
}
#Command("onTimer")
pulic void comandRefresh(){
Executions.sendRedirect("");
}
....
}
Now you can refresh your page with Executions.sendRedirect(""); any time you want, you will not lost the state of your <progressmeter/>. The whole refresh must consider the initialization of your vars on #Init.
The other way is to notify changes to all the other vars associated with your View Contenten using #NotifyChange
#Command("onTimer")
#NotifyChange({"redrawValue1","RedrawValue2","others"....})
pulic void comandRefresh(){
Executions.sendRedirect("");
}
That means that all your content related with the values of "redrawValue1","RedrawValue2","others" will be updated on the view. If you need to force a redraw of some areas you can use templates Template and associated with fields in your VM in order to use #NotifyChange in the same manner.

how to reload or refresh a tab's content base on actions in vaadin

The content of the tab is formed and displayed when the application is loaded. Later the content of the tab may be changed by other actions. I want to show the newer content after each action. And each time when I click the tab sheet, the content should be refresh/updated. But I failed.
//the content of the tab from the "reprintsTab" class
//in the "reprintsTab" it query data from database and print out
//later I update the data in the database from somewhere else, and I want the tab shows the new content
//I want to click the tab sheet to reload the "reprintTab" class and print out the new content
//here is what I did:
public TabSheet sheet;
//add tab and add the content from "reprintTab" into this tab
sheet.addTab(new reprintsTab());
//add the listener
sheet.addListener(new TabSheet.SelectedTabChangeListener() {
#Override
public void selectedTabChange(SelectedTabChangeEvent event) {
//I know it does not work, because it only reload the class. but not put the content under the tab I want
new reprintsTab();
}
});
What should I do? please help me, thanks.
You can use TabSheet.replaceComponent method to do this:
//Field to store current component
private reprintsTab currentComponent;
//during initialization
currentComponent = new reprintsTab();
sheet.addTab(currentComponent);
sheet.addListener(new TabSheet.SelectedTabChangeListener() {
#Override
public void selectedTabChange(SelectedTabChangeEvent event) {
reprintsTab newComponent = new reprintsTab();
sheet.replaceComponent(currentComponent, newComponent);
currentComponent = newComponent;
}
});
Also, you might want to reload this tab only when it's shown:
sheet.addListener(new TabSheet.SelectedTabChangeListener() {
#Override
public void selectedTabChange(SelectedTabChangeEvent event) {
if (event.getTabSheet().getSelectedTab() == currentComponent) {
//here goes the code
}
}
});
This should work for you, but I would suggest a cleaner approach: implement reprintsTab as a container for components, create method reload or buildInterface method to refresh its' state, so you can just call:
currentComponent.reload();
when you need to update interface.
Also, I hope reprintsTab is just an example name, java class names starting with lowercase letter look ugly.

Wicket - updating ListView using AJAX and Wicket Model

I have a :
Client Class
ListView
TextField
I need to populate my ListView in order to form a table:
WORKING CODE:
clientModel = new LoadableDetachableModel() {
#Override
protected Object load() {
return Client.getClientListByCompanyName(searchClientInput.getValue());
}
};
searchClientInput.setModel(new Model<String>());
searchClientInput.add(new AjaxFormComponentUpdatingBehavior("onkeyup") {
#Override
protected void onUpdate(AjaxRequestTarget target) {
target.add(clientListViewContainer);
}
});
clientListView = new ListView<Client>(CLIENT_ROW_LIST_ID, clientModel) {
#Override
protected void populateItem(ListItem<Client> item) {
Client client = item.getModelObject();
item.add(new Label(CLIENT_ROW_COMPANY_CNPJ_ID, client.getCompanyName()));
item.add(new Label(CLIENT_ROW_COMPANY_NAME_ID, client.getCompanyCnpj()));
}
};
clientListViewContainer.setOutputMarkupId(true);
clientListViewContainer.add(clientListView);
add(clientListViewContainer);
Now, in my HTML, I have a TextField. Whenever an user types something in this TextField, a select will be made in the database with whatever he typed. So for each word, a select is made, and the table needs to be updated. I am guessing I will need to use AJAX and possibly a Model. I'm kind of lost about how I can do this, if someone can provide me examples I would be very grateful.
EDIT: New code that is throwing exception: Last cause: Attempt to set model object on null model of component: searchClientForm:searchClientInput
EDIT 2: Ok so the exception was that my TextField didn't had a model to bind data to. So what I did was: searchClientInput.setModel(new Model<String>());
I also had a problem with the event. Using onkeydown was working, but not as intended. I had Company Name 1-4. If I typed Company Name 1, I would need to press one key again so the table would get updated. With onkeyup this don't happens. Thanks for the help.
You could give the ListView a LoadableDetachableModel which provides the selected clients matching your TextField's value.
Use an AjaxFormComponentUpdatingBehavior on your TextField which add a parent of the ListView to the request target (don't forget #setOutputMarkupId().
I believe the best way to perform what you want (which is repainting a table/list at each input change --> DB access) is with a DataView and a DataProvider.
A DataView is just like the ListView component except it uses an IDataProvider to get the data you want to present. You are able to implement the DataProvider so it accesses your DB, and you can add restrictions (where clauses) to the DataProvider.
[this is more like pseudo-code]
public final class MyDataProvider<T> extends SortableDataProvider<T> {
// ...
Set filters;
// filters is the set where the restrictions you want to apply are stored
...
#Override
public Iterator<T> iterator(int first, int count) {
// DAO (Data Access Object) access to DB
// ...
return dao.findByRestrictions(filters).iterator();
}
...
}
Now on the ajax event on your input component you are able to update the filter being used in the DataProvider, and in the the next repaint of the DataView, the provider will "pull" the data matching the restrictions defined in the filter.
Hope it helps. Best regards.

Displaying number of search results in Wicket form

In my current Apache Wicket project I have a search form for querying the database and displaying the query results in a ListView. The search input box is on the same page as the ListView with the results, and that ListView is filled with query results from a DAO, during invocation of the onSubmit() method of the form.
Everything works fine, but I need to display the number of search results. I tried to create a Label that is filled with the value of the size() method of the list got by the getList() method of the ListView instance, but no luck.
Thank you for any help in advance.
Depending on how you have built this form, you might only need to do label.setModelObject(listResults.size()). It's difficult to tell without seeing how are you doing it.
By what you're telling in your question, probably you're creating your Label like this new Label(labelId, listView.getList().size(). This won't work, you're setting the Label's Model at construction time with a constant value, that's the size of the list at construction time. You need to get that value inside a Model's getObject() to make the value "dynamic". Like, for instance,
AbstractReadOnlyModel sizeModel = new AbstractReadOnlyModel(){
public getObject(){
return listView.getList().getSize();
}
}
new Label(labelId, sizeModel);
With this, every time the page renders, sizeModel().getObejct() will be called to retrieve the value for the Label. In that other way, the Label has got a Model with a constant value.
You could even do label.setModelObject(list.size()) in the onSubmit() method.
From my ignorance on how you have built this form, I'll show you how would I do this. The List of results would be retrieved with a LoadableDetachableModel. That would be the Model of the ListView. Then, the Label can have for instance an AbstractReadOnlyModel that uses the ListViews modelObject to get its size.
public class MyForm extends Form {
private LoadableDetachableModel resultsModel;
private IModel searchModel;
public MyForm(){
searchModel = new Model();
TextField searchTextField = new TextField("search", searchModel);
resultsModel = new LoadableDetachableModel(){
protected Object load(){
return myService.get(searchModel.getModelObject());
}
}
ListView lv = new ListView("list", resultsModel){
// ...
}
Label resultsCount = new Label("count", new AbstractReadOnlyModel(){
public Object getObject(){
return ((List) resultsModel.getObject()).size();
}
})
SubmitButton button = new SubmitButton(){
public void onSubmit(){
//... No actions needed, really
}
}
// add's...
}
}
Using a LoadableDetachableModel for the ListView has the advantage of automatically detaching the Model, and therefore avoiding the whole List of results to get serialized into the Session.

Categories