Apache wicket: how to update model after validation error - java

I have form with dateTimeField, and ListView.
ListView looks like that:
final ListView<String> countryView = new ListView<String>("country", model.<List<String>>bind("country")) {
#Override
protected void populateItem(final ListItem<String> item) {
final String country = item.getModelObject();
item.add(new ValidationDisplayableLabel("country", country, new String[] { modelPath }));
item.add(new AjaxLink("deleteLink") {
#Override
public void onClick(AjaxRequestTarget target) {
model.getObject().getCountry().remove(country);
if (issPeriod) {
addButton.setVisible(true);
countryTextField.setVisible(true);
findButton.setVisible(true);
}
if (target != null)
target.addComponent(rowPanel);
}
});
}
};
countryTextField = new ValidationDisplayableTextField("countryCodeInput", model.bind("oneCountry"), "job.country.value");
**countryView.setReuseItems(true);**
rowPanel.add(countryView);
rowPanel.add(countryTextField);
addButton.setOutputMarkupPlaceholderTag(true);
rowPanel.add(addButton);
And the addButton looks like that:
AjaxSubmitLink addButton = new AjaxSubmitLink(LinkNames.addCountry.toString()) {
#Override
public void onSubmit(AjaxRequestTarget target, Form form) {
if (model.getObject().getOneCountry() != null)
addCountry();
if (target != null)
target.addComponent(rowPanel);
target.addComponent(form.getPage().get("feedbackPanel"));
}
#Override
protected void onError(AjaxRequestTarget target, Form<?> form)
{
onSubmit(target, form);
}
};
The thing is, that when I fail my dateTimeField (e.g. set hours to 100), enter country code in countryTextField, and press on addButton, it displays validation message in feedback panel, that hour range is incorrect, but don't add the country. This is because my model isn't updated. Maybe there is a way to update it manually? So validation message will be displayed, but the country listView still could be updated?
Submit of the whole form is on other button, so logically it is normal to add a country even if there is a validation error in dateTimeField.
Thanks!
P.S. i've read a lot of posts about similar problem, but most of them were solved with .setReuseItems(true), but it doesn't work in my case.
P.P.S Apache wicket 1.4.17

As an update to this answer, in Wicket 6, you can accomplish this by overriding onError() in the Form:
#Override
protected void onError() {
super.onError();
this.updateFormComponentModels();
}

I faced a similar problem in my project, the workaround I found was to use a special Visitor. It will update the model even though the submitted input is invalid.
public class VisitorUpdateModelWithoutValidation implements FormComponent.IVisitor {
public Object formComponent(IFormVisitorParticipant formComponent) {
if (formComponent instanceof FormComponent) {
final FormComponent<?> formComponent1 = (FormComponent<?>) formComponent;
boolean required = formComponent1.isRequired();
if (required) {
formComponent1.setRequired(false);
}
formComponent1.modelChanging();
formComponent1.validate();
formComponent1.updateModel();
formComponent1.modelChanged();
if (required) {
formComponent1.setRequired(true);
}
}
return Component.IVisitor.CONTINUE_TRAVERSAL;
}
}
Simply use it in the onSubmit method of your behavior : getForm().visitFormComponents(new VisitorUpdateModelWithoutValidation());

You can issue a field.clearInput() on the fields you are updating before you target the update(s).

Related

Apache wicket breadcrumb on one page

