Wicket - Update item inside listview with ajax timer - ajax

There is an example of a world clock on wicket-example http://www.wicket-library.com/wicket-examples/ajax/world-clock?0 .
I tried the same, but inside a list-view.
final DataView<LongRunningTask> dataView = new DataView<LongRunningTask>("sorting", adp) {
private static final long serialVersionUID = 1L;
#Override
protected void populateItem(final Item<LongRunningTask> item) {
...
final Label clock = new Label("timer", getCounter().getObject());
item.add(clock);
clock.setOutputMarkupId(true);
add(new AbstractAjaxTimerBehavior(Duration.seconds(1)) {
#Override
protected void onTimer(AjaxRequestTarget target) {
target.add(clock);
}
});
...
}
}
};
But this is not working. If I do
add(new AjaxSelfUpdatingTimerBehavior(Duration.seconds(1)));
inside the Page itself, then it works. But I don't want to refresh the page every second, I just want to refresh the one item (clock) inside the listView.
Any chance how I can make this work?

The simplest solution to this problem is to add the Behavior to the clock instead of the surrounding container.
You're adding the behavior to the DataView once per row. Since DataViews can't be updated via Ajax, this is bound to fail. Updating the clock component itself instead works.
final DataView<LongRunningTask> dataView = new DataView<LongRunningTask>("sorting", adp) {
private static final long serialVersionUID = 1L;
#Override
protected void populateItem(final Item<LongRunningTask> item) {
...
final Label clock = new Label("timer", getCounter().getObject());
item.add(clock);
clock.setOutputMarkupId(true);
clock.add(new AbstractAjaxTimerBehavior(Duration.seconds(1)) {
#Override
protected void onTimer(AjaxRequestTarget target) {
target.add(clock);
}
});
...
}
}
};
After having a second look at this, I think there is another culprit:
final Label clock = new Label("timer", getCounter().getObject());
I don't know exactly what getCounter().getObject() does but chances are that it doesn't return a model...
final Label clock = new Label("timer", new PropertyModel(this, "counter"));
should be a better fit here. You might habe to replace "this" with an approprately prefixed version to make sure the container defining getCounter() is adressed.
If this doesn't work, have a look at your AjaxDebugWindow. You should see data coming in every second. If it doesn't your component isn't updated. If the data doesn't change, your model isn't updated. Armed with this, you should be able to fix this easily.

You have to refresh the DataView, todo this add it to a WebMarkupContainer and target the WebMarkupContainer with the ajax timer behavior.
A second way could be to add the label and ajax timer behavior to a new Panel. Then add that panel to your DataView. Im not sure if this way would work, as i've never tried it before but its worth a go.

Related

JavaFX Tableview displaying Image#Hashcode rather than the Image itself from display model

I'm trying to display an Image for an object in a table view, I have created a DisplayModel class in order to display the relevant values in the table columns from an observable collection, I have 2 columns containing string values and one integer all of which are displaying fine, I also have an image of the Car I wish to display but it will only display the Image#Hashcode rather than the image itself.
The implementation I currently have is as follows:
The display model class:
private ObjectProperty<Image> image;
private SimpleStringProperty make;
private SimpleStringProperty model;
private SimpleDoubleProperty price;
public DisplayModel(Image image, String make, String model, Double price) {
this.image = new SimpleObjectProperty<>(image);
this.make = new SimpleStringProperty(make);
this.model = new SimpleStringProperty(model);
this.price = new SimpleDoubleProperty(price);
}
public Image getImage() {
return image.get();
}
public void setImage(Image displayImage) {
this.image = new SimpleObjectProperty<>(displayImage);
}
The implementation in the initialise method on the controller for the FXML file:
try {
OutputStream targetFile = new FileOutputStream(s + "\\" + imageUrl);
targetFile.write(fileBytes);
targetFile.close();
File f = new File(s + imageUrl);
image = new Image(f.toURI().toString());
} catch (IOException e) {
e.printStackTrace();
}
Using the ImageView controller and trying to display the same image presents it fine so I'm wondering whether it's do with the ObjectProperty implementation or the TableView control.. I've recently faced a similar and far more common issue of needing to override the toString method to return the actual value rather than the String#HashValue, I wonder whether there is a similar way to resolve this issue.
Any advice would be greatly appreciated, thanks for your time.
This has to do with the updateItem implementation of the TableCell returned by the default cellFactory. This is implemented as follows (ignoring empty cells):
If the item is a Node, display it using setGraphic with the item as parameter. This adds the Node to the scene inside the TableCell.
Otherwise convert the object to text using it's toString method and use setText to display the resulting value as text in the TableCell.
Image is not a subclass of Node; therefore you get the latter behavior.
To change this behavior, you need to use a custom cellFactory that deals with the item type properly:
ItemImage.setCellFactory(col -> new TableCell<Item, Image>() { // assuming model class named Car here; type parameters match the TableColumn
private final ImageView imageView = new ImageView();
{
// set size of ImageView
imageView.setFitHeight(50);
imageView.setFitWidth(80);
imageView.setPreserveRatio(true);
// display ImageView in cell
setGraphic(imageView);
}
#Override
protected void updateItem(Image item, boolean empty) {
super.updateItem(item, empty);
imageView.setImage(item);
}
});
Note that usually you don't keep the graphic for empty cells. I decided to do this in this case to keep the cell size consistent.

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) {
.....
}
}

