Wicket SELECT doesn't update it's model - select

i've wicket panel with list of ProductViews (as SELECT)
after you choose your ProductView from SELECT, its load Product from database by id of ProductView into details form. You can modify Product entity and you can save it when you finish.
After save i try to refresh SELECT list to update its data, but it doesn't work(i mean, for example, SELECT contains an old name of product after rename it, but when i select the same ProductView, it reload an entity into details form again, and of course the new data appears from database) i don't want to reload product list again, i want to solve it from memory. Here is my source:
ProductView:
#Entity
#Table(name = "product")
#XmlRootElement
public class ProductView implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#Enumerated(EnumType.ORDINAL)
#Column(name = "category")
private Category category;
public ProductView() {
}
public ProductView(Long id) {
this.id = id;
}
public ProductView(Product product) {
this.id = product.getId();
this.name = product.getName();
this.category = product.getCategory();
}
// + getters & setters
}
Product:
#Entity
#Table(name = "product")
#XmlRootElement
public class Product implements Serializable {
// same as ProductView but more data, objects, connections, etc
}
And wicket panel with comments
private Product product;
private ProductView productView;
private List<ProductView> productViews;
private Form productForm;
private Select categorySelectComponent;
private WebMarkupContainer contentContainer;
public ProductPanel(String id) {
super(id);
setOutputMarkupId(true);
add(contentContainer = new WebMarkupContainer("contentContainer")); // container DIV
contentContainer.setOutputMarkupId(true); // refreshable
contentContainer.add(productForm = new Form("productForm")); // details FORM
contentContainer.add(categorySelectComponent = new Select("categorySelectComponent", new PropertyModel<ProductView>(this, "productView"))); // item SELECT
categorySelectComponent.add( new SelectOptions<ProductView>( // first category
"oneCategory",
new PropertyModel<List<ProductView>>(this, "oneProducts"), // see getOneProducts(); list of productviews
new IOptionRenderer<ProductView>() {
#Override
public String getDisplayValue(ProductView p) {
return p.getName();
}
#Override
public IModel<ProductView> getModel(ProductView p) {
return new Model<ProductView>(p);
}
}));
categorySelectComponent.add( new SelectOptions<ProductView>( // second category
"twoCategory",
new PropertyModel<List<ProductView>>(this, "twoProducts"), // see getTwoProducts();
new IOptionRenderer<ProductView>() {
#Override
public String getDisplayValue(ProductView p) {
return p.getName();
}
#Override
public IModel<ProductView> getModel(ProductView p) {
return new Model<ProductView>(p);
}
}));
categorySelectComponent.add( new SelectOptions<ProductView>( // third category
"threeCategory",
new PropertyModel<List<ProductView>>(this, "threeProducts"), // see getThreeProducts();
new IOptionRenderer<ProductView>() {
#Override
public String getDisplayValue(ProductView p) {
return p.getName();
}
#Override
public IModel<ProductView> getModel(ProductView p) {
return new Model<ProductView>(p);
}
}));
categorySelectComponent.add(new OnChangeAjaxBehavior() { // update form after choose entity
#Override
protected void onUpdate(final AjaxRequestTarget art) {
product = getProductFacade().find( productView.getId() );
updatePanel(art);
}
});
productForm.add(
// some details component (textfields, radios, links, etc) to edit Product
);
productForm.add(new AjaxSubmitLink("formSubmitLink") { // save entity
#Override
protected void onSubmit(AjaxRequestTarget art, Form<?> form) {
super.onSubmit(art, form); // i don't know it is necessary at all
getProductFacade().edit( product );
updateProductViewInCategoryMap(art); // important method
//art.add(contentContainer); //it is in update method
}
});
}
more methods inside panel
private Map<Category, List<ProductView>> categoryMap; // all product by categories
public void initCategoryMap() {
categoryMap = new EnumMap<Category, List<ProductView>>(ProductView.class);
categoryMap.put( Category.ONE, new ArrayList<ProductView>() );
categoryMap.put( Category.TWO, new ArrayList<ProductView>() );
categoryMap.put( Category.THREE, new ArrayList<ProductView>() );
for (ProductView view : getProductViews()) {
categoryMap.get(view.getCategory()).add(view);
}
}
//***** Get Products By Categories *******
final public List<ProductView> getOneProducts(){
if (categoryMap == null){
initCategoryMap();
}
return categoryMap.get( Category.ONE );
}
final public List<ProductView> getTwoCategory(){
if (categoryMap == null){
initCategoryMap();
}
return categoryMap.get( Category.TWO );
}
final public List<ProductView> getThreeProducts(){
if (categoryMap == null){
initCategoryMap();
}
return categoryMap.get( Category.THREE );
}
// **************************************
public List<ProductView> getProductViews() { // Get All Product
if (productViews == null) {
productViews = getProductFacade().findAllProductAsView();
}
return productViews;
}
private void updatePanel(AjaxRequestTarget art) { // refresh panel
art.add(ProductPanel.this);
}
private void updateProductViewInCategoryMap(AjaxRequestTarget art) { // change Product in map after save (call from onSubmit method of AjaxSubmitLink)
for(Map.Entry<Category, List<ProductView>> entry : categoryMap.entrySet()){ // search category contains entity
if (entry.getValue().contains( productView )){
entry.getValue().remove( productView ); // remove entity from category
break;
}
}
productView = new ProductView( product ); // new productview by modified product
categoryMap.get( productView.getCategory() ).add( productView ); // add entity to it's category's list
art.add(contentContainer);
}
and HTML:
<select class="categorySelect" wicket:id="categorySelectComponent">
<optgroup label="Category One">
<wicket:container wicket:id="oneCategory">
<option wicket:id="option"></option>
</wicket:container>
</optgroup>
<optgroup label="Category Two">
<wicket:container wicket:id="twoCategory">
<option wicket:id="option"></option>
</wicket:container>
</optgroup>
<optgroup label="Category Three">
<wicket:container wicket:id="threeCategory">
<option wicket:id="option"></option>
</wicket:container>
</optgroup>
</select>
Any idea?