I would like to create breadcrump on my page, but I am unable to do it by following wicket examples.. I need to extend BreadCrumbPanel when I also need to extend WebPage..
I want breadcrumb to be visible only on this specific page, after clicking some of links on the page(they call setresponsepage with some parameters) I would like to append my breadcrumb
public final class GroupMainPage extends WebPage {
// Map<String, Group> hostGroup = PropertiesLoader.getInstance().getHostGroupMap();
List<Group> hostGroup = PropertiesLoader.getInstance().getHostGroupList();
public GroupMainPage() {
super();
HostGroupManager.getInstance();
createMainTable(hostGroup);
add(new Label("message", "OK \n ok"));
}
then I have some methods which help me create table on my page, especially this one with links where I would like to append my breadcrumb:
ListView<Component> list = new ListView<Component>("groupListElement", ((Group) component).getDependentGroups()) {
#Override
protected void populateItem(ListItem<Component> item) {
final Component c = item.getModelObject();
Link link = new Link("groupLink") {
#Override
public void onClick() {
Group g = (Group) c;
if (g.getDependentGroups() != null && g.getDependentGroups().size() > 0) {
setResponsePage(new GroupMainPage(g.getDependentGroups()));
} else {
setResponsePage(new GroupServiceListPage(g.createServicesList()));
}
}
};
link.add(new Label("linkLabel", c.getName()));
item.add(link);
How to make it possible?

Wicket - hide element on AjaxButton during lazy loading

I have a page with AjaxLazyLoadPanel, which contain a long-loading list and submitting AjaxButton.
After the AjaxLazyLoadPanel is ready, when I submit, the another long-loading is executing, and after that I need to refresh whole page. This is where I get myself, the code looks as follows:
AjaxButton button1 = new AjaxButton("submit1") {
#Override
protected void onSubmit(AjaxRequestTarget target, Form form) {
someLongWorkingMethod();
setResponsePage(page); //refreshing page
}
};
add(button1);
And it works perfectly. But what I'm trying to do now - is to disable this button or hide this button (or the whole panel if its necessary) when the method is computing, and when the page refresh with setResponsePage I'd like to have this button back.
I've read many posts/mailing lists about it but nothing helped me, I tried all things that I found and placed it before someLongWorkingMethod():
ajaxLazyLoadPanel.setOutputMarkupId(true);
ajaxLazyLoadPanel.setOutputMarkupPlaceholderTag(true);
ajaxLazyLoadPanel.add(new AttributeAppender("style","display:none;"));
ajaxLazyLoadPanel.setVisible(false);
ajaxLazyLoadPanel.setEnabled(false);
target.add(ajaxLazyLoadPanel);
And the same not for ajaxLazyLoadPanel but for "this" (AjaxButton).
Am I doing something terribly wrong or what? Is this what I'm trying to do even possible? I would really appreciate some help.
In your AjaxButton you could override updateAjaxAttributes() and add an IAjaxCallListener which hides the button via jQuery right after you click the button. When the responsePage is rendered the button appears again. The code for the override looks like this:
#Override
protected void updateAjaxAttributes(AjaxRequestAttributes attributes) {
super.updateAjaxAttributes(attributes);
attributes.getAjaxCallListeners().add(new IAjaxCallListener() {
#Override
public CharSequence getBeforeHandler(Component cmpnt) {
return "$(\"#" + cmpnt.getMarkupId() + "\").hide()";
}
#Override
public CharSequence getPrecondition(Component cmpnt) {
return "";
}
#Override
public CharSequence getBeforeSendHandler(Component cmpnt) {
return "";
}
#Override
public CharSequence getAfterHandler(Component cmpnt) {
return "";
}
#Override
public CharSequence getSuccessHandler(Component cmpnt) {
return "";
}
#Override
public CharSequence getFailureHandler(Component cmpnt) {
return "";
}
#Override
public CharSequence getCompleteHandler(Component cmpnt) {
return "";
}
});
}
Thanks to #Robert Niestroj answer (but working only in newest Wicket 6), I found solution for Wicket 1.5, I hope it will help someone.
Its just similar idea, but achieved with overriding getAjaxCallDecorator for AjaxButton, and using pure Javascript
AjaxButton button1 = new AjaxButton("submit1") {
String id = this.getMarkupId();
#Override
protected void onSubmit(AjaxRequestTarget target, Form form) {
...
}
#Override
protected org.apache.wicket.ajax.IAjaxCallDecorator getAjaxCallDecorator() {
return new AjaxCallDecorator() {
#Override
public CharSequence decorateScript(Component component, CharSequence script) {
return "document.getElementById(\'"+id+"\').style.display = 'none';"+script;
}
};
}
};
Important thing to remember is to ensure that you are returning script+[your added script], because otherwise your onsubmit action won't happen.
More info (and also onSucces and onFailure versions) in Wicket 1.5 doc

How do I update a ColourHighlighter (swingx) when the predicate has changed

I have a class called ErrorHighlighter which gets notified anytime a property called errorString is changed. Based on this propertychangeevent I update the HighLighterPredicate to highlight a particular row with a red background.
ErrorHighlighter receives the propertychangeevent, it also changes the HighlighterPredicate, but the table row does not get updated with red background.
I also update the tooltip of the row. That does not get reflected either.
Please see the code below. Could someone please help?
public class ErrorRowHighlighter extends ColorHighlighter implements PropertyChangeListener {
private Map<Integer, String> rowsInError;
private SwingObjTreeTable<ShareholderHistoryTable> treeTable;
public ErrorRowHighlighter(SwingObjTreeTable<ShareholderHistoryTable> treeTable) {
super(CommonConstants.errorColor, null);
this.treeTable = treeTable;
rowsInError=new HashMap<Integer, String>();
setHighlightPredicate(new HighlightPredicate() {
#Override
public boolean isHighlighted(Component renderer, ComponentAdapter adapter) {
if(rowsInError.containsKey(adapter.row)){
return true;
}
return false;
}
});
this.treeTable.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
int row=ErrorRowHighlighter.this.treeTable.rowAtPoint(e.getPoint());
if(rowsInError.containsKey(row)){
ErrorRowHighlighter.this.treeTable.setToolTipText(rowsInError.get(row));
}else{
ErrorRowHighlighter.this.treeTable.setToolTipText(null);
}
}
});
}
public void highlightRowWithModelDataAsError(ShareholderHistoryTable modelData){
int indexForNodeData = treeTable.getIndexForNodeData(modelData);
if(indexForNodeData>-1){
rowsInError.put(indexForNodeData, modelData.getErrorString());
updateHighlighter();
}
}
public void unhighlightRowWithModelDataAsError(ShareholderHistoryTable modelData){
int indexForNodeData = treeTable.getIndexForNodeData(modelData);
if(indexForNodeData>-1){
rowsInError.remove(indexForNodeData);
updateHighlighter();
}
}
public void updateHighlighter(){
treeTable.removeHighlighter(this);
treeTable.addHighlighter(this);
}
#Override
public void propertyChange(PropertyChangeEvent evt) {
ShareholderHistoryTable sourceObject= (ShareholderHistoryTable) evt.getSource();
if(StringUtils.isNotEmpty(sourceObject.getErrorString())){
highlightRowWithModelDataAsError(sourceObject);
}else{
unhighlightRowWithModelDataAsError(sourceObject);
}
}
}
This looks like a mistake on my part. The method treeTable.getIndexForNodeData() actually returns back the index of the row by doing a pre-order traversal of the underlying tree data structure. This includes a root node that is not being displayed on the jxtreetable. Hence I needed to minus 1 from the index
int indexForNodeData = treeTable.getIndexForNodeData(modelData)-1;
This fixed the problem for me. I am leaving the post rather than deleting it if anyone wants to look at an example of a ColorHighlighter and a property change listener.

