grails - show validation error messages on gsp - grails

I have the following domain class:
class User {
String name
String contactName
String primaryEmail
String url
String phoneNumber
String address
static hasMany = [users: User]
static constraints = {
name blank: false
contactName blank: false
primaryEmail email: true
url blank: false
phoneNumber blank: false
address blank: false
}
}
And controller for the User:
class UserController {
def create() {
User user = new User()
[user: user]
}
def save(User user) {
if (!user.save(flush: true)) {
render (view : 'create', model: [user: user])
}
redirect action: 'create'
}
}
I want show validation errors in case if validation fails. My create.gsp looks like this:
<body>
<g:form action="save" >
<g:renderErrors bean="${user}"/>
<g:textField name="user.name" id="message" value="${user.name}"/>
<g:textField name="user.contactName" id="contactName" value="${user.contactName}"/>
<g:textField name="user.primaryEmail" id="primaryEmail" value="${user.primaryEmail}"/>
<g:textField name="user.url" id="url" value="${user.url}"/>
<g:textField name="user.phoneNumber" id="phoneNumber" value="${user.phoneNumber}"/>
<g:textField name="user.address" id="address" value="${user.address}"/>
<g:submitButton name="submit" value="Save"/>
</g:form>
</body>
</html>
But after sumbit of create.gsp with invalid data two strange thing happen
1) Despite the fact that all fields have value property mapped to some field of User bean all fields are empty
2) There are no validation errors on the page
What I'm doing wrong?
Thank you!

you must return after calling render() or use else
def save(User user) {
if (!user.save(flush: true)) {
render (view : 'create', model: [user: user])
return // either return here
}else // or else here
redirect action: 'create'
}
In your original code you redirect to create and pass no models into it

Related

ASP MVC - Form input types and model binding issues

I am playing around with server-side form validation, model binding and ModelState and I’ve noticed the following:
If I have an input of type “email” and I insert an invalid email
address (such as ‘hello’), model binding doesn’t work – the value
shows up as ‘null’.
However, if I insert a valid email address,
everything works out.
Can someone explain to me why this happens?
Thanks in advance!
Form:
<form name="contactForm" id="contactF" ng-controller="Contacts" ng-submit="contactForm.$invalid ? sendMessage() : return;" novalidate>
<input type="email" name="email" ng-model="model.EmailAddress" required placeholder="Email"/>
<div ng-messages="contactForm.email.$error" ng-if="contactForm.email.$touched || contactForm.$submitted">
<div ng-messages-include="/Helpers/error-messages.html"></div>
</div>
<textarea name="message" ng-model="model.message" required placeholder="Escreva a sua mensagem aqui."></textarea>
<div ng-messages="contactForm.message.$error" ng-if="contactForm.message.$touched || contactForm.$submitted">
<div ng-messages-include="/Helpers/error-messages.html"></div>
</div>
<br />
<input type="submit" value="Enviar"/>
</form>
Model:
public class HomeContactVM
{
[Required(ErrorMessage = "Error message here")]
public string Name { get; set; }
[Required(ErrorMessage = "Error msg here.")]
[EmailAddress(ErrorMessage = "Please use a valid email address.")]
public string EmailAddress { get; set; }
[Required(ErrorMessage = "Error msg here.")]
public string Message { get; set; }
}
Action method:
[HttpPost]
public JsonResult Contact(HomeContactVM model)
{
string message;
if (ModelState.IsValid)
{
using (SmtpClient client = new SmtpClient())
{
MailMessage msg = new MailMessage()
{
Body = model.Message,
BodyEncoding = Encoding.UTF8,
Subject = "New message from " + model.EmailAddress,
From = new MailAddress(model.EmailAddress),
Sender = new MailAddress("xxx#gmail.com", "xx")
};
int retries = 5;
bool retry = true;
while (retry)
{
try
{
client.Send(msg);
message = "Your message was sent.";
return Json(message, JsonRequestBehavior.AllowGet);
}
catch (Exception)
{
if (retries > 0) {
retries--;
}
else {
retry = false;
message = "Something went wrong. Please try again later.";
return Json(message, JsonRequestBehavior.AllowGet);
}
}
}
}
}
message = "Your model is not valid.";
return Json( message, JsonRequestBehavior.AllowGet);
}
So, if anyone is facing the same problem, here's what I found: the problem is not related to ASP, it's due to Angular's default behaviour.
If an input fails validation, Angular sets its value to undefined and it doesn't pass it to the model, hence it showing up as null. A possible workaround is to set ng-model-options="{allowInvalid: true}"
You can find more about this behaviour here: Angular.js - ngModel value is undefined when ng-pattern is set in directive and Get "raw" value from invalid input field
Hope this helps!

