This is my first development using Grails. I have a requirement to create a questionnaire. This is my GSP page in which the questions are listed, possible answers for each question is listed and depending upon the type of answer a checkbox or radio button is displayed.It works fine till here
<div class="body">
<h1><g:message code="default.edit.label" args="[entityName]" /></h1>
<g:if test="${message}">
<div class="message">${message}</div>
</g:if>
<g:form action="createDonation" >
<div class="dialog">
<table>
<tbody>
<g:each in="${questionList}" status="i" var="questionInstance">
<tr>
<td>${fieldValue(bean: questionInstance, field: "text")}</td>
</tr>
<tr>
<g:each in="${questionInstance?.answers?}" status="j" var="a">
<td >
<g:if test="${fieldValue(bean: a, field: 'ansType.name') == 'Multiple'}"><g:checkBox name="myGroup" value="${false}" /></g:if>
<g:if test="${fieldValue(bean: a, field: 'ansType.name') == 'Single'}"><g:radio name="myGroup" value="1"/></g:if>
</td>
<td >${fieldValue(bean: a, field: "text")}</td>
</g:each>
</tr>
</g:each>
</tbody>
</table>
</div>
<div class="buttons">
<span class="button"><g:submitButton name="return" class="save" value="${message(code: 'default.button.backtodonorlogin.label', default: 'Back')}" /></span>
<span class="button"><g:submitButton name="submit" class="save" value="${message(code: 'default.button.saveandcontinue.label', default: 'Create')}" /></span>
</div>
</g:form>
</div>
</body>
Now i want to save the response of the user i.e what is the answer selected by each user for each question. For Multiple choice multiple answers can be selected. I am having a hard time to figure out how to create a model for that.
Help is requested.
Thanks
how about this: you create a QuestionResponse entity with the following properties:
question (linking to the question being answered)
response (answer given by user)
responder (responding user)
You can find out about the current user by using spring security for instance.
You can determine the question being responded from the hidden ID in your form.
Related
Form inputs error messages and form entries are not updated when the inputs are changed.
I tried to switch to form instead of ng-form, tried different kind of validations (required, email, pattern, ...) None seemed to change anything.
I am using
angular 1.7
angular-material 1.1.9
angualr-messages 1.7
This form is inside a table > tbody
<ng-form name="editedJiraForm">
<tr md-row ng-repeat="task in jira.tasks track by $index">
<td md-cell>
<md-input-container>
<label>Title</label>
<input name="title" ng-model="editedJira.title" required>
<div ng-messages="editedJiraForm.title.$error" md-auto-hide="false">
<div ng-message="required">Title required</div>
</div>
</md-input-container>
</td>
<td md-cell>
<md-input-container>
<label>Description</label>
<textarea name="description" ng-model="editedJira.description" md-maxlength="5000" rows="3" md-select-on-focus required></textarea>
<div ng-messages="editedJiraForm.description.$error" md-auto-hide="false">
<div ng-message="required">Description required</div>
</div>
</md-input-container>
</td>
<td md-cell>
<md-input-container>
<label>Priority</label>
<input name="priority" ng-model="editedJira.ubi_priority" ng-pattern="/^(01|02|03)$/"/>
<div ng-messages="editedJiraForm.priority.$error" md-auto-hide="false">
<div ng-message="pattern">Invalid priority</div>
</div>
</md-input-container>
</td>
<td md-cell>
<md-input-container>
<label>Estimation</label>
<input name="estimation" ng-model="editedJira.estimation" ng-pattern="/^\d+(.\d+)?$/">
<div ng-messages="editedJiraForm.estimation.$error" md-auto-hide="false">
<div ng-message="pattern">Invalid estimation, use int or float</div>
</div>
</md-input-container>
</td>
<td md-cell>
<md-button ng-disabled="editedJiraForm.$invalid" class="md-fab md-primary md-mini" aria-label="validate" ng-click="editJira(selection.feature, $index, true)">
<md-icon md-svg-src="static/images/validate.svg"></md-icon>
</md-button>
<md-button class="md-fab md-primary md-mini" aria-label="cancel" ng-click="editJira(selection.feature, $index)">
<md-icon md-svg-src="static/images/cancel.svg"></md-icon>
</md-button>
</td>
</tr>
</ng-form>
I expect to have
- The messages displayed when for instance the priority does not match the pattern.
- Similarly, when the form is invalid the validate button should be disabled.
Though in my case:
- The inputs fields do become red when the pattern or requirement is not respected but the messages do not show up.
- The validation button is always active.
When displaying the editedJiraForm in the html it is not updated as I edit the form, maybe a problem there ?
Ok if anybody got a similar problem, I found an answer from there
This was due because of the form being split into several breaking it.
The solution was to remove the actual element and replace it by using ng-form name="editedJiraForm" on the element.
This gives the following code:
<tr ng-form name="editedJiraForm">
<td md-cell>
<md-input-container>
<label>Title</label>
<input name="title" ng-model="editedJira.title" required>
<div ng-messages="editedJiraForm.title.$error" md-auto-hide="false">
<div ng-message="required">Title required</div>
</div>
</md-input-container>
</td>
<td md-cell>
....
</td>
</tr>
Grails issue :
When clicking over the Edit in the following screen : Domain and Variable values does not seem to retain their values (When creating it, we are able to enter the values - Domain and Variable, but it does not retain the values when editing..
Clicking over EDIT in the following screen :
does not retain the values of domain and variable alone:
.gsp code below
<tr>
<td>Domain<span style="color: red">*</span></td>
<td><g:if test="${isCreate}">
<g:select class="statSele" id="domain_name" name="domain_name" from="${com.datumrite.master.DomainMaster.list()}" optionKey="id" noSelection="['':'Select Domain']" onchange="${remoteFunction(
action:'driveVariableFromDomain',
params:'\'id=\'+escape(this.value)',
update: 'variable_select'
)}"></g:select> <br>
</g:if>
<g:else>
<input disabled="true" id="dN" name="name" type="text" style="margin-bottom:4px" value="${(data.domain_name instanceof String)?'':com.datumrite.master.DomainMaster.get(data.domain_name)}" />
</g:else>
</td>
</tr>
<tr>
<td>Variable<span style="color: red">*</span></td>
<td><g:if test="${isCreate}">
<span id="variable_select">
<g:select class="statSele" name="var_name" from="${[]}" value="${data?.var_name}" noSelection="['':'Select Variable']"></g:select></span> <br>
</g:if>
<g:else>
<input disabled="true" type="text" name="name" style="margin-bottom:4px;margin-left:5px" value="${data?.var_name}" />
</g:else></td>
</tr>
<tr>
<tr>
<td>Condition<span style="color: red">*</span></td>
<td><g:if test="${isCreate}">
<g:select class="statSele" id="constrain_type" name="constrain_type"
from="${['Lesser than','Greater than','Lesser than nor Equal','Greater than nor Equal','Equal To','Not Equal To']}"
value="${data?.constrain_type}" noSelection="['':'Select Condition']">
</g:select>
</g:if>
<g:else>
<input disabled="true" name="name" value="${data?.constrain_type}" type="text" style="margin-bottom:4px"/>
</g:else></td>
</tr>
And, I am using MySQL DB for the same.
can anyone help me with this issue ?
You need to make sure the subscription instance is passed when you click Edit link. (possibly under <g:link...> tag) The gsp you've posted is for create view. Check your List of Subscription view. (possibly show.gsp)
I have put the ":spring-security-ui:1.0-RC1" into my BuildConfig and compiled it then I created the auth, register and securityInfo views and controller by using the spring ui command. I also put a user in my db to test it.
However, when I tested it I just get to the login page, after login in I get to the grails page back. However the button Register New User and Forgotten Passoword do not work, I only get redirected to the login page! Even though the file gets correctly rendered to *.html.
This is how the view structure looks like:
My ÙRLMappings look like that:
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?(.${format})?"{
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
}
}
I would appreciate your answer on my problem!
UPDATE
This is my auth.gsp:
<html>
<head>
<title><g:message code='spring.security.ui.login.title'/></title>
<meta name='layout' content='register'/>
</head>
<body>
<p/>
<div class="login s2ui_center ui-corner-all" style='text-align:center;'>
<div class="login-inner">
<form action='${postUrl}' method='POST' id="loginForm" name="loginForm" autocomplete='off'>
<div class="sign-in">
<h1><g:message code='spring.security.ui.login.signin'/></h1>
<table>
<tr>
<td><label for="username"><g:message code='spring.security.ui.login.username'/></label></td>
<td><input name="j_username" id="username" size="20" /></td>
</tr>
<tr>
<td><label for="password"><g:message code='spring.security.ui.login.password'/></label></td>
<td><input type="password" name="j_password" id="password" size="20" /></td>
</tr>
<tr>
<td colspan='2'>
<input type="checkbox" class="checkbox" name="${rememberMeParameter}" id="remember_me" checked="checked" />
<label for='remember_me'><g:message code='spring.security.ui.login.rememberme'/></label> |
<span class="forgot-link">
<g:link controller='register' action='forgotPassword'><g:message code='spring.security.ui.login.forgotPassword'/></g:link>
</span>
</td>
</tr>
<tr>
<td colspan='2'>
<s2ui:linkButton elementId='register' controller='register' messageCode='spring.security.ui.login.register'/>
<s2ui:submitButton elementId='loginButton' form='loginForm' messageCode='spring.security.ui.login.login'/>
</td>
</tr>
</table>
</div>
</form>
</div>
</div>
<script>
$(document).ready(function() {
$('#username').focus();
});
<s2ui:initCheckboxes/>
</script>
</body>
</html>
You probably need to add the following two rules in your grails.plugin.springsecurity.controllerAnnotations.staticRules. It is in the Config.groovy file.
'/register/index': ['permitAll'],
'/register/forgotPassword': ['permitAll'],
Otherwise, the spring security will bump your request back to the login/auth screen.
What about your controllers ?? I hear no mention of them you including them in your config file. Define your success handler and define a controller for successful login.
grails.plugin.springsecurity.successHandler.defaultTargetUrl = '/homePage'
Then if you already are being logged in then set access into the controllers using security annotations or using InterceptMapURL.
Looking at your problem you just haven't configured the security plugin well that's it. Well look at some other spring security implementation for grails in github, I think that will help you.
Simply add anonymous role to your RegisterController
import grails.plugin.springsecurity.annotation.Secured;
#Secured(['ROLE_ANONYMOUS'])
class RegisterController extends grails.plugin.springsecurity.ui.RegisterController {
}
This is the GSP code generated by Grails for the view of the edit action for a Person domain object which is part of my model, and also happens to be the primary class for authentication by the ACEGI security plug-in. I have snipped out a bunch of properties to keep it short. The file resides in the standard location, grails-app/views/person/edit.gsp
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="layout" content="main" />
<g:set var="entityName" value="${message(code: 'person.label', default: 'Person')}" />
<title><g:message code="default.edit.label" args="[entityName]" /></title>
</head>
<body>
<div class="nav">
<span class="menuButton"><a class="home" href="${createLink(uri: '/')}">Home</a></span>
<span class="menuButton"><g:link class="list" action="list"><g:message code="default.list.label" args="[entityName]" /></g:link></span>
<span class="menuButton"><g:link class="create" action="create"><g:message code="default.new.label" args="[entityName]" /></g:link></span>
</div>
<div class="body">
<h1><g:message code="default.edit.label" args="[entityName]" /></h1>
<g:if test="${flash.message}">
<div class="message">${flash.message}</div>
</g:if>
<g:hasErrors bean="${personInstance}">
<div class="errors">
<g:renderErrors bean="${personInstance}" as="list" />
</div>
</g:hasErrors>
<g:form method="post" >
<g:hiddenField name="id" value="${personInstance?.id}" />
<g:hiddenField name="version" value="${personInstance?.version}" />
<div class="dialog">
<table>
<tbody>
<tr class="prop">
<td valign="top" class="name">
<label for="username"><g:message code="person.username.label" default="Username" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: personInstance, field: 'username', 'errors')}">
<g:textField name="username" value="${personInstance?.username}" />
</td>
</tr>
...SNIP...
a bunch of props
</tbody>
</table>
</div>
<div class="buttons">
<span class="button"><g:actionSubmit class="save" action="update" value="${message(code: 'default.button.update.label', default: 'Update')}" /></span>
<span class="button"><g:actionSubmit class="delete" action="delete" value="${message(code: 'default.button.delete.label', default: 'Delete')}" onclick="return confirm('${message(code: 'default.button.delete.confirm.message', default: 'Are you sure?')}');" /></span>
</div>
</g:form>
</div>
</body>
</html>
My question is, how does the field personInstance get set up and populated?
I suspect this is a basic question which belies a fundamental lack of understanding on my part about how Grails works, but I need to know nonetheless.
This stems from the desire to create my own composite pages which access a Person object and its associated data, which is the heart of my app. I was expecting to be able to create a new page alongside this one, let's call it map.gsp, and get at the personInstance in some magic way. I can't figure out how to do that in spite of trying the obvious, and I think I have a gap right at the centre of my understanding.
PersonInstance will be populated on the controller.
When you submit your form, the associated controller will receive a map containing the fields present on your form.
So, in your controller you'll find a command like
personInstance.properties = params
where params is a map containing the fields submited for the controller, which the keys are the names of the input elements you defined on your gsp file.
I'm a Grails noob and running into something that seems to be a bug, but it is entirely possible I'm not configuring everything correctly.
I've got two simple Domain Classes:
class Player {
String firstName
String lastName
static constraints = {
firstName(blank:false)
lastName(blank:false)
}
String toString() { lastName + ", " + firstName }
}
and
class Team {
String mascot;
static hasMany = [players:Player]
static constraints = {
mascot(blank:false)
}
}
I have controllers for both that do nothing beyond dynamic scaffold these two Domain Classes.
But even when I have a list of Players in my DB, I don't get a multi-select box for them when creating a new Team.
However, the multi-select shows up when I go to edit a Team
Is this a bug in the dynamic scaffolding for new items, do I misunderstand how this is supposed to work, or is there something else I need to declare here?
Any help is hugely appreciated! I've got screenshots that StackOverflow won't let me add because of my newness, but I'd be happy to show them another way if that'll help.
I finally figured this out and wanted to pass on what I did just in case someone else runs into it.
When I generated the views for Team, the form block in edit.gsp looks like this:
<input type="hidden" name="id" value="${teamInstance?.id}" />
<input type="hidden" name="version" value="${teamInstance?.version}" />
<div class="dialog">
<table>
<tbody>
<tr class="prop">
<td valign="top" class="name">
<label for="mascot">Mascot:</label>
</td>
<td valign="top" class="value ${hasErrors(bean:teamInstance,field:'mascot','errors')}">
<input type="text" id="mascot" name="mascot" value="${fieldValue(bean:teamInstance,field:'mascot')}"/>
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="players">Players:</label>
</td>
<td valign="top" class="value ${hasErrors(bean:teamInstance,field:'players','errors')}">
<g:select name="players"
from="${Player.list()}"
size="5" multiple="yes" optionKey="id"
value="${teamInstance?.players}" />
</td>
</tr>
</tbody>
</table>
</div>
<div class="buttons">
<span class="button"><g:actionSubmit class="save" value="Update" /></span>
<span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
</div>
</g:form>
but the form block in create.gsp looks like this:
<g:form action="save" method="post" >
<div class="dialog">
<table>
<tbody>
<tr class="prop">
<td valign="top" class="name">
<label for="mascot">Mascot:</label>
</td>
<td valign="top" class="value ${hasErrors(bean:teamInstance,field:'mascot','errors')}">
<input type="text" id="mascot" name="mascot" value="${fieldValue(bean:teamInstance,field:'mascot')}"/>
</td>
</tr>
</tbody>
</table>
</div>
<div class="buttons">
<span class="button"><input class="save" type="submit" value="Create" /></span>
</div>
</g:form>
In other words, for this corner case, the default Create view omits the widget to properly display the multi-select list. When I did a copy and paste of the missing code, the dynamically scaffolded controller picked it up and persisted it as expected. So, it's definitely a bug in the view generation code.
Yes, the default scaffolding puts a parent selector in the child class' create/edit page.
I'm guessing it was just easier for them this way. It shouldn't be a multi-select though, just a pull-down single-select, as it's a One-to-Many.
As you've explained you wanted more of a Many-to-Many relationship, you might try adding:
static hasMany = [teams:Team]
to your Player class. I've found that Grails does better with bi-directional relationships. It's also useful to have when building search queries, and shouldn't require more than the one relationship table you'd already need.
If you're using Grails pre-v1.1, Many-to-Many relationships aren't directly supported, so even adding the static hasMany above won't be the complete solution, as you'll need to manage adding to the other list when you add to one direction. I haven't used v1.1 yet, so I can't talk about what is needed to specify the Many-to-Many in it.
I encountered the same problem using current version (v1.3.4) of Grails. Had to manually modify the create.gsp