I am using grails web flow for multiform registration process in my project. I created the command object which implements Serializable.
class CustomerCommand implements Serializable{
String Name
Integer Age
Date DateOfBirth
String FatherOrHusbandName
String IdProof
String IdProofNumber
static constraints = {
}
}
My flow section
def customerRegisterFlow = {
enter {
action {
Customer flow.customer = new Customer()
[customer: flow.customer]
}
on("success").to("AddCustomer1")
}
AddCustomer1 {
on("next") { CustomerCommand cuscmd ->
if(cuscmd.hasErrors()) {
flash.message = "Validation error"
flow.cuscmd = cuscmd
return error()
}
bindData(flow.customer, cuscmd)
[customer: flow.customer]
}.to("AddCustomer2")
}
}
Now I am facing two problems.
1) When I click next button, the hasErrors() function is not properly validating the form input values. It simply redirects to AddCustomer2 page. It accepts blank values also.
2) I am not able to access the flow scope object in view page(GSP). This is required when I click back button from AddCustomer2, it should show the page with values which are already entered by the user from flow scope
<input type="text" class="input" name="Name" value="${customer?.Name}"/>
This is my input field in AddCustomer1. Kindly help me anyone to fix this issue which you might have faced already. Thanks in advance
you should call cuscmd.validate() before checking if the method cuscmd.hasErrors()
CustomerCommand class should have annotation #Validateable:
#grails.validation.Validateable
class CustomerCommand implements Serializable{
I think lukelazarovic already answered your first question. To answer your second question: Have to add the commandobj to the flow when the back button is clicked like this:
AddCustomer2 {
on("next") { CustomerCommand cuscmd ->
if(cuscmd.hasErrors()) {
flash.message = "Validation error"
flow.cuscmd = cuscmd
return error()
}
bindData(flow.customer, cuscmd)
[customer: flow.customer]
}.to("finish")
on("back"){CustomerCommand customer->
flow.customer= customer
}.to "AddCustomer1"
}
UPDATE
Try to be consistent in you naming of the command objects too to reduce confusion
For example above you are using flow.cuscmd and flow.customer. This will cause problems for you when you are rendering errors in your view e.g
<g:if test="${customer?.hasErrors()}">
<g:renderErrors bean="${customer}" as="list" />
</g:if>
In your case errors won't be rendered because you have named the object flow.cuscmd
Related
My domain object booking has multiple attributes that are allowed to be null, because they will be set later after the object has been saved to the db.
Part of myService.action():
booking.properties = params
if (booking.contactFirstname?.length() <= 1) { booking.errors.rejectValue("contactFirstname", "empty") }
if (booking.contactLastname?.length() <= 1) { booking.errors.rejectValue("contactLastname", "empty") }
if (booking.contactPhone?.length() <= 1) { booking.errors.rejectValue("contactPhone", "empty") }
if (booking.contactMobile?.length() <= 1) { booking.errors.rejectValue("contactMobile", "empty") }
if (booking.contactEmail?.length() <= 1) { booking.errors.rejectValue("contactEmail", "empty") }
if (booking.hasErrors() || ! booking.validate()) {
return [success: false, model: booking]
} else {
booking.save(failOnError: true)
return [success: true, model: booking]
}
My controller does:
def result = myService.action(params)
if (result.success) {
flash.success = message(code: "msg.successfullySaved")
redirect(action: "registerEventConfirmation", id: result.model.uid, params: [lang: params.lang], mapping: "paginated")
} else {
flash.error = message(code: "msg.errorSavingCheckFields")
render(view: "registerEventStep3", params: [lang: params.lang], model: [booking: result.model])
I'm using
hasErrors(bean: booking,field:'contactFirstname', 'has-error')}
to mark error fields.
If I now submit the form without any values in textfields, all fields are red, booking.errors has >0 errors.
If I submit the form after with a firstname, booking.errors is NULL and no other field is marked.
Is this a Bug? I'm with Grails 2.3.6
additional information
I visit the form, submit it empty completely
I see all form fields in red, object.errors has >0 errors (VALID)
I enter a value in the first field, firstname and submit
I see none of the form fields in red, object.errors =0 errors (INVALID)
I re-submit the form with none changes
I see all empty form fields in red, object.errors has >0 errors (VALID)
Now that I fully understand the situation and since I was having trouble sleeping I thought I give you a very concise answer so that you can hopefully make full sense and use things properly.
Firstly I know creating a validation bean sounds like it will be a lot of work so let me teach you how to do it all relatively simply and why it is my preferred method.
It is my preferred method simply because when you do
class MyController {
def myAction(Mybean bean) {
// 1. the object allowed into this save action
// are all that is available objects withing MyBean.
// If it has user define but not telephone. Then
// if telephone is passed to myAction it will fail and not recognise
// field
// When declaring Date someField or User user then the params now
// received as bean this way is now actually properly bound
// to the data / domainType declared.
// Meaning user will now be actual user or someField actually Date
}
So now to explain how to best solve this issue. When creating beans simply copy over the actual domain class from your domain folder into src/groovy/same/package in grails 2 or src/main/groovy/same/package in grails 3
Change name / class or copy as from Booking to BookingBean so it has a different name.
Add #Validateable above actual BookingBean in grails 2 or add implements to main class like Class BookingBean implements Validateable { in grails 3
Now since it is copied all the objects are identical and at this point a save from the controller would be
class MyController {
def myAction(BookingBean bean) {
Booking booking = new Booking()
// this will save all properties
booking.properties = bean
booking.save()
}
}
But you have a special circumstance and you wanted to declare a transient field in the main domain class what I would do instead is
class BookingBean {
def id
String contactFirstname
String contactLastname
boolean secondSave=false
static constraints = {
id(nullable: true, bindable: true)
contactFirstname(nullable:true) //,validator:checkHasValue)
contactLastname(nullable:true) //,validator:checkHasValue)
secondSave(nullable:true,validator:checkHasValue))
}
//use the same validator since it is doing identical check
static checkHasValue={value,obj,errors->
// So if secondSave has a value but contactFirstName
// is null then complain about contactFirstName
// you can see how secondSave gets initialise below
//typical set this to true when you are about to save on 2nd attempt
//then when set run validate() which will hit this block below
// Check all the things you think should have a
// value and reject each field that don't
if (val) {
if ( !obj.contactFirstname) {
errors.rejectValue('contactFirstname',"invalid.contactFirstname")
}
if ( !obj.contactSecondname) {
errors.rejectValue('contactSecondname',"invalid.contactSecondname")
}
//and so on
}
}
So now in your controller:
class MyController {
def save1(BookingBean bean) {
Booking booking = new Booking()
// this will save all properties
booking.whatEver = bean.whatEver
booking.save()
// you can choose to validate or not here
// since at this point the secondSave has
// not been set therefore validation not called as yet in the bean
}
//you probably have id and it should bind with actual domain class
def save2(BookingBean bean) {
booking.secondSave=true
if (!bean.validate()) {
//this is your errors
//bean.errors.allErrors
return
}
//otherwise out of that loop since it hasn't returned
//manually set each object
booking.contactFirstname=bean.contactFirstName
booking.contactSecondname=bean.contactSecondname
booking.save()
}
}
e2a side note - above should answer
well don't validate it until you have created it. Only validate it after you created the object then added a value. Alternative create a function possibly in a validation bean that you run as part of your 2nd check. This Example bean is not validated until formatRequest is called as seen here
I don't grasp the specifics of your question, so I will give some general guidance since I have just dug into this.
Don't call hasErrors() before validate(). If you do, Grails won't hand you errors from domain constraints and you will only end up with the errors you set yourself using rejectValue().
Be careful with using rejectValue(). Try to set all your errors using domain constraints. If you have sophisticated constraints use the validator syntax and obj.getPersistentValue() might be your friend once in a while.
If you still have to use rejectValue(), understand that any later calls to validate() will start from scratch and erase your prior errors. I have written a workaround for this (to be placed in your domain object) although I can't assure you it is 100% ok:
def validateWithErrors(def fields = null) {
def existingErrors = this.errors
def ret = (fields ? this.validate(fields) : this.validate())
existingErrors?.allErrors?.each { error ->
this.errors.rejectValue(error.field, error.code)
}
return (existingErrors?.allErrors ? false : ret)
}
I am building a service which requires a somewhat lengthy setup process. I have it broken into 4 models and 4 corresponding views. They are Setup, Setup2, Setup3, and Setup4. Each of these views gathers information from the user which is stored in a User object. I have been passing the user along like this:
[HttpPost]
public ActionResult Setup(FormCollection values)
{
User registeringUser = new User();
registeringUser.email = User.Identity.Name;
registeringUser.fName = values["fName"];
registeringUser.lName = values["lName"];
registeringUser.phone = values["phone"];
return RedirectToAction("/Setup2", registeringUser);
}
For some reason, this seems to work just fine for the first jump (from Setup to Setup2) but after that I'm getting weird behavior, such as User. getting set to null when the User is passed to another View.
In a related, but slightly different issue, I need the last screen (Setup4) to be recursive. This screen adds a course in which the user is enrolled, and if they don't check the "This was my last class" button, it needs to basically clear the form so they can enter another course.
The entire Controller looks like this:
[HttpPost]
public ActionResult Setup4(FormCollection values, User registeringUser)
{
// values["allClassesAdded"] returns "false" as a string if box is unchecked, returns "true,false" if checked.
// Solution: parse string for "true"
if (utils.parseForTrue(values["allClassesAdded"]))
{
// TODO Redirect to "congratulations you're done" page.
database.CreateUserInDB(registeringUser);
return Redirect("/Home");
}
else
{
// Build course and add it to the list in the User
course c = new course(values);
if (Request.IsAuthenticated)
{
//registeringUser.currentCourses.Add(c);
registeringUser.AddCourse(c);
return RedirectToAction("/Setup4", registeringUser); // <---- This doesn't really work right
//return View();
}
else
{
return Redirect("/Account/Login");
}
}
}
This is my first project with MVC, so if you find that I'm doing the entire thing completely incorrectly, feel free to not answer the question I asked and offer the proper solution to this need. I'm moving an existing (pure) C# project to MVC and I'm mainly just stuck on how to work within MVC's interesting structure. I'm very grateful for any help you can give!
Thanks!
You can store user related data in session without passing it between requests
Smth like this
[HttpPost]
public ActionResult Step1(Step1Model model)
{
Session["UserRegistration"] = new UserRegistration
{
FirstName = model.fName,
....
}
....
}
[HttpPost]
public ActionResult Step2(Step2Model model)
{
var userRegistration = Session["UserRegistration"] as UserRegistration;
if (userRegistration == null) { return Redirrect("Step1"); }
userRegistration.SomeField = model.someField;
...
Session["UserRegistration"] = userRegistration;
....
}
I want to make a drop down using the data given in the BootStrap.groovy.
My City domain class
package city.model
class City {
String cityName
static constraints = {
cityName(maxSize: 50) }
}
In my Service class
public class CityService {
def citySelect(String cityName) //this is just a sample
{
City city = new City()
city.cityName = city.findByCityName(cityName)
}
}
controller
public class CityController {
def cityService
def
def selCity() {
def selectCity = cityService.citySelect(params.cityName){
if(selectCity != null){
render view // my view
}
else{
render view // error select again view
}
}
}
BootStrap.groovy
import city.City;
class BootStrap {
def init = { servletContext ->
for(String cityName in ['Addis Ababa', 'Semera','Asosa','Gondar', 'Jijiga','Harar', 'Dire Dawa', 'Bahir Dar',
'Hawassa', 'Arba Minch', 'Adama', 'Mekelle']) {
City.findOrSaveByCityName(cityName).save()
}
}
def destroy = {
}
}
i used this
<g:select name="cities" from="${City.list()}" optionKey="id" optionValue="cityName"/>
in my view but showing error cannot envoke method list on null object
What is wrong within mu code and what should i do to make it work. please any suggestions
<g:select name="city" from="${city.model.City.list()}" optionValue="${cityName}"
noSelection="['':'-Please select the City-']" optionKey="id"/>
this could work fine.
Maybe you have misunderstood the point of Bootstrap.
Bootstrap as per the naming convention is the part that is triggered when your site is booting up.
You would typically use it to ensure required db table records are generated before it has booted up i.e. admin account or in your case generation of some cities.
You would not be using Bootstrap to interact with the records you generated in the way of editing or selecting.
Once this has all be done and saved - you would have also used the Controllers/Views to list/view/update/add cities.
You would create your g:select tags in these views and matching controllers that would query the records you have saved via bootstrap
E2A:
Ok Just read your comment
Either use an import on the top of your gsp
<%# page import="city.City" %>
or call full packaged path to City domainClass city.City.list
<g:select name="cities" from="${city.City.list()}" optionKey="id" optionValue="cityName"/>
To create a list of the Cities you inserted into the Database in the Bootstrap.groovy (in a view i.e [viewName].gsp)
Mark up is like so
<g:select name="city" from="${city.model.City.list()}" value="${city.name}"
noSelection="['':'-Please select the City-']" optionKey="id"/>
However, the Bootstrap should be for initialization of Database and Application defaults, Also to perform start (Startup in the init closure while shutdown in the destroy Closure).
This should work:
<g:select name="cities" from="${city.model.City.list()}"
optionKey="id" optionValue="cityName"/>
However, executing queries like city.model.City.list() in a GSP is not a recommended practice. Instead you should retrieve your data (the list of cities) in a controller action or a service and pass that via the model to the GSP.
I have a User class which has a List field namely pt. This field is not initialized when User register his account. But when user goes this controller action :
def updatePt() {
//performs some action
def user = User.get(springSecurityService.principal.id) //find the user
user.pt = []
//on certain conditions i put values into user.pt like this
user.pt << "E"
//at last I save it
user.save()
}
But using user/show action via scaffolding I found that pt field is not saved on users object. Where I'm making a mistake?
You have to provide a static mapping in the Users domain class so that Grails knows the field must be persisted:
class User {
static hasMany = [pt: String]
}
It's possible because of validation error. Try with
if (!user.save()) {
log.error('User not saved')
user.errors.each {
log.error('User error: $it')
}
}
PS or you can use println instead of log.error
I have an optiontransferselect in a form but i dont know how to get the selected items in the rightlist back in my action.
I need to get a list with all the visited countries' ids. i tried in my action List (Integer) countriesVisitedId; but it returns nullPointerException. then i tried Integer id but it returns null.
this is what i have:
s:optiontransferselect
label="Select visited countries"
name="countriesNotVisitedId"
leftTitle="Not visited countries"
rightTitle="Visited Countries"
list="%{countriesNotVisited}"
listKey="id"
listValue="name"
headerKey="countryNotVisitedId"
headerValue="--- Please Select ---"
doubleName="countriesVisitedId"
doubleList="%{countriesVisited}"
doubleHeaderKey="countryVisitedId"
doubleHeaderValue="--- Please Select ---"
doubleListKey="id"
doubleListValue="name" />
how can I get the list with the Integers ids of the visited countries in my action?
I was banging my head on the wall wondering what I was doing wrong. It is pretty simple
doubleName="fields" is the tag field that is returned
public void setFields(String fields) { this is what needs to be in your action class.
The thing that I didn't realise is the elements need to be selected in order to be sent back. Or simple use ajax with in your header
Here's what I tried, it works fine.
Step 1: JSP to select the country from the left hand side into right hand.
<s:optiontransferselect
label="Favourite Characters"
name="leftSide"
id="left"
leftTitle="Left Title"
rightTitle="Right Title"
list="%{countriesNotVisited)"
multiple="true"
headerKey="headerKey"
doubleList="{}"
doubleId="right"
doubleName="rightSide"
doubleHeaderKey="doubleHeaderKey"
doubleMultiple="true" />
Step 2: Javascript code to auto select all data from the right hand side.
function selectall()
{
var list = document.getElementById("right");
for (var i = 0; i < list.options.length; i++)
{
alert(list.options[i].value)
list.options[i].selected = true;
}
var form = document.getElementById("right");
form.submit();
return true;
}
Step 3: call this function on submit, from the JSP side.
<s:submit id="submitid" value="Submit" action="insert" onclick="selectall()"/>
Step 4: In the action, make the getters and setters of object names of the left and right sides take strings and not string arrays.
private String leftSide;
private String rightSide;
public String getLeftSide() {
return leftSide;
}
public String getRightSide() {
return rightSide;
}
public void setRightSide(String rightSide) {
this.rightSide = rightSide;
}
public void setLeftSide(String leftSide) {
this.leftSide = leftSide;
}
Now if you try to print a value in the action, you will get values:
System.out.println("right side list " + ad.getRightSide());
In your action:
public void setCountriesVisitedId(String[] countriesVisitedId) {
this.countriesVisitedId = countriesVisitedId;
}