Call #setRecreateChoices(true) on all your SelectOptions.

What about updating productViews on saving changes or using LoadableDetachableModel instead of PropertyModel in categorySelectComponent?
here:
new Select("categorySelectComponent", new PropertyModel<ProductView>(this, "productView")

Related

Wicket - Load method in LoadableDetachableModel is not called after changing the value of the dropdown list

I am facing a problem with the update of a list that is filtered depending on the value of a dropdown.
This is a description of my model:
I have a list of users
When I click on a user, another list of orders of this user is
displayed
The list of orders is filtered according to the value of a dropdown
containig a list of status
Please, see the image below:
Users With orders
The filter is working well, but the problem that I am facing is that once I choose an element from the dropdown, the list of orders is no longer updated when the user is changed.
This is a snippet of my code:
Construction of the Order Panel and instantiation of the list:
public OrdersPanel(String id)
{
super(id);
this.setOutputMarkupId(true);
setOutputMarkupPlaceholderTag(true);
IModel<List<Order>> orderListModel = new OrderListModel();
orderListView = new orderListView("orderListView", orderListModel);
//...
}
OrderListModel:
private final class OrderListModel extends LoadableDetachableModel<List<Order>> {
private static final long serialVersionUID = 1L;
#Override
protected List<Order> load() {
//...
//We set the variable allOrders in order to be used later in the filtering process
//...
}
}
Construction of the dropdown:
private class StatusDropDown extends CustomDropDown<String> implements IAjaxIndicatorAware {
private static final long serialVersionUID = 1L;
private StatusDropDown(String id) {
super(id);
this.setNullValid(true);
StatusListModel statusModel = new StatusListModel();
setChoices(statusModel);
setChoiceRenderer(new StatusChoiceRenderer(statusModel));
}
#Override
protected void onUpdate(AjaxRequestTarget target) {
super.onUpdate(target);
if (target != null) {
new StatusDropDownRefreshEvent(this, target).fire();
target.addComponent(this);
}
}
/**
* disable ajax marker for the form fields
*/
public String getAjaxIndicatorMarkupId() {
return null;
}
}
CustomDropDown (Must be used by the context of the project on which I am working):
public class CustomDropDown<V> extends DropDownChoice<V> {
private static final long serialVersionUID = 1L;
public CustomDropDown(String id) {
this(id, id);
}
public CustomDropDown(String id, String property) {
super(id);
setOutputMarkupId(true);
setModel(new CustomComponentPropertyModel<V>(property));
add(new AjaxFormComponentUpdatingBehavior("onchange") {
private static final long serialVersionUID = 1L;
#Override
protected void onUpdate(AjaxRequestTarget target) {
new UpdateEvent(CustomDropDown.this, target).fire();
if (target != null) {
target.addComponent(CustomDropDown.this);
}
CustomDropDown.this.onUpdate(target);
}
});
}
#Override
protected void onComponentTag(ComponentTag tag) {
super.onComponentTag(tag);
if (!isValid()) {
tag.append("class", "invalid", " ");
FeedbackMessage message = getFeedbackMessage();
if (message != null) {
tag.put("title", message.getMessage().toString());
message.markRendered();
}
} else if (isRequired()) {
tag.append("class", "red-background", " ");
}
}
public void setWidth(String width) {
this.add(new AttributeAppender("style", true, new Model<String>("width:" + width), ";"));
}
public CustomDropDown<V> setChoices(V... choices) {
this.setChoices(Arrays.asList(choices));
return this;
}
protected void onUpdate(AjaxRequestTarget target) {
}
}
StatusDropDownRefreshEvent listener:
#AjaxUpdateEventListener
public void statusDropDownRefreshPanel(StatusDropDownRefreshEvent event){
if (event.getTarget() != null) {
orderListView.setList(getOrdersByStatus(allOrders));
event.getTarget().addComponent(this);
}
}
Changing of the user:
When the user is changed, an update event is fired from the users panel, and then cached in the orders panel:
#AjaxUpdateEventListener
public void refreshPanel(CustomerOrderRefreshEvent event) {
if (event.getTarget() != null) {
event.getTarget().addComponent(this);
this.onBeforeRender();
}
}
onBeforeRender() to determin the visibility of the panel (if no order is available then the orders panel is not visible)
#Override
public void onBeforeRender() {
setVisibilityAllowed(checkVisibility());
super.onBeforeRender();
}
Finally, the checkVisibility Method:
private boolean checkVisibility() {
if (isUserChanged()) {
List<Order> src = orderListView.getModelObject();
statusDropDown.setDefaultModelObject(null);
return CollectionUtils.isNotEmpty(src);
}
return true;
}
My main problem is that the changing of the selected user doesn't update the list of orders once a status is chosen from the list.
Thank you very much for your replies and your time.
Best regards.
I found a solution to my problem.
The list of orders wasn't updated because The method getObject was called on the wrong object.
The call of the load() method can be done via getObject(), but the condition is: The object must be detached (See the implementation of getObject() at this link)
The detached object in my case is the orderListModel and not the orderListView, so this is what I added to my code:
//Set the content of the list model
List<Order> orders = orderListModel.getObject(); //This invokes the load method
//Update the content of the list
orderListView.setList(orders);

TableViewer not refreshing in RAP

I tried to run my Eclipse RCP code to run in Eclipse RAP environment. In my Eclipse RCP code, there is functionality to add the rows in to table. But
adding the code does not work in Eclipse RAP. I am using TableViewer.
Following is my code.
public class BasicEntryPoint extends AbstractEntryPoint {
private static final int COLUMNS = 2;
private TableViewer viewer;
private class ViewContentProvider implements IStructuredContentProvider {
public Object[] getElements(Object inputElement) {
List<Person> list = (List<Person>) inputElement;
return list.toArray();
}
public void dispose() {
}
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
}
}
private class ViewLabelProvider extends LabelProvider implements
ITableLabelProvider {
public Image getColumnImage(Object element, int columnIndex) {
return null;
}
public String getColumnText(Object element, int columnIndex) {
Person p = (Person) element;
if (columnIndex == 0) {
return p.getName();
}
return p.getPlace();
}
}
private class Person{
String name;
String place;
public void setName(String name) {
this.name = name;
}
public void setPlace(String place) {
this.place = place;
}
public String getName() {
return name;
}
public String getPlace() {
return place;
}
}
public List<Person> persons() {
List<Person> list = new ArrayList<Person>();
Person person = new Person();
person.setName("bb");
person.setPlace("jjj");
list.add(person);
person = new Person();
person.setName("sss");
person.setPlace("fff");
list.add(person);
return list;
}
#Override
protected void createContents(Composite parent) {
parent.setLayout(new GridLayout(2, false));
viewer = new TableViewer(parent, SWT.NONE);
viewer.setContentProvider(new ViewContentProvider());
viewer.setLabelProvider(new ViewLabelProvider());
final Table table = viewer.getTable();
viewer.setColumnProperties(initColumnProperties(table));
viewer.setInput(persons());
viewer.getTable().setHeaderVisible(true);
Button checkbox = new Button(parent, SWT.CHECK);
checkbox.setText("Hello");
Button button = new Button(parent, SWT.PUSH);
button.setText("World");
button.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
System.out.println("Button clicked");
Person p = new Person();
p.setName("Dee");
p.setPlace("TCR");
persons().add(p);
String prop[] ={"name","place"};
viewer.update(p, prop);
//viewer.refresh();
}
});
}
private String[] initColumnProperties(Table table) {
String[] result = new String[COLUMNS];
for (int i = 0; i < COLUMNS; i++) {
TableColumn tableColumn = new TableColumn(table, SWT.NONE);
result[i] = "Column" + i;
tableColumn.setText(result[i]);
if (i == 2) {
tableColumn.setWidth(190);
} else {
tableColumn.setWidth(70);
}
}
return result;
}
}
You should use:
viewer.add(p);
rather than update to add a new item to a table (both for SWT and RAP).
You must also update your model to contain the new item.