Reading ungenerated properties from User-class springSecurityService in a Grails GSP

I'm writing an update user-data form in Grails. It must show an old user data in the fields before update. This is a part of this form:
<g:form class="form-signin" controller="VisitorSpace">
<label for="login" class="sr-only">Login</label>
<g:textField id="login" class="form-control" name="login" placeholder="Login"
value="${applicationContext.springSecurityService.principal.username}" required="" autofocus=""/>
<label for="firstName" class="sr-only">Your name</label>
<g:textField id="firstName" class="form-control" name="firstName" placeholder="Your name"
value="${applicationContext.springSecurityService.principal.firstName}" required="" type="text"/>
...
</g:form>
This is a part of my domain User-class:
class Person {
transient springSecurityService
String username
String password
boolean enabled = true
boolean accountExpired
boolean accountLocked
boolean passwordExpired
String firstName
String lastName
String email
boolean isAdminCafee = false
static transients = ['springSecurityService']
static constraints = {
username blank: false, unique: true
firstName blank: false
lastName blank: false
password blank: false
email blank: false, unique: true
}
...
}
Generated username-property of the domain class is getting correctly, but while I trying request ungenerated firstName-property I get an error:
URI:/restorator/visitorSpace/editPrivateDataClass:groovy.lang.MissingPropertyExceptionMessage:
No such property: firstName for class: grails.plugin.springsecurity.userdetails.GrailsUser
Solved. I've solved this problem via rendering gsp with parameters.
Render-action of gsp-controller:
def editPrivateData(){
def user = springSecurityService.currentUser
render (view:'editPrivateData.gsp', model: [user: user])
}
Using in gsp-form:
<g:textField id="firstName" class="form-control" name="firstName" placeholder="Your name" var = "user" value="${user.firstName}" required="" type="text"/>

associating http request parameters from a form with a collection of objects

I have an application that has Question and Answers (see domains listed below). Basically, a question has many answers. Each answer has an ordinal that determines the order which the answers will be displayed. I want the form to allow the user to change the ordinal. My problem is, I'm not sure how I can associate the input fields from the html form, with the correct answer on the server side. When I submit this form
<g:if test="${questionInstance.answers}">
<div class="control-group">
<label class="control-label">Order</label>
<div class="controls">
<g:each status="i" in="${questionInstance.answers}" var="answer">
<div><input type="text" name="answer[${i}].id" style="width:15px;" value="${answer?.ordinal}" />
${answer.body}
</div>
</g:each>
</div>
</div>
</g:if>
I get these parameters on my update action question controller (controller is shown at bottom). I want to update the ordinal for each answer but not sure how
question object
question domain
class Question {
DateTime dateCreated
DateTime lastUpdated
String body
Answer correctAnswer
Integer ordinal
static belongsTo = [lesson: Lesson]
static hasMany = [answers: Answer]
static constraints = {
body blank: false
correctAnswer nullable: true,
validator: { Answer val, Question obj ->
// Correct answer must have this as it's question
val ? val.question == obj : true // TODO: Give this a proper error message
}
ordinal unique: 'lesson'
}
static mapping = {
lesson lazy: true
answers sort: 'ordinal'
answers cascade: 'all-delete-orphan'
}
}
answer domain
class Answer {
DateTime dateCreated
DateTime lastUpdated
String body
Integer ordinal
String reason
static belongsTo = [question: Question]
static constraints = {
body blank: false
ordinal unique: 'question'
}
static mapping = {
question lazy: true
}
String toString() {
"Answer: $body"
}
Integer getNextOrdinal() {
Integer ordinal = Answer.createCriteria().get {
projections {
max "ordinal"
}
}
ordinal = ordinal ?: 1
return ordinal
}
}
question controller
def update(Long id, Long version) {
def questionInstance = Question.get(id)
if (!questionInstance) {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'question.label', default: 'Question'), id])
redirect(action: "lessonQuestionDashBoard", controller: "lesson")
return
}
if (version != null) {
if (questionInstance.version > version) {
questionInstance.errors.rejectValue("version", "default.optimistic.locking.failure",
[message(code: 'question.label', default: 'Question')] as Object[],
"Another user has updated this Question while you were editing")
render(view: "edit", model: [questionInstance: questionInstance])
return
}
}
questionInstance.properties = params
if (!questionInstance.save(flush: true)) {
render(view: "edit", model: [questionInstance: questionInstance])
return
}
flash.message = "Question: '${questionInstance.body}' has been updated"
flash.messageType = "success"
redirect(action: "show", id: questionInstance.id)
}
as described in the docs about data binding (as Martin already mentioned), you have to edit the form elements like following:
<g:if test="${questionInstance.answers}">
<g:each status="i" in="${questionInstance.answers}" var="answer">
<input type="text" name="answer[${i}].ordinal" value="${answer?.ordinal}" />
<input type="hidden" name="answer[${i}].id" value="${answer?.id}" />
</g:each>
</g:if>
This is because your hasMany Relationship from Question to Answer is from type Set. Whit this construct params looks like this:
params[answer][0] = [id: 123, ordinal: 4]
and so on. This way data-binding will work as you expect it to.