Wicket - How to reload/refresh reusable components correctly?

I have a java class:
public Task {
private int id;
private Company sender;
private Company receiver;
//Getter and Setter
...
}
As you can see, I have 2 other custom classes in the task class. And a company has for example Adress and Directory.
I have a CompanyPanel which will reusable be used on the Page. Here is some code from the panel.
public class CompanyPanel extends Panel {
protected List<Company> companies;
public CompanyPanel(String id, IModel<Company> model) {
super(id,new CompoundPropertyModel<Company>(model));
companies = new ArrayList<Company>();
Company company_1 = new Company();
//Setting default predefined values for the company, so I can select it from the dropdown and to set fields automatically
company_1.setFtpAdress("adress1.com");
company_1.setFtpDir("/MusterDir/");
companies.add(company_1);
//SAME for another company
...
companies.add(comany_2);
...
final DropDownChoice<Company> companyList = new DropDownChoice<Company>("companies", model,
new LoadableDetachableModel<List<Company>>() {
#Override
protected List<Company> load() {
return companies;
}
}){
protected boolean wantOnSelectionChangedNotifications() {
return true;
}
};
add(companyList);
final TextField<String> ftpAdress = new TextField<String>("ftpAdress");
ftpAdress.setOutputMarkupId(true);
add(ftpAdress);
final TextField<String> ftpDir = new TextField<String>("ftpDir");
ftpDir.setOutputMarkupId(true);
add(ftpDir);
//added Ajax to dropdown to update textfields automatically, based on selection of dropdown
companyList.add(new AjaxFormComponentUpdatingBehavior("onchange")
{
#Override
protected void onUpdate(AjaxRequestTarget target)
{
target.add(ftpAdress);
target.add(ftpDir);
}
});
}
}
In the Page I use reuseable CompanyPanels.
...
CompanyPanel senderPanel = new CompanyPanel("senderPanel", new PropertyModel(task,"sender"));
senderPanel.setOutputMarkupId(true);
form.add(senderPanel);
CompanyPanel receiverPanel = new CompanyPanel("receiverPanel", new PropertyModel(task,"receiver"));
receiverPanel.setOutputMarkupId(true);
form.add(receiverPanel);
...
When I submit the form I do:
public void onSubmit(AjaxRequestTarget target, Form<?> form) {
//doSomething
target.add(senderPanel);
target.add(receiverPanel);
}
The problem: The company panel is not being rerendered. And I don't really know why.
Workflow:
I select a company from the dropdown panel
The TextFields(which are inside the companyPanel) will be set correctly, based on the dropdown
I modify a textField (which belongs to a company)
I submit the form
I change the company from the dropdown list
I change back to the first company -> PROBLEM: the modified textfields displays still the modified text inside. It was not reseted to the default values.
Any help very appreciated.
Of course they will display the modified values. You create a list of companies in the CompanyPanel constructor. When you modify a company's data, the object is modified inside that list.
A quick way to fix this would be to replace the CompanyPanel panel with a new instance of CompanyPanel in your onSubmit method. That would recreate the list of companies with your default values. You would of course lose the modified values.
Another possibly better fix is to move the companies list creation into the loadabledetachablemodel:
final DropDownChoice<Company> companyList = new DropDownChoice<Company>("companies", model,
new LoadableDetachableModel<List<Company>>() {
#Override
protected List<Company> load() {
List<Company>companies = new ArrayList<Company>();
Company company_1 = new Company();
//Setting default predefined values for the company, so I can select it from the dropdown and to set fields automatically
company_1.setFtpAdress("adress1.com");
company_1.setFtpDir("/MusterDir/");
companies.add(company_1);
//SAME for another company
...
companies.add(comany_2);
...
return companies;
}
This way the list of companies is recreated on every request with the default values.
Make sure you implement a proper equals() and hashCode() method in Company though for DropDownChoice to show the proper selected element though - because in this scenario the object in your model and the objects in the list may never be ==.
You have to provide more code. If you submit the correctly so that the model changes try:
senderPanel.modelChanged();
receiverPanel.modelChanged();
target.add(senderPanel);
target.add(receiverPanel);

Wicket: form submit with reusable components and AjaxButton

I'm using wicket version 6.
I have a form. as contents of the form, I have a FormComponentPanel that contains two DateTimeField's (org.apache.wicket.extensions.yui.calendar).
In the class containing the form I have one AjaxButton, and one AjaxLink, both doing the same: reading the models, creating a new object and sending it to some server for processing.
Anyhow,
when clicking on the Link my new object is created with the correct values except those newly dates selected with the datepicker
when clicking the Button I get some error ([AjaxRequestHandler#1701777932 responseObject [org.apache.wicket.ajax.AjaxRequestHandler$1#3e1]) but no further information on the error
well, I addressed the first issue (Link) with trying to add ajax update behavior to it, as suggested here , but the selected date is not updated in the model
the AjaxButton is created and onSubmit overwritten with just calling another method and target.add(form); also setOutputMarkupId is set to true, but it seems that still something is missing
In order to get it to work I'd just need to solve one of the problems, but it would be great if someone has a solution for both problems. Thanks in advance.
edit
public MyPanelIncludingForm() {
// ...
form.add(getRangePanel()); // creates a new TimeRangePanel and returns the instance
form.add(getSubmitButton());
// ...
}
private FormComponent<String> getSubmitButton() {
FormComponent<String> submitBtn = new AjaxButton("submitBtn", form) {
private static final long serialVersionUID = 3005L;
#Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
System.out.println("submit QT ajax button");
setResponsePage(HomePage.class);
sendQuery();
target.add(form);
}
#Override
protected void onError(AjaxRequestTarget target, Form<?> form) {
System.err.println("error occurred. " + target);
target.add(feedback);
}
};
submitBtn.setOutputMarkupId(true);
return submitBtn;
}
// separate FormComponentModel for timeRange
public class TimeRangePanel extends FormComponentPanel<MyRange> {
MyRange range;
PropertyModel<Date> dpFromPM = new PropertyModel<Date>(this, "range.start");
PropertyModel<Date> dpToPM = new PropertyModel<Date>(this, "range.stop");
public RangePanel(String id, IModel<MyRange> model) {
super(id, model);
dpFrom = new DateTimeField("dpFrom", dpFromPM) {
private static final long serialVersionUID = 3006L;
#Override
protected DateTextField newDateTextField(String id, PropertyModel<Date> model) {
DateTextField dtf = super.newDateTextField(id, model);
AjaxFormComponentUpdatingBehavior a = new AjaxFormComponentUpdatingBehavior("onChange") {
private static final long serialVersionUID = 3006L;
#Override
protected void onUpdate(AjaxRequestTarget target) {
System.out.println("here u " + dpFrom.getModelObject().toString());
}
};
dtf.add(a);
return dtf;
}
};
// second DateTimeField as dpFrom
} // end of constructor
#Override
protected void onBeforeRender() {
range = getModelObject();
super.onBeforeRender();
}
} // end of class
edit2
this is what the wicket ajax debug window is printing:
INFO: focus removed from
INFO: focus set on submitBtn11
INFO: Received ajax response (299 characters)
INFO:
<div wicket:id="feedbackQuery" class="feedback" id="feedbackQuery1c"><wicket:panel>
<ul wicket:id="feedbackul" class="feedbackPanel">
<li wicket:id="messages" class="feedbackPanelERROR">
<span wicket:id="message" class="feedbackPanelERROR"></span>
</li>
</ul>
</wicket:panel></div>
INFO: returned focused element: [object HTMLInputElement]
INFO: returned focused element: [object HTMLInputElement]
INFO: Response processed successfully.
INFO: refocus last focused component not needed/allowed
INFO: focus removed from submitBtn11
edit3
As I wrote in a comment:
I removed the re-usable components (FormComponentPanel) and now I don't get an error with the AjaxButton . anyhow, it's weird, I thought re-usable components should work, even with Ajax; also the models were assigned properly.
when clicking the Button I get some error
([AjaxRequestHandler#1701777932 responseObject
[org.apache.wicket.ajax.AjaxRequestHandler$1#3e1]) but no further
information on the error
=> You should run Wicket in DEVELOPMENT mode to get a more detailed trace.
You might simply add in your form a field called:
range.start
range.stop
and use the Object MyRange as property model instead create a new one for each field.
There is actually no need for that extra cycle. And I don't think Wicket will write the information back to the MyRange object if you create new PropertyModels for every attribute.
For example: http://svn.apache.org/viewvc/incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/web/components/admin/users/UserForm.java?view=markup
Line 276
and the corresponding HTML
http://svn.apache.org/viewvc/incubator/openmeetings/trunk/singlewebapp/src/org/apache/openmeetings/web/components/admin/users/UsersPanel.html?view=markup
Line 82
Sebastian

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