apache wicket - column link inside DataTable on table change (lets say filter and getting 0 rows) the opened link page fail

scenario : I am using data table with columns
one of the columns is link .
on run time I calculate the link created
the problem start when:
1. opennig table page P1
2. I clicked on link open in new tab
2.new tab is created with this URL
mit:8080/backoffice/?4-1.ILinkListener-MainPanelComponentWrapper-MainPanelComponent-table-gridForm-grid-body-rows-3-cells-2-cell-link
which is a component on P1 ,this tab generate P2.
changing P1 ,filtering on ajax and the table is empty so table-gridForm has no data
refreshing P2
getting exception
org.apache.wicket.WicketRuntimeException: Component 'MainPanelComponentWrapper:MainPanelComponent:table:gridForm:grid:body:rows:1:cells:2:cell:link' has been removed from page.
i need to create a redirected link that the new page woul be linked to .
how can i achive it ?
public class LinkPropertyColumn<T extends IEntity> extends BOPropertyColumn<T, String> implements IBOExportableColumn<T, String, Object> {
private static final long serialVersionUID = 1L;
private String headerTilte;
private GridViewType navigateTo;
private String routingByProperty;
private String entityId;
private String navigateToDynamicFunction;
private Map<String, String> filterByMap;
protected BOLinkPanel<T> linkPanel ;
public LinkPropertyColumn(String displayModel, String propertyToSortBy, String propertyExpression, String entityId,
String routingByProperty, String headerTilte, String navigateToDynamic) {
super(Model.of(displayModel), propertyToSortBy, propertyExpression);
this.headerTilte = headerTilte;
this.entityId = entityId;
this.navigateToDynamicFunction = navigateToDynamic;
this.routingByProperty = routingByProperty;
}
#Override
public Component getHeader(String componentId) {
return new Label(componentId, headerTilte);
}
#Override
public void populateItem(Item<ICellPopulator<T>> item, String componentId, final IModel<T> rowModel) {
linkPanel = new BOLinkPanel<T>(componentId, rowModel, getPropertyExpression()) {
private static final long serialVersionUID = 1L;
#Override
void onLinkClicked() {
LinkPropertyColumn.this.onLinkClicked(rowModel);
}
};
item.add(linkPanel);
}
public void onLinkClicked(IModel<T> rowModel) {
doing stuff...
params.add(HomePage.ENTITY_ID, idProperty);
final Object routingProperty = routingByProperty == null ? idProperty : BeanUtils.getProperty(object, routingByProperty);
params.set(HomePage.ROUTING_PROPERTY, routingProperty);
HomePage homePage = new HomePage(params);
final RequestCycle requestCycle = RequestCycle.get();
requestCycle.setResponsePage(homePage);
}
}
}
and :
public abstract class BOLinkPanel<T> extends Panel {
private static final long serialVersionUID = 1L;
/**
* #param id
*/
public BOLinkPanel(String id, IModel<T> model, String propertyExpression) {
super(id);
AbstractLink link = getLink();
link.add(new Label("caption", new PropertyModel<String>(model.getObject(), propertyExpression)));
add(link);
}
protected AbstractLink getLink() {
Link<Void> link = new AjaxFallbackLink<Void>("link") {
private static final long serialVersionUID = 1L;
#Override
public void onClick(AjaxRequestTarget target) {
// TODO Auto-generated method stub
BOLinkPanel.this.onLinkClicked();
}
// #Override
// public void onClick() {
// BOLinkPanel.this.onLinkClicked();
//
// }
};
return link;
}
abstract void onLinkClicked();
}
i did :
requestCycle.setResponsePage(HomePage.class, params);

