Grails Duplicate Error Messages - validation

I'm new to grails and I have a problem:
I have this snippet of GSP:
<g:form url="[controller:'main',action:'login']">
<label for="name">Usuario:</label><br/>
<input type="text" name="name" /><br/>
<label for="pass">Password:</label><br/>
<input type="password" name="password"/><br/>
<input type="submit" value="Login"/><br/>
<g:renderErrors bean="${cmd}"/>
</g:form>
The Controller (MainController.groovy) uses a Command Object, here's the code for both:
def login = { LoginCommand cmd ->
if(cmd.validate()){
redirect(action:'ok')
}else{
render(view:'index',model:[cmd:cmd])
}
}
class LoginCommand {
String name
String password
static constraints = {
name(blank:false,size:5..10)
password(blank:false,size:5..10)
}
}
The problem is that when I enter a bad name or pass (blank or outside the range) it shows me 4 errors, two for the password and two for the username. They are the same, but duplicated.
I found that creating a method "bool validateCommand(){ name && password }" and replacing it for command.validate() does not throw duplicates, but I want to use the constraints features of Grails to keep things DRY.
Any idea why this happens? Thanks so much!

When you inject command objects into controller actions, Grails executes validate() automatically, so there is no need to call it manually. Try
if(!cmd.hasErrors())
instead of
if(cmd.validate())
It seems, that every call to validate() adds new (duplicate) errors to the command object. IMHO this shouldn't happen and probably is a bug in Grails. You should consider reporting this issue.

Related

Dynamically created elements are not validated on the client

I have a table in my web application, which is populated from the model, where properties have attribute for validation:
[Required(ErrorMessage = "Please enter amount!")]
[DisplayFormat(NullDisplayText = "", ApplyFormatInEditMode = true)]
public decimal? Amount { get; set; }
When I press Submit, the field is properly validated on the client, displaying an error message, if the amount is left empty.
Now the user can add new TRs to the table using jquery. The new record completely imitates the existing records, e.g. when I inspect the Amount field for an existing TR:
<input class="form-control" type="text" data-val="true" data-val-number="The field Amount must be a number." data-val-required="Please enter amount!" id="Financials_1__Amount" name="Financials[1].Amount" value="1834.09"><span class="text-danger field-validation-valid" data-valmsg-for="Financials[1].Amount" data-valmsg-replace="true"></span>
Cf. to the same field of the dynamically added TR:
<input class="form-control" type="text" data-val="true" data-val-number="The amount must be a number." data-val-required="Please enter amount!" id="Financials_77e9f261-010a-4c7c-ae50-e3f6587a8c4e__Amount" name="Financials[77e9f261-010a-4c7c-ae50-e3f6587a8c4e].Amount" value="33"><span class="text-danger field-validation-valid" data-valmsg-for="Financials[77e9f261-010a-4c7c-ae50-e3f6587a8c4e].Amount" data-valmsg-replace="true"></span>
The records look very similar. Yet when I leave the required field empty and press submit, the value is not validated on the client, and the execution comes to the controller's action method. There ModelState.IsValid is false though. This is my first issue.
Here is the action method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit(int contractorId, ContractorDetailsDto dto)
{
if (ModelState.IsValid)
{
...
}
return View(_contractorRepository.GetContractorDetailsViewModelByDto(dto));
}
As our ModelState is invalid, the same view is loaded. There the error is displayed in the validation summary, BUT NOT UNDER THE FIELD WITH THE INVALID VALUE. This is the second issue.
I have on my view:
#section Scripts {
<partial name="_ValidationScriptsPartial" />
}
How can I find the cause of my problems?
Answering for your main question. Can you try to execute next javascript code after you add new row
function refreshValidators(formSelector) {
var targetForm = $(formSelector);
targetForm.removeData('validator');
targetForm.removeData('unobtrusiveValidation');
targetForm.removeAttr('novalidate');
$.validator.unobtrusive.parse(targetForm);
}
Regarding your second problem I think you should create separate question for it as it unrelated to your main question. You need to find proper way how to add new rows in ASP.NET Core. For ASP.NET MVC we had next solution how to solve this task http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/ . After googling BeginCollectionItem I found core analog solution, but it is pretty old and I didnt use it personaly.

