I have a User domain with a flag for security officer and a Location domain to hold all locations in my web app. I want to allow an administrator to assign a primary and secondary security officer to any location from a pick list of all users with the security officer flag checked. I have this working in a hand-made MVC structure, but I'm currently playing with scaffolding and have run into a strange error I can't figure out. My Location domain is coded as
class Location {
static hasMany = [users:User]
Integer id
...
String address
String city
State state
String zip
...
User primarySecurityOfficer
User secondarySecurityOfficer
static mapping = {
table 'location'
id generator: 'identity'
sort "state"
version false
}
static constraints = {
...
address(blank: false, nullable: false)
city(blank: false, nullable: false)
state(blank: false, nullable: false)
zip(blank: false, nullable: false, size: 5..5)
...
primarySecurityOfficer(blank: true, nullable: true)
secondarySecurityOfficer(blank: true, nullable: true)
}
}
and I generated the views with grails generate-all Location. I modified the security officer selection in the generated _form.gsp to
<div class="fieldcontain ${hasErrors(bean: locationInstance, field: 'primarySecurityOfficer', 'error')}">
<label for="primarySecurityOfficer">
<g:message code="location.primarySecurityOfficer.label" default="Primary Security Officer" />
</label>
<g:select id="primarySecurityOfficer" name="primarySecurityOfficer" value="${locationInstance?.primarySecurityOfficer?.employeeNumber}" from="${securityOfficers}" optionKey="employeeNumber" optionValue="${{it.firstName + ' ' + it.lastName}}" noSelection="${['null':' ']}" disabled="${disabled}"/>
</div>
<div class="fieldcontain ${hasErrors(bean: locationInstance, field: 'secondarySecurityOfficer', 'error')}">
<label for="secondarySecurityOfficer">
<g:message code="location.secondarySecurityOfficer.label" default="Secondary Security Officer" />
</label>
<g:select id="secondarySecurityOfficer" name="secondarySecurityOfficer" value="${locationInstance?.secondarySecurityOfficer?.employeeNumber}" from="${securityOfficers}" optionKey="employeeNumber" optionValue="${{it.firstName + ' ' + it.lastName}}" noSelection="${['null':' ']}" disabled="${disabled}"/>
</div>
and the controller's create and save auto-generated actions (modified to send the view the security officer list) are
def create() {
def securityOfficers = User.findAll("from User as u where u.securityOfficer='1'")
println params
respond new Location(params), model: [ securityOfficers:securityOfficers ]
}
#Transactional
def save(Location locationInstance) {
def securityOfficers = User.findAll("from User as u where u.securityOfficer='1'")
if (locationInstance == null) {
notFound()
return
}
if (locationInstance.hasErrors()) {
respond locationInstance.errors, view:'create', model: [ securityOfficers: securityOfficers ]
return
}
locationInstance.save flush:true
request.withFormat {
form {
flash.message = message(code: 'default.created.message', args: [message(code: 'locationInstance.label', default: 'Location'), locationInstance.id])
redirect locationInstance
}
'*' { respond locationInstance, [status: CREATED], model: [ securityOfficers:securityOfficers ] }
}
}
The error I'm getting is that if I select any security officers and try to save a new location, I get the error Could not find matching constructor for: User(java.lang.String). If I leave the selector blank, the location will save successfully. Any ideas why?
Edit 1: Interesting find from tinkering around. I can successfully save security officers to locations if I change the view code to
<div class="fieldcontain ${hasErrors(bean: locationInstance, field: 'primarySecurityOfficer', 'error')}">
<label for="primarySecurityOfficer">
<g:message code="location.primarySecurityOfficer.label" default="Primary Security Officer" />
</label>
<g:select id="primarySecurityOfficer.employeeNumber" name="primarySecurityOfficer.employeeNumber" value="${locationInstance?.primarySecurityOfficer?.employeeNumber}" from="${securityOfficers}" optionKey="employeeNumber" optionValue="${{it.firstName + ' ' + it.lastName}}" noSelection="${['null':' ']}" disabled="${disabled}"/>
</div>
<div class="fieldcontain ${hasErrors(bean: locationInstance, field: 'secondarySecurityOfficer', 'error')}">
<label for="secondarySecurityOfficer">
<g:message code="location.secondarySecurityOfficer.label" default="Secondary Security Officer" />
</label>
<g:select id="secondarySecurityOfficer.employeeNumber" name="secondarySecurityOfficer.employeeNumber" value="${locationInstance?.secondarySecurityOfficer?.employeeNumber}" from="${securityOfficers}" optionKey="employeeNumber" optionValue="${{it.firstName + ' ' + it.lastName}}" noSelection="${['null':' ']}" disabled="${disabled}"/>
</div>
But now I cannot save WITHOUT a security officer. The error I get if I attempt to is Unparseable number: "null".
Related
I have gsp with two textfield for firstname-lastname and reCaptcha. What I want is for every wrong captcha code the user's input for firstname and last name won't be erased.
snippet for controller:
***captcha_code****
if (result) {
def person = new Person(params)
person.save()
render "Success!"
} else {
flash.message = message(code: 'forgotPassword.captcha.wrong')
redirect(controller:'person', action:'form')
}
snipper for form.gsp
***captcha_code_here***
<g:form controller="person" action="save">
<label>First Name: </label>
<g:textField name="firstName"/><br/>
<label>Last Name: </label>
<g:textField name="lastName"/><br/>
<g:if test="${flash.message}">
<div class="message" role="status" style="font-size: medium;color: green;">${flash.message}</div>
</g:if>
***captcha_code_here***
<g:actionSubmit value="Save"/>
To repopulate the fields you can use the same flash scope you're using for the message. On error, add the first and last name to the flash scope, and then in your GSP use those values when they are available:
PersonController
class PersonController {
def save() {
...
if(/* recaptcha failed */) {
flash.firstName = params.firstName
flash.lastName = params.lastName
}
...
}
}
GSP
<label>First Name: </label>
<g:textField name="firstName" value="${flash.firstName ?: ''}"/><br/>
<label>Last Name: </label>
<g:textField name="lastName" value="${flash.lastName ?: ''}"/><br/>
In Controller Action, send back fields that you want to be repopulated.
I am relatively new to grails and have been trying to configuring a check box to set a boolean value to true when it is checked for a number of list items.
The relationship is a hasMany between two domains where the boolean canEdit is set in the work domain. The other domain is called role.
I have set up the relationship so that work hasManyRoles and I would like my domain to reflect the boolean canEdit being set to true for some of these roles based on what gets checked in the checkbox list.
I have managed to get the gsp working displaying the list with checkboxes beside each one but am unsure as to how to save the boolean as true once some of these roles are checked as currently they are all getting set to true regardless of which is checked.
Any assistance would be greatly appreciated. I will post my code below:
class Work {
static hasMany = [canEditRole: Role]
String name = "Default"
Boolean canEdit = false
static constraints = {
name blank: false, unique: true
canEdit nullable: true
}
boolean canEdit(Role role){ // not sure the best way to check for if the role is associated, this was something I tried with te default add method.
if(canEdit){
addToCanEditRole(role)
}
return canClose
}
}
class WorkController{
def save(Long id, String canEdit) {
def work = Work.get(params.id)
def roleToCheck = Role.findByName(params.roleNames)
Boolean roleCanEdit = canEdit ? true : false
if(roleCanEdit){
workflow.canEdit(roleToCheck)
println "role is " + roleToCheck
}else{
println "no roles added"
}
}
}
GSP Code Snippet:
<tr class="prop">
<td valign="top" class="name">
<label for="canEdit">Allow Role to Edit</label>
</td>
<td valign="top" style="text-align: left;"
class="value ${hasErrors(bean: WorkInstance, field: 'canEdit', 'errors')}">
<ul>
<g:each in="${roleNames}" var="role">
<li>
<g:checkBox name="canEdit" value="${WorkInstance?.canEdit}"/> <label>${role.name}</label>
</li>
</g:each>
</ul>
</td>
</tr>
</tbody>
</table>
</div>
<div class="buttons">
<span class="button"><g:actionSubmit class="save" action="save"
params="[roleName: roleNames.name]"
value="${message(code: 'default.button.update.label', default: 'Update')}"/></span>
</div>
Have you looked at grails.plugin.springsecurity.ui.UserController
? It seems that the functionality that you want is already there.
protected Map buildUserModel(user) {
Set userRoleNames = user[authoritiesPropertyName].collect { it[authorityNameField] }
def granted = [:]
def notGranted = [:]
for (role in sortedRoles()) {
String authority = role[authorityNameField]
if (userRoleNames.contains(authority)) {
granted[(role)] = userRoleNames.contains(authority)
}
else {
notGranted[(role)] = userRoleNames.contains(authority)
}
}
[roleMap: granted + notGranted, tabData: tabData, user: user]
}
Then in your gsp, you can access roleMap like:
<g:each var="entry" in="${roleMap}">
<div class="form-group">
<label class="col-xs-4">
<g:link controller='role' action='show' id='${entry.key.id}'>
${entry.key.authority.encodeAsHTML()}
</g:link>
</label>
<div class="col-xs-8">
<g:checkBox name="${entry.key.authority}" value="${entry.value}"/>
</div>
</div>
</g:each>
I am using grails 2.1.1. I have a table named audit firm. There are two types of firm. Audit review firm and ca firm. I am saving the value of audit review firm in parent table and ca firm in child table that's why ca firm is a multiple select box. But when I want to edit the audit review firm is selected but ca firms are not selected in the multiple select box. I have searched for it and applied some things but no luck. Can anyone please help me on this please ??!!! Here are my attempts below ::
my parent domain >>>
class DistrictAssignToAuditReviewFirm {
static mapping = {
table('ADT_DIST_ASSN_TO_ADT_RV_FIRM')
version(false)
caFirm cascade: 'all'
}
AuditFirm auditReviewFirm
long CREATED_BY=0
Date CREATED_DATE=new Date()
long UPDATED_BY=0
Date UPDATED_DATE=new Date()
static hasMany = [caFirm: DistrictAssignToAuditReviewFirmDetails]
static constraints = {
auditReviewFirm(nullable: false, blank: false)
CREATED_BY(nullable:true)
CREATED_DATE(nullable:true)
UPDATED_BY(nullable: true)
UPDATED_DATE(nullable: true)
}
String toString(){
return auditReviewFirm
}
}
my child domain >>>
class DistrictAssignToAuditReviewFirmDetails {
static mapping = {
table('ADT_DIST_ASSN_TO_ADT_RV_DETL')
version(false)
}
AuditFirm caFirmDetails
DistrictAssignToAuditReviewFirm distAssnToAdtRevFirm
static constraints = {
caFirmDetails(nullable: false, blank: false)
distAssnToAdtRevFirm(nullable: false, blank: false)
}
String toString(){
return caFirmDetails
}
}
my view page >>>
<div class="col-xs-6">
<div class="form-group">
<label for="auditReviewFirm">
<g:message code="dl" default="Audit Review Firm" />
</label>
<g:select id="auditReviewFirm" name="auditReviewFirm.id" from="${auditReviewFirm}" optionValue="auditFirmName" optionKey="id" noSelection="['':'Select One']" required="" value="${districtAssignToAuditReviewFirmInstance?.auditReviewFirm?.id}" class="form-control"/>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label for="caFirm">
<g:message code="dl" default="CA Firm" />
</label>
<g:select id="caFirm" name="caFirm.id" from="${caFirm}" optionValue="auditFirmName" optionKey="id" multiple="true" required="" value="${districtAssignToAuditReviewFirmInstance?.caFirm?.caFirmDetails?.id}" class="form-control"/>
</div>
</div>
I've created a JSFiddle to help demonstrate my question: http://jsfiddle.net/jeffreyrswenson/CrYWn/5/
Here's what I'd like to see:
Messages should not appear when page loads.
Messages should appear when submit button is pushed.
Messages should appear after input value is changed and user leaves element. (Tabs or clicks to next field)
Messages should appear after user leave an input without changing.(For example a field is required and the user tabs through the field, but doesn't enter a value. I'd like the validation message to appear when this happens.)
The first four work as I'd expect. Is the last item possible and if so, what do I need to change to enable that behavior?
HTML:
<label>First name:
<input data-bind='value: firstName' />
</label>
<br/>
<label>Last name:
<input data-bind='value: lastName' />
</label>
<br/>
<button type="button" data-bind='click: submit'>Submit</button>
<br/>
<span data-bind='text: errors().length'></span> errors
ViewModel:
var viewModel = function () {
ko.validation.configure({
decorateElement: true,
registerExtenders: true,
messagesOnModified: true,
insertMessages: true,
parseInputAttributes: true,
messageTemplate: null
});
this.firstName = ko.observable().extend({
required: true
});
this.lastName = ko.observable().extend({
required: true,
pattern: {
message: 'Hey this doesnt match my pattern',
params: '^[A-Z0-9]+$'
}
});
this.submit = function () {
if (this.errors().length == 0) {
alert('Thank you.');
} else {
this.errors.showAllMessages();
}
};
this.errors = ko.validation.group(this);
};
You just need to use the standard valueUpdate option of the value binding where you can specify additional events to trigger your property change and with that the validation.
So you just need to add the valueUpdate: "blur" setting on your bindings:
<label>First name:
<input data-bind='value: firstName, valueUpdate: "blur"' />
</label>
<br/>
<label>Last name:
<input data-bind='value: lastName, valueUpdate: "blur"' />
</label>
Demo JSFiddle.
In my case, I needed the value to update after key down because I was making some fields visible if the input had a value. I wanted the underlying value to update but didn't want the validation to show until the user tabbed to the next input.
A bit of CSS and a couple of bindings is what worked for me:
CSS:
div.validationWrapper.standard-focus.has-focus .validationMessage
{
display: none;
}
HTML:
<div class="validationWrapper standard-focus" data-bind="css: { 'has-focus': MyObservableHasFocus() }">
<input class="standard-focus" type="text" data-bind="hasFocus: MyObservableHasFocus, value: MyObservable, valueUpdate: 'afterkeydown'" />
</div>
Knockout:
self.MyObservable = ko.observable('').extend({/* Your validation here */});
self.MyObservableHasFocus = ko.observable(false);
The result is an observable that updates it's value after key up and shows the validation message after it loses focus.
I'm trying to add the field confirm password to an user register form. This is an extract of my domain class:
package usuario
class Usuario {
transient springSecurityService
String password
String confirmarPassword
static constraints = {
password blank: false, password: true, size:5..15, matches:/[\S]+/
confirmarPassword blank:false, password: true, size:5..15, matches:/[\S]+/, validator:{ val, obj ->
if (obj.password != obj.confirmarPassword)
return 'usuario.password.dontmatch'
}
}
static transients = ['confirmarPassword']
static mapping = {
password column: '`password`'
}
Set<Rol> getAuthorities() {
UsuarioRol.findAllByUsuario(this).collect { it.rol } as Set
}
def beforeInsert() {
encodePassword()
}
def beforeUpdate() {
if (isDirty('password')) {
encodePassword()
}
}
protected void encodePassword() {
password = springSecurityService.encodePassword(password)
}
}
obj.confirmarPassword is always null so the constraint is never accomplished.
I installed the Grails Templates as it is suggested here. I changed the attribute persistentProperties, but it is only at _form.gsp, and _form.gsp is not used by Grails Fields Plugins. Therefore, the transients attributes don't appear in the Internet Browser.
I copied the generated transients attributes of the _form.gsp to create.gsp:
<fieldset>
<g:form class="form-horizontal" action="create" >
<div class="fieldcontain ${hasErrors(bean: usuarioInstance, field: 'confirmarPassword', 'error')} required">
<label for="confirmarPassword">
<g:message code="usuario.confirmarPassword.label" default="Confirmar Password" />
<span class="required-indicator">*</span>
</label>
<g:field type="password" name="confirmarPassword" maxlength="15" pattern="${usuarioInstance.constraints.confirmarPassword.matches}" required="" value="${usuarioInstance?.confirmarPassword}"/>
</div>
<fieldset>
<f:all bean="usuarioInstance"/>
<div class="form-actions">
<button type="submit" class="btn btn-primary">
<i class="icon-ok icon-white"></i>
<g:message code="default.button.create.label" default="Create" />
</button>
</div>
</fieldset>
</g:form>
</fieldset>
The transients attributes appear on the screen, but obj.confirmarPassword is still null
The last approach I thought is to modify views_fields\default_field.gsp to also use these transients attributes, but I didn't find how. This is the documentation. This is the content of that file:
<%# page defaultCodec="html" %>
<div class="control-group ${invalid ? 'error' : ''}">
<label class="control-label" for="${property}">${label}</label>
<div class="controls">
<%= widget %>
<g:if test="${invalid}"><span class="help-inline">${errors.join('<br>')}</span></g:if>
</div>
</div>
Is it possible with Grails Fields Plugin? Where are the conditions tested, on client or server side? (I think part of the checking is on client side, but all the restrictions are checked again on server side).
Oh that i have i encountered it before .
Declaring
String confirmarPassword
and making it traniset it doesn't work me like this
static transients = ['confirmarPassword']
But
transient confirmarPassword
As you did it for the springService...i don't now why that doesn't work and this , some one may have a better clarification ...
In latest versions of Grails, transient attributes are not binded with form by default. This is the documentation of the bindable constraint. This is how my code would become (I just added bindable: true):
static constraints = {
password blank: false, password: true, size:5..15, matches:/[\S]+/
confirmarPassword bindable: true, blank:false, password: true, size:5..15, matches:/[\S]+/, validator:{ val, obj ->
if (obj.password != obj.confirmarPassword)
return 'usuario.password.dontmatch'
}
}
However, transient attributes are not shown with <f:all bean="usuarioInstance"/>, We have to add these attributes manually: <f:field bean="${usuarioInstance}" property="confirmarPassword" />. It is not an ideal solution. I'll keep this question open some days hoping that someone knows the answer.
UPDATE
In my case, the above solution didn't work either because I'm using Spring Security Core plugin. It gives the next error:
null id in usuario.Usuario entry (don't flush the Session after an exception occurs)
In that case, we have to use a command object. That issue is described (and solved) in this post.