Struts 2 ModelDriven Action suporting both a list and individual items

I have inherited some struts2 REST-plugin based code, and the following construct puzzles me:
#Namespace("/merchants/{id}")
public class MerchantAction extends ActionSupport implements ModelDriven<Object> {
private Merchant merchant = new Merchant(); // A Model
private Iterable<Merchant> merchants; // A list of models
....
public HttpHeaders index() {
merchants = merchantService.findAllMerchants();
return new DefaultHttpHeaders("index");
}
#Override
public Object getModel() {
return (merchant != null ? merchant : merchants);
}
public void setId(String id) {
merchant = merchantService.findMerchant(id));
}
In other words, it seems to be toggling between returning a list and returning an individual item in the getModel() call. Is this kosher ? Looks a bit strange to me
I've considered your approach, but finally gave it up. IMO, it lost the advantage of strong typed action.
My solution is, creating a ViewModel for each action. In the view models, there can be the single model, the list of the model, and other items for pages usage, such as items for drop down list or radio buttons.
So the UserViewModel is like:
public class UserViewModel implements IViewModel<User> {
private User model;
private List<User> list;
public void setModel(User user) {
this.model = user;
}
public User getModel() {
return model;
}
public void setList(List<User> list) {
this.list = list;
}
public List<User> getList() {
return list;
}
}
And the actions are like:
public class UserController implements ModelDriven<UserViewModel> {
private int id;
private UserViewModel model = new UserViewModel();
public String index() {
return "success";
}
public String show() {
return "success";
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return this.id;
}
#Override
public UserViewModel getModel() {
return model;
}
}
But in this way, I still lose the shortcut way in jsp files. I should write long model.userName instead of short userName.
I'm still finding the best solution of it.