Aurelia validatejs not working when bind on a object

I was playing around with aurelia-validatejs and was able to validate simple fields on a page. On the same page, I tried initializing a object and bind validation to that object and it stops working:
user_data:IUserData = {
Login:"",
Id : null,
UserProfile: {
UserId: null
},
Permissions: null
};
constructor(){
this.reporter = ValidationEngine.getValidationReporter(this.user_data);
// if I just use this and not user_data object,
// it works and error message is displayed in UI
this.validator = new Validator(this.user_data)
// when I use this.user_data (user_data object), it does validate
// and I can see validation result on console, but it's not shown in UI
.ensure('Login').required().length({minimum: 3, maximum:10});
this.observer = this.reporter.subscribe(result => {
console.info(result);
});
}
Someone mentioned that validatejs looks for a label, and I do have it, it worked when I initialize on this object but as soon as I want to validate on this.user_data property, it just doesn't display in UI, but I can see it on console.
<form action="" submit.delegate="createUser()">
<div class="input-group ${user_data.Login | loginClass}">
<label></label>
<input placeholder="Login" type="text" class="form-control"
keyup.delegate="validate()" value.bind="user_data.Login & validate">
</div>
</form>
this doesn't work, but if I clear user_data like:
user_data:IUserData = {
Login:"",
Id : null,
UserProfile: {
UserId: null
},
Permissions: null
};
constructor(){
this.reporter = ValidationEngine.getValidationReporter(this);
//if I just use this and not user_data object,
//it works and error message is displayed in UI
this.validator = new Validator(this)
.ensure('Login').required().length({minimum: 3, maximum:10});
this.observer = this.reporter.subscribe(result => {
console.info(result);
});
}
and
<form action="" submit.delegate="createUser()">
<div class="input-group ${user_data.Login | loginClass}">
<label></label>
<input placeholder="Login" type="text" class="form-control"
keyup.delegate="validate()" value.bind="Login & validate">
</div>
</form>
This works and is displayed in UI. I searched a little bit on stackoverflow and got few results similar (My problem is similar. I have error messages on UI if I don't use it on a object but when I try to use it on an object, it logs on console but it doesn't display in UI)
I also found this but it wasn't of much help.
I found one more which wasn't of much help either.
I understand there are a lot of articles which might make this question marked duplicate, the reason I'm asking is aurelia is quite new technology and stuffs are changing quite frequently.
Thanks in advanced!
So I dug around a little bit and asked developers in Gitter. As it turns out, it's a bug in validatejs plugin.
Recent changes were made to make something available on all classes, and when that change was done, validatejs looks for validationError Reporter on same model even if we provide different model.
A small hacky workaround is to have
this.__validationReporter__ = ValidationEngine
.getValidationReporter(this.user_data);
Instead of:
this.reporter = ValidationEngine.getValidationReporter(this.user_data);
I have reported this issue and will update once they come up with something.
For now I'm gonna use
this.__validationReporter__ = ValidationEngine
.getValidationReporter(this.user_data);
Or one other workaround is to downgrade to 0.3.x which is not very recommended as there might have been some important changes.
Have you used form-group? Usually our validation problems come down to HTML structure not conforming to the library.
<form action="" submit.delegate="createUser()">
<div class="form-group ${user_data.Login | loginClass}">
<label></label>
<input placeholder="Login" type="text" class="form-control"
keyup.delegate="validate()" value.bind="Login & validate">
</div>
</form>

Internationalized Labels for Form Components in Wicket