Wicket: Update Model on DropDownChoice Item Selection Event

I need to update a model or object immediately after select
a DropDownChoice item.
Bellow is the code that I'm working:
add(new ListView[Company]("listCompanies", listData) {
override protected def onBeforeRender() {
//
// ...
super.onBeforeRender()
}
def populateItem(item: ListItem[Company]) = {
var company = item.getModelObject()
//...
val listClients: java.util.List[Client] = clientControler.listClients
item.add(new DropDownChoice("clientSelection", listClients,new ChoiceRenderer[Client]("name")))
In the Listview with properties of Company Object,
after choose a name property of the DropDownChoice, the model
Company would be updated with the Client Name selected.
How can I achieve this?
Thanks
I think you can override onSelectionChanged. But you also need to override wantOnSelectionChangedNotifications to return true to make it work. Something like this.
DropDownChoice<Client> dropDownChoice = new DropDownChoice("clientSelection", listClients) {
#Override
protected boolean wantOnSelectionChangedNotifications() {
return true;
}
#Override
protected void onSelectionChanged(Object newSelection) {
// Do something here when selection is changed
}
};
you need to add updating behavior:
add(new DropDownChoice<Client>("clientSelection", listClients)
.add(new AjaxFormComponentUpdatingBehavior("onchange") {
private static final long serialVersionUID = 1L;
#Override
protected void onUpdate(AjaxRequestTarget target) {
// update your model here
// then you need to add model to target
target.add();
}
}));
I think you can use an AjaxEventBehavior on your DropDownChoice:
item.add(new DropDownChoice("clientSelection", listClients,new ChoiceRenderer[Client("name")).add(new AjaxEventBehavior("change") {
#Override
protected void onEvent(AjaxRequestTarget target) {
// TODO Auto-generated method stub
}
}))

GWT GXT StoreFilterField event

I would like to search into my tree only if the user type 3 character at least.
How i can catch "doSelect" event to do this?
If i add a keylistener on the text field to check its value, the handler is invoked after the doSelect.
Can anyone help me?
This is an example...
filterText = new StoreFilterField<ModelData>() {
#Override
protected boolean doSelect(Store<ModelData> store,
ModelData parent, ModelData record, String property,
String filter) {
String name = record.get("name");
name = name.toLowerCase();
if (name.startsWith(filter.toLowerCase()))
return true;
else
return false;
}
};
KeyListener keyListener = new KeyListener() {
public void componentKeyUp(ComponentEvent event) {
loadingImage.setVisible(false);
if(filterText.isValid() && filterText.getRawValue().length()>=FILTER_MIN_SIZE) {
filterText.bind(store);
} else {
filterText.unbind(store);
}
}
};
filterText.setMinLength(FILTER_MIN_SIZE);
filterText.bind(store);
(Appears to be GXT 2, let me know if you are actually using GXT 3)
StoreFilterField relies on the applyFilters method to actually apply all of these. It has a check already that the text has at least one character:
protected void applyFilters(Store<M> store) {
if (getRawValue().length() > 0) {
store.addFilter(filter);
store.applyFilters(property);
} else {
store.removeFilter(filter);
}
}
The easiest way I see to override this would be to subclass StoreFilterField and redefine this method:
filterText = new StoreFilterField<ModelData>() {
#Override
protected void applyFilters(Store<M> store) {
if (getRawValue().length() > 3) {
store.addFilter(filter);
store.applyFilters(getProperty());
} else {
store.removeFilter(filter);
}
}
}

Categories

Resources