gwt - celltable - adding extra row

enter code hereI have a celltable and the columns contains some numbers. I want to add an extra row at the end of the table which will hold the total for each column. Is there any way to do this?
Following is my code:
import java.util.*;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.TextColumn;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.view.client.ListDataProvider;
public class TestProject implements EntryPoint
{
private static int totalSalary=0;
private static class Contact
{
private final String salary;
private final String name;
public Contact(String name, String salary)
{
this.name = name;
this.salary = salary;
}
}
private static List<Contact> CONTACTS = Arrays.asList(new Contact("John","100000"),
new Contact("Mary", "200000"),
new Contact("Zander", "300000"));
/**
* This is the entry point method.
*/
public void onModuleLoad()
{
final CellTable<Contact> table = new CellTable<Contact>();
// Create name column.
TextColumn<Contact> nameColumn = new TextColumn<Contact>()
{
#Override
public String getValue(Contact contact)
{
return contact.name;
}
};
// Create address column.
TextColumn<Contact> addressColumn = new TextColumn<Contact>()
{
#Override
public String getValue(Contact contact)
{
totalSalary+=Integer.parseInt(contact.salary);
return contact.salary;
}
};
// Add the columns.
table.addColumn(nameColumn, "Name");
table.addColumn(addressColumn, "Salary");
// Create a data provider.
ListDataProvider<Contact> dataProvider = new ListDataProvider<Contact>();
// Connect the table to the data provider.
dataProvider.addDataDisplay(table);
// Add the data to the data provider, which automatically pushes it to the
// widget.
final List<Contact> list = dataProvider.getList();
for (Contact contact : CONTACTS) {
list.add(contact);
}
// We know that the data is sorted alphabetically by default.
table.getColumnSortList().push(nameColumn);
Contact total = new Contact("Total: ",totalSalary+"");
list.add(total);
// Add it to the root panel.
RootPanel.get().add(table);
//RootPanel.get().add(add);
}
}
Also I would suggest that you use the footer-Argument when you add the column:
addColumn(Column col, Header header)
When you mean totals im not quite sure what you mean but this is similiar to your code but I have added a button that will add the row you howeve can take this out and just add the row.
/**
* Entry point classes define <code>onModuleLoad()</code>.
*/
public class TestGwt implements EntryPoint {
private static class Contact {
private final String address;
private final String name;
public Contact(String name, String address) {
this.name = name;
this.address = address;
}
}
private static List<Contact> CONTACTS = Arrays.asList(new Contact("John",
"123 Fourth Road"), new Contact("Mary", "222 Lancer Lane"), new Contact(
"Zander", "94 Road Street"));
/**
* This is the entry point method.
*/
public void onModuleLoad() {
// Create a CellTable.
final CellTable<Contact> table = new CellTable<Contact>();
// Create name column.
TextColumn<Contact> nameColumn = new TextColumn<Contact>() {
#Override
public String getValue(Contact contact) {
return contact.name;
}
};
// Make the name column sortable.
nameColumn.setSortable(true);
// Create address column.
TextColumn<Contact> addressColumn = new TextColumn<Contact>() {
#Override
public String getValue(Contact contact) {
return contact.address;
}
};
// Add the columns.
table.addColumn(nameColumn, "Name");
table.addColumn(addressColumn, "Address");
// Create a data provider.
ListDataProvider<Contact> dataProvider = new ListDataProvider<Contact>();
// Connect the table to the data provider.
dataProvider.addDataDisplay(table);
// Add the data to the data provider, which automatically pushes it to the
// widget.
final List<Contact> list = dataProvider.getList();
for (Contact contact : CONTACTS) {
list.add(contact);
}
// We know that the data is sorted alphabetically by default.
table.getColumnSortList().push(nameColumn);
Button add = new Button("Add Row");
add.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
list.add(new Contact(Integer.toString(table.getRowCount()),Integer.toString(table.getRowCount())));
}
});
// Add it to the root panel.
RootPanel.get().add(table);
RootPanel.get().add(add);
}
}
Hopefully this will help I am not sure what you mean by total though anything can be added to that final field in the Guise of a contact even though it isnt neccessarily one , the better approach would be to use Generics for the data provider but this will achieve same effects with minimal code
public void addNewRow(){
List<Contact> newContactLst = Arrays.asList(new Contact("TEST",
"Sample"));
int numRows = table.getRowCount();
table.setRowCount(numRows+1);
table.setRowData(numRows,newContactLst);
}