How do I correctly create internationalized labels for my form components so that when displaying feedback messages an internationalized field name is displayed instead of the name of the field in the java code?
I've read this:
https://cwiki.apache.org/WICKET/everything-about-wicket-internationalization.html
as well as the documentation for wicket's xhtml tags:
https://cwiki.apache.org/WICKET/wickets-xhtml-tags.html
<label wicket:for="name">
<wicket:label>
<wicket:message key="label.name"/>
</wicket:label>
</label>
<input wicket:id="name" type="text" wicket:message="placeholder:label.name" />
This results in the following error:
Last cause: Expected close tag for '<wicket:label>' Possible attempt to embed
component(s) '<wicket:message key="label.name"/>' in the body of this
component which discards its body
If I replace the wicket:message with some arbitrary text it displays the text in any associated feedback messages.
(There's a related jira issue: https://issues.apache.org/jira/browse/WICKET-3903 however I still do not understand what has been done to fix this and what I must do ...)
Just found out there is a way to do this in java:
add(new TextField<String>("name").setRequired(true).setLabel(new Model<String>(getString("label.name"))));
Is it possible to somehow do this in a more comfortable way?
I just tested the following:
<form wicket:id="form">
<label for="input"><wicket:message key="input">some input</wicket:message></label>
<input wicket:id="input" type="text" name="input">
<input type="submit" value="submit">
</form>
And in the java class:
Form<HomePage> form = new Form<HomePage>("form"
, new CompoundPropertyModel<HomePage>(this));
wmc.add(form);
TextField textField = new TextField("input");
textField.setRequired(true);
form.add(textField);
In the property file I provided:
input=SomeInputField
This led to the following screen (if I leave the requiered field empty and press submit.
Is this what you are looking for?
Here is an alternative approach to #bert's that has always worked for me (wasn't aware of <wicket:label>)
The text shown for a FormComponent when a validation error occurs can be specified by means of FormComponent.setLabel(IModel). The shown text will be the result of the IModel's getObject().
TextField comp = new TextField("comp");
// Use internationalized text from XML resource file
comp.setLabel(new StringResourceModel("formResources.comp.label", this, null));
Notice this has nothing to do with <label> nor FormComponentLabel. FormComponentLabel is a component that can be used to model <label> tags.
You could even subclass FormComponentLabel to provide the label text based on FormComponent.getLabel(), and maybe output an extra mark when the field is required:
public class MyLabel extends SimpleFormComponentLabel{
private boolean required;
public MyLabel (String id, LabeledWebMarkupContainer labelProvider) {
super(id, labelProvider);
if (labelProvider instanceof FormComponent){
required = ((FormComponent)labelProvider).isRequired();
}
}
protected void onComponentTagBody(final MarkupStream markupStream,
final ComponentTag openTag) {
String mark = "";
if (required){
// could be for instance "*"
mark = getString("formResources.requiredField");
}
String text = getModelObjectAsString() + mark;
replaceComponentTagBody(markupStream, openTag, text);
}
}
{
TextField component = new TextField("component");
component.setRequired(true);
component.setOutputMarkupId(true);
IModel labelModel = new StringResourceModel("formResources.component.label",
this, null);
component.setLabel(labelModel);
add(component);
add(new MyLabel("componentLabel", component);
}
<label wicket:id="componentLabel"/>
<input type="text" wicket:id="component"/>
This way you would have clean way of
Setting the FormComponent's text to an internationalized resource string
Reusing exactly the same resource string transparently for the <label> tag and even adding custom marks to it based on FormComponent's properties.
Another alternative is to use the key attribute of <wicket:label/>, like so:
<label wicket:for="name">
<wicket:label key="label.name">Placeholder label</wicket:label>
</label>
<input wicket:id="name" type="text"/>
Unfortunately this attribute is not documented on the wiki page describing wicket's xhtml tags. All attributes supported are documented using JavaDoc in the class handling the tag (org.apache.wicket.markup.html.form.AutoLabelTextResolver).
The advantage of this alternative is that there is no additional coding required.
Wicket throws an exception to tell you that your <wicket:message> tag will be removed because the body of the <wicket:label> tag is replaced. The problem is you cannot nest the <wicket:message> tag inside the <wicket:label> tag (and shouldn't need to).
either this (Option 1):
<label wicket:for="name">
<wicket:label key="label.name"/>
</label>
<input wicket:id="name" type="text />
or this (Option 2):
<label wicket:for="name">
<wicket:message key="label.name"/>
</label>
<input wicket:id="name" type="text />
should work for you and result in HTML something like the following (assuming the properties file contains label.name=Name):
<label for="someMarkupId">
Name
</label>
<input id="someMarkupId" type="text" />
The difference is that if you set the label for the component through the Java code like so:
component.setLabel(new Model("value set in code"));
then using the Option 1 will result in the label being set to "value set in code", while using Option 2 will still result in the label set to "Name". Also if the label is set through Java code, and the key is missing from the properties file the Option 2 will throw an exception, while Option 1 will simply use the value set in the code.
I prefer this:
<label wicket:for="name"><wicket:label />:</label>
<input type="text" wicket:id="name"></input>
Just make sure to set the label in the FormComponent using setLabel, so the only java needed is:
add(new TextField("name", nameModel).setLabel(Model.of("i18n.name")));
This will be rendered as (in Dutch):
<label id="name63-w-lbl" for="name63">Naam:</label>
<input type="text" value="" name="name" id="name63">

play framework: how to repopulate form on validation-failure when using datamodel?

I'm building some crude CMS-like functionality (to get acquinted with Play Framework). For this test-case I've build 2 pages, 1 for listing tags and 1 for creating/editing/saving tags.
The flow is like this (routes-file):
#list tags
GET /tags Application.listTags
#view/edit existing tag
GET /tag/{<(?!new$)(.+)>name} Application.showTag
#new tag
GET /tag/new Application.showTag
the create/view/edit page displays a form which gets it's values from a tagDTO.
The normal flow works without problems, but when the form gives validation-errors (e.g: the tag-name must exist) I want to display the page again, repopulating the form with the edited values.
For this (following the Play Framework conventions) I could use the 'flash'-object which contains these last values, but the form is already bound to the tagDTO (which is null on redirect) instead of the 'flash'-object.
First the code:
Application.java
.....
public static void showTag(String name) {
TagDTO tagDTO = TagDTO.buildDTOFromModelOrNew(name);
render(tagDTO);
}
/**
* Save tag and redirect to Show
*
* #param name
* #param displayname
* #param isnew
*/
public static void saveTag(
#Required(message="Name is required") String name,
String displayname,
boolean isnew)
{
checkAuthenticity();
if(validation.hasErrors()) {
params.flash();
validation.keep();
showTag(null);
}
//fetch tagDTO based on backend or create new if not exist
TagDTO tag = TagDTO.buildDTOFromModelOrNew(name);
// Append / Overwrite values
tag.displayname = displayname;
tag.name = name;
//save result to model
TagDTO.buildAndSaveModelFromDTO(tag);
flash.success("Thanks for " + (isnew?"creating":"updating") + " tag " + tag.name);
//redirect to show
showTag(tag.name);
}
And ShowTag.html
#{extends 'main.html' /}
#{if flash.success}
<p class="success">${flash.success}</p>
#{/if}
#{ifErrors}
<p class="errors">Oops...</p>
#{/ifErrors}
#{form #Application.saveTag()}
#{authenticityToken /}
<p>
<label for="name">Name: </label>
<input type="text" name="name" id="name" value="${tagDTO.name}" />
<span class="error">#{error 'name' /}</span>
</p>
<p>
<label for="displayname">Displayname: </label>
<input type="text" name="displayname" id="displayname" value="${tagDTO.displayname}" />
<span class="error">#{error 'displayname' /}</span>
</p>
<p>
<input type="hidden" name="isnew" value="${tagDTO.isnew}" />
<input type="submit" value="Submit your comment" />
</p>
#{/form}
Now I could think of some ways to make it work, but none really elegant:
bind the form to the flash-object (or params-object) and populate the flas/params- object from the tagDTO
on validation-failure, refetch the tagDTO (not avail anymore so DB-call necessary) and overwrite values in tagDTO with values available in flash-object, bind form to tagDTO.
like 2, but using some sort of cache to quickly fetch tagDTO (so no need for db-call)
Some general mechanism to (de)serialize tagDTO from/to the session.
In short, I don't like any of them really.
What would you consider to be a best practice in this situation? Or is there any functionality in the Play Framework that I'm missing?
This is where the explicit render calls comes handy. Retain the form values from previous submission and give it back (if validation fails) as follows,
checkAuthenticity();
if(validation.hasErrors()) {
render("#showTag", name, displayname, isnew);
}
This will avoid the extra redirect (307 in case of Play!) that would have happened if you had called 'action from another action'.
Render the form again and avoid the redirect is a solution. I think it's OK if a user press F5 he will get the error again. But I think you should create a reload/cancel button, so the user can dismiss all the information.
To have always the correct URL you can do the following in the routes.conf:
GET /tag/create TagController.create
POST /tag/create TagController.insert
The flash solution has the disadvantage that your cookie can get really big.

Grails File Upload Problems

I'm trying to emulate the file upload code from the grails website, and I'm running into some problems. I'm using the same code as found here. Here is my code:
<g:form action="upload" method="post" enctype="multipart/form-data">
<input type="file" name="myFile" />
<input type="submit" value="Upload" />
</g:form>
and
def upload = {
def f = request.getFile('myFile')
if(!f.empty) {
flash.message = 'success'
}
else {
flash.message = 'file cannot be empty'
}
}
I'm receiving the following error at runtime:
Message: No signature of method: org.mortbay.jetty.Request.getFile() is applicable for argument types: (java.lang.String) values: {"myFile"}
Caused by: groovy.lang.MissingMethodException: No signature of method: org.mortbay.jetty.Request.getFile() is applicable for argument types: (java.lang.String) values: {"myFile"}
It appears to be related to some Spring configuration. Spring does not appear to be injecting MultipartHttpServletRequest, so my request doesn't have the appropriate method. I just created this applications using grails create-app. I have not modified the resources.groovy file. I'm using grails 1.0.3.
Any help is much appreciated. The grails website makes this look so easy.
Problem solved!
I was using the example code for uploading files to Grails differently than the original author probably intended. The problem is that when the upload method of the controller was called, it was sometimes for the original render of the Upload page. The request in that method was was not of type MultipartHttpServletRequest. When I did a POST with my file to upload, then Spring did the correct thing and changed my requestion to MultipartHttpServletRequest. So, I needed to do a simple check in my update controller method before using my request like a MultipartHttpServletRequest.
if(request instanceof MultipartHttpServletRequest)
{
MultipartHttpServletRequest mpr = (MultipartHttpServletRequest)request;
CommonsMultipartFile f = (CommonsMultipartFile) mpr.getFile("myFile");
if(!f.empty)
flash.message = 'success'
else
flash.message = 'file cannot be empty'
}
else
flash.message = 'request is not of type MultipartHttpServletRequest'
Now with the Grails 2.x use:
<g:uploadForm name="upload" action="upload" method="POST">
<input type="file" name="file" />
</g:uploadForm>
def upload = {
def file = request.getFile('file')
assert file instanceof CommonsMultipartFile
if(!file.empty){ //do something }
else { //do something }
}
More clean, more simple.
make sure you update the html (your gsp with the form to upload from) to have the enctype as they show:
<g:form action="upload" method="post" enctype="multipart/form-data">
Hope that is helpful, seems too obvious but it's my first thought after seeing your error message.
Someone here seems to be having the same troubles you had. He says he "fixed" it:
Solved. It was my mistake, I was going in action save before submitting the form, so I suppose there was no file.
Not sure how to take what he said, but maybe it'll help you.

Resources