I'm trying to make my text fields erroneous when validation fails for a form component.
I'm adding an "error" value to my textfield class attribute which makes it red.
I do this by overriding the onValidate() method on the form and loping my components to see if they have errors.
#Override
protected void onValidate() {
super.onValidate();
Iterator<Component> compIter = iterator();
while(compIter.hasNext()) {
final Component comp = compIter.next();
if(comp instanceof AbstractTextComponent<?>) {
comp.add(new AttributeAppender("class", new Model<String>() {
#Override
public String getObject() {
return (comp.hasErrorMessage())?"error":"";
}
}, " "));
}
}
}
This works, but when I look at the generated HTML:
<input id="user_username" class="normal error error error" type="text" name="user.userName" value="stijn" maxlength="25" wicket:id="user.userName">
It generates the error value 3 times.
What am I doing wrong?
Is this the best way to accomplish this in wicket or are there better ways???
thx,
Koen
I don't know why it prints 3 times "error". Instead of writting your own tool you could use this one.
https://cwiki.apache.org/WICKET/automatic-styling-of-form-errors.html
Related
I have a Note element in my zend framework 1 form used for registration. It is defined in the format:
$captcha_reload = new Zend_Form_Element_Note('captcha_reload',
array('value'=>"<div id='captcha_reload_div'>
<a href='javascript:return false;'
id='change-image'>Change text.</a></div>"));
$this->addElement($captcha_reload);
This element displays a hyperlink and displays perfectly during registration page call.
The problem is during form submission. This note element doesn't displays anything (ie missing the hyperlink) if there is form validation error.
I have checked and tried the code below:
$this->setDefaults(array('captcha_reload'=>"<div id='captcha_reload_div'>
<a href='javascript:return false;'
id='change-image'>Change text.</a></div>"));
But still there is no value if there is form validation error.
For Note element, I have included the following in the Zend Registration Form page:
class Zend_Form_Element_Note extends Zend_Form_Element_Xhtml
{
public $helper = 'formNote';
}
When the form is submitted it is over-riding the value property of your element. As there is nothing being submitted, when the form is echoed again to show form errors, the value of the element is nothing as well.
Perhaps adding an isValid function to the element?
// pseudo-code
public function isValid($value, $context = null) {
$this->_setValue("<div id='captcha_reload_div'><a href='javascript:return false;' id='change-image'>Change text.</a></div>");
return true;
}
This will reset the value to your custom text, and return true without doing any checks (as you know the value is what you want it to be). Subsequently, when the form echos again it will show the value as set in isValid
class Zend_Form_Element_Note extends Zend_Form_Element_Xhtml
{
public $helper = 'formNote';
public function isValid($value, $context = null)
{
return true;
}
}
I have added that isValid() into Note class and it works fine. It doesn't need to use _setValue() inside Note class.
We are using wicket 6.
Both Session and Component classes have error() method to display an error. However in both cases these methods are final.
Is there any other universal way to add postfix to any error message? (we are looking to add error id)
Edit:
We have hundreds of files of code which already uses error() method from both Session and Component, so massive refactoring is not an option.
You can add arbitrary message objects to a Wicket component:
component.error(new ErrorCode(code));
With a custom FeedbackPanel you can then display the error code as needed:
protected Component newMessageDisplayComponent(String id, FeedbackMessage message)
{
Serializable rawMessage = message.getMessage();
if (rawMessage instanceof ErrorCode) {
// create custom component to display a text and/or code
...
} else {
return super.newMessageDisplayComponent(id, message);
}
}
is there any way to format number in ZK label component that looks like like
<label value="${each.value}" /> ? Values are doubles and I want to separate thousands etc... I know that doublebox for example has format property but what if I just want to display number as label? Thanks for any help.
Feature Request
First of all I have opened a feature request for this on ZK's tracking system you can find it here. Please follow this if you want updates.
Ways of implementing
There are in fact ways of implementing this depending on what pattern & techniques you are using.
MVC & EL
You can create an EL function which will do the formatting for you in your ZUL file. First of all create a class such as this:
public class FormatElNumber {
public static String formatStock(double stock) {
final NumberFormat nf = NumberFormat.getInstance();
nf.setMaximumFractionDigits(2);
return nf.format(stock);
}
}
This will output numbers with two decimal places. Secondly you need to add this to the top of your zul file:
<?xel-method prefix="c" name="formatStock" class="demo.grid.hierarchy.FormatElNumber"
signature="java.lang.String formatStock(double)"?>
Then when you have a label you can do as follows:
<label style="color:red;" value="${c:formatStock(each.averageHigh)}" />
More infomration on this technique is available here.
MVVM
The MVVM is actually easier to implement, you create what's called a Converter, for example (please note this class is untested, but you get the idea).
public class NumberFormatConverter implements Converter {
#Override
public Object coerceToBean(Object val, Component comp, BindContext ctx) {
return null;
}
#Override
public Object coerceToUi(Object val, Component comp, BindContext ctx) {
if(!(val instanceof Integer)) {
throw new IllegalArgumentException("The argument must be a number!");
}
final Object tmp = ctx.getConverterArg("length");
int length = 0;
if(tmp instanceof Integer) {
length = (Integer)tmp;
}
final NumberFormat nf = NumberFormat.getInstance();
nf.setMaximumFractionDigits(length);
return nf.format(val);
}
}
Then all you do in your zul file is specify you would like to use a converter on the value, for example:
<label value="#load(vm.message) #converter(vm.myConverter)"/>
For more information on this technique you can refer here.
From the docs: http://books.zkoss.org/wiki/ZUML_Reference/EL_Expressions/Core_Methods/formatNumber
<?taglib uri="http://www.zkoss.org/dsp/web/core" prefix="c"?>
<zk>
<label value="${c:formatNumber(2332315231, '$ ###,###,###.00')}" />
</zk>
I have a rather complex form in the way that the number of form fields is flexibel. In short, the model object is a TLabel (TranslationLabel) that contains a Map of values (translations). Language here is an enum so the idea is that the number of fields (text areas) for which a translation is given depends on the values in this enum.
This is my form (simplified):
public class TranslationEditForm extends Form {
private final static List<Language> LANGUAGES = newArrayList(Language.values());
public TranslationEditForm(String id, final TranslationLabelView label) {
super(id, new CompoundPropertyModel<TranslationLabelView>(label));
ListView<Language> textAreas = new ListView<Language>("translationRepeater", LANGUAGES) {
#Override
protected void populateItem(final ListItem<Language> itemLang) {
//loop through the languages and create 1 textarea per language
itemLang.add(new Label("language", itemLang.getModelObject().toString()));
Model<String> textModel = new Model<String>() {
#Override
public String getObject() {
//return the value for current language
return label.getValue(itemLang.getModelObject());
}
#Override
public void setObject(String object) {
//set the value for current language
label.getTranslations().put(itemLang.getModelObject(), object);
}
};
itemLang.add(new TextArea<String>("value", textModel).setRequired(true));
}
};
//add the repeater containing a textarea per language to the form
this.add(textAreas);
}
}
Now, it works fine, 1 text area is created per language and its value is also set nicely; even more when changed the model gets updated as intended.
If you submit the form after emptying a text area (so originally there was a value) then of course there is a validation error (required). Normal (wicket) behaviour would be that the invalid field is still empty but for some reason the original value is reset and I don't understand why.
If I override onError like this:
#Override
protected void onError() {
this.updateFormComponentModels();
}
then it is fine, the value of the field is set to the submitted value (empty) instead of the original value.
Any idea what is causing this? What is wicket failing to do because the way I've set up the form (because with a simple form/model this is working fine as are the wicket examples)?
Posted as answer, so the question can be marked as solved:
ListView does recreate all its items at render time. This means that the validation will be broken. Have a look at API doc of the ListView
Calling setReuseItems() on the ListView solves this.
Regards,
Bert
If there's a form, and has a textbox and a button, how do you erase the content of the textbox after you submit the form?
<h:inputText id="name" value="#{bean.name}" />
<h:commandButton id="submit" value="Add Name" action="#{bean.submit}" />
After I enter a value in the textbox and submit, the value still appears in the textbox. I need to clear the content of the textbox once its been submitted. How can I achieve this?
Introduction
There are several ways to achieve this. The naive way is to simply null out the fields in backing bean. The insane way is to grab JS/jQuery for the job which does that after submit or even during page load. Those ways only introduces unnecessary code and indicates a thinking/design problem. All you want is just starting with a fresh request/page/view/bean. Like as you would get with a GET request.
POST-Redirect-GET
The best way is thus to just send a redirect after submit. You probably already ever heard of it: POST-Redirect-GET. It gives you a fresh new GET request after a POST request (a form submit), exactly as you intended. This has the additional benefit that the previously submitted data isn't re-submitted when the enduser ignorantly presses F5 afterwards and ignores the browser warning.
There are several ways to perform PRG in JSF.
Just return to same view with faces-redirect=true query string. Assuming a /page.xhtml, you could do so in action method:
public String submit() {
// ...
return "/page.xhtml?faces-redirect=true";
}
If you're still fiddling around with navigation cases the JSF 1.x way, then it's a matter of adding <redirect/> to the navigation case in question. See also How to make redirect using navigation-rule.
To make it more reusable, you can obtain the view ID programmatically:
public String submit() {
// ...
UIViewRoot view = FacesContext.getCurrentInstance().getViewRoot();
return view.getViewId() + "?faces-redirect=true";
}
Either way, if you've view parameters which needs to be retained in the request URL as well, then append &includeViewParams=true to the outcome. See also Retaining GET request query string parameters on JSF form submit.
If you're making use of some URL rewriting solution which runs outside JSF context, then you'd best grab the current request URL (with query string) and use ExternalContext#redirect() to redirect to exactly that.
public void submit() throws IOException {
// ...
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
StringBuffer requestURL = ((HttpServletRequest) ec.getRequest()).getRequestURL();
String queryString = ((HttpServletRequest) ec.getRequest()).getQueryString();
ec.redirect((queryString == null) ? requestURL.toString() : requestURL.append('?').append(queryString).toString());
}
It's only a mess which should really be refactored to some utility class.
Request/View scoped bean
Note that this all works only nicely in combination with request or view scoped beans. If you've a session scoped bean tied to the form, then the bean wouldn't be recreated from scratch. You've then another problem which needs to be solved as well. Split it into a smaller session scoped one for the session scoped data and a view scoped one for the view scoped data. See also How to choose the right bean scope?
Faces Messages
If you've a faces message to be shown as result of successful action, then just make it a flash message. See also How to show faces message in the redirected page.
public String submit() {
// ...
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(clientId, message);
context.getExternalContext().getFlash().setKeepMessages(true);
return "/page.xhtml?faces-redirect=true";
}
Ajax
Only if you happen to have an ajax-only page on which a F5 would always trigger a fresh new GET request, then simply nulling out the model field(s) in action method shouldn't harm that much.
See also:
How to navigate in JSF? How to make URL reflect current page (and not previous one)
Pure Java/JSF implementation for double submit prevention
You can blank out the property of the managed bean that should not be repainted when you render the response. This can be done done using code similar to the snippet posted below:
private String name;
public String getName(){return name;}
public void setName(String name){this.name=name};
public String submit()
{
//do some processing
...
// blank out the value of the name property
name = null;
// send the user back to the same page.
return null;
}
The reason for the current behavior can be found in how the JSF runtime processes requests. All JSF requests to a view are processed in accordance with the JSF standard request-response lifecyle. In accordance with the lifecyle, the managed bean contents are updated with the value from request (i.e. the value of DataForm.Name is set) before the application event (DataForm.submit) is executed. When the page is rendered in the Render Response phase, the current value of the bean is used to render the view back to the user. Unless the value is changed in an application event, the value will always be one that is applied from the request.
You can clear the form from the Bean method that gets called when the form is submitted;`
private String name;
private String description;
private BigDecimal price;
/*----------Properties ------------*/
/*-----Getter and Setter Methods---*/
public void save()throws SQLException{
String sql = "INSERT INTO tableName(name,description,price) VALUES (?,?,?)";
Connection conn = ds.getConnection();
try {
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, getName());
pstmt.setString(2, getDescription());
pstmt.setBigDecimal(3, getPrice());
pstmt.executeUpdate();
} catch (SQLException e) {
e.getMessage();
e.toString();
}finally{
conn.close();
clear();
}
}//End Save Method
public void clear(){
setName(null);
setDescription(null);
setPrice(null);
}//end clear`
Notice that the clear() method is called from the save method after all the operations of the save method is complete. As an option you could perform the clearing only if the methods operation was successful...The method below is placed in the ProductController Class...
public String saveProduct(){
try {
product.save();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
The method call from the view/jsp would look like the Following:
<h:commandButton value="Save" action="#{productController.saveProduct}"/>
You can do it with jQuery.
I had the similar problem. I needed to clear popup window form.
<rich:popupPanel id="newProjectDialog" autosized="true"
header="Create new project">
<h:form id="newProjectForm">
<h:panelGrid columns="2">
<h:outputText value="Project name:" />
<h:inputText id="newProjectDialogProjectName"
value="#{userMain.newProject.projectName}" required="true" />
<h:outputText value="Project description:" />
<h:inputText id="newProjectDialogProjectDescription"
value="#{userMain.newProject.projectDescription}" required="true" />
</h:panelGrid>
<a4j:commandButton id="newProjectDialogSubmit" value="Submit"
oncomplete="#{rich:component('newProjectDialog')}.hide(); return false;"
render="projects" action="#{userMain.addNewProject}" />
<a4j:commandButton id="newProjectDialogCancel" value="Cancel"
onclick="#{rich:component('newProjectDialog')}.hide(); return false;" />
</h:form>
</rich:popupPanel>
jQuery code:
$('#newProjectForm').children('input').on('click', function(){$('#newProjectForm').find('table').find('input').val('');});
I added a code snippet how to reset all values for the current ViewRoot recursively for JSF 2 here:
Reset all fields in form
This works for submitted forms showing validation errors as well as for newly entered values in a form.