Get error when using findBy() in grails

I have a GSP page:
<g:if test="freelancer">
<g:each in="${ freelancer }" var="freelance">
${ freelance.firstName }
</g:each>
</g:if>
and an action:
def homepage() {
println(params.id)
def user = User.find{
username == params.id
}
if(user){
println(user.username + "!")
return[user:user]
}
}
and a welcome page:
<body>
Welcome ${ user.username }
</body>
in the first GSP page I get firstName link, and when I click on it to view his homepage I get an error:
Cannot get property 'username' on null object
But in the Console of my IDE, println(params.id) prints a username, BUT with square brackets. What is wrong in my code?
In your "homepage" action try
def user = User.find{ it.username == params.id } instead of
def user = User.find{ username == params.id }
Yes, you will have to use brackets in your tag. That's number one.
<g:if test="${freelancer}">
</g:if>
Otherwise, the groovy truth will always evaluate a non-empty string as true, thus your test will be flawed.
Also, I would use the g:link instead of html 'a' tag.
<g:each in="${ freelancer }" var="freelance">
<g:link controller='user', action='homepage', id="${freelance.user.username}">
${freelance.firstName}
</g:link>
</g:each>
As a suggestion, think of using User.findByUsername() instead of find closure.
Let us know if that works. ;)
This is my User domain class:
class User {
String username
String passwordHash
byte[] passwordSalt
Profile profile
static hasMany = [ roles: Role, permissions: String ]
static constraints = {
profile(nullable:false)
username(nullable: false, blank: false, unique: true, size:3..15)
passwordHash(nullable:false, blank:false, minSize:6)
}
static mapping = {
profile lazy:false
}
In the g:if tag I put freelancer to the brackets and changed to User.findByUsername(). But there is the same error. I don't want params.id as list.

Grails form error handling: Binding 1:1 object mappings

I have a registration form that contains fields related to two domain objects; User and Profile. The relationship is a 1:1 mapping owned by the User domain class.
A 'register' action on the User controller marshals the form values, and provided there are no validation errors, persists the user object and redirects to the applications root when the form is submitted. Otherwise, the controller will redirect back to the registration form showing pre-populated fields with failed values.
However, in practice, when validation fails, the failed values aren't displayed in the view. Below is the code for the register action:
def registration = {
}
def register = {
def user = new User()
bindData(user, params)
if (user.save()) {
flash.message = 'Successfully Registered User'
redirect(uri: '/')
}else {
flash.message = 'Registration Failed!'
redirect(action: registration, params: [ user: user ])
}
}
Below is an example html excerpt from the view showing User and Profile related fields:
<div class="row">
<label for="city"> City, State: </label>
<g:textField id="city" name="profile.city"
value="${user?.profile?.city}" size="28" />
<span class="red">*</span>
</div>
<hr />
<div class="row">
<label for="email"> E-mail address: </label>
<g:textField id="email" name="userId" value="${user?.userId}" size="28" />
<span class="red">*</span>
</div>
Syntactically, everthing looks okay; I'm using appropriate naming conventions and grail's interpolation for acessing values, so I'm at wits end as to why this isn't behaving as expected.
Any comments or suggestions would be appreciated.
Thanks,
-Tom
If i remember correctly i thought it was something in the lines of:
def user = new User()
user.properties = params
You need to somehow pass the submitted values from user in register action to user in registration action. Like this:
if (params.user) {
user.properties = params.user.properties
}
Try explicitly calling the erorr?
Ive been using this pattern to redirect back to the same form.
if (user.save()) {
...
} else {
return error()
}
I normally use command objects in webflows, so my normal pattern looks like:
def registerFlow = {
registerPage = {
on("submit") { FormDataCommand cmd ->
cmd.validate()
if (cmd.hasErrors()) {
flow.cmd = cmd
return error()
} else {
...
}
}
}
}
class FormDataCommand implements Serializable {
User u
Profile p
static constraints = {
u(validator: { it.validate() })
p(validator: { it.validate() })
}
}

Resources