grails render list of checkboxes - grails

I've scoured the web for almost a day and can't seem to find a solution to my problem.
I have a lookup table called Hobby which has a bunch of hobbies in it (camping, hiking, biking, etc). The hobbies table is populated during the bootstrap. Grails creates a hobby table with an id, and description field.
I have a domain object called Applicant. An applicant can have zero or more hobbies. I've declared the domain like this:
class Applicant {
static hasMany = [hobbies:Hobby]
List <Hobby> hobbies = LazyList.decorate(new ArrayList(), FactoryUtils.instantiateFactory(Hobby.class));
}
In my controller I'm using a command object for the page that will allow the applicant to select their hobbies. It is defined as:
class LifestyleCommand {
List <Hobby> hobbies = LazyList.decorate(new ArrayList(), FactoryUtils.instantiateFactory(Hobby.class));
}
My gsp looks like this:
<g:each var="item" in="${Hobby.list()}" status="i">
<g:set var="newline" value="${(i % 3) == 0 ? 'newline' : ''}" />
<div class="formcheckbox columns3 ${newline}">
<g:checkBox name="hobbies_${item.id}" optionKey="id" value="${item.id}" />
<label>${item.description}</label>
</div>
</g:each>
The page will display all of the hobbies in the page correctly. However when I try to submit the form back to the controller the list in the LifeStyleCommand object is null. I'm not sure if my gsp has the g:checkBox variables set correctly, and I'm not sure if I declared the List in the command object correctly either. Once I get the data to be sent back to the controller, my next problem to overcome will be copying the data from the command object to the Applicant. Any help would be appreciated.
I've tried using just a plain List in the command but grails complains about type conversions when the form is submitted.
EDIT:
Here is what I got to work:
class Applicant {
static hasMany = [hobbies:Hobby]
//I removed the List <hobby>... code
...
}
My command object:
class LifestyleCommand {
Set <Hobby> hobbies;
}
gsp:
<g:each in="${Hobby.list()}" var="hobby">
<g:set var="checked" value="${ command?.hobbies.find{h->h.id == hobby.id } != null }" />
<g:checkBox value="${checked}" name="${ 'hobby' +'[' + hobby.id + ']'}"/>${hobby.description}</td>
</g:each>
And my new controller code:
Hobby.list().each{hobby->
if (params["hobby[${hobby.id}]"] == 'on') {
applicant.addToHobbies(hobby)
}
else if (applicant.getHobbies().contains(hobby)) {
applicant.removeFromHobbies(hobby)
}
}
Everything is working. There may be better ways, and I haven't done any refactoring yet but having it work is a starting point.

The problem is with your GSP. When you need a list of objects, you have to maintain a standard name for your checkbox:
<g:checkBox name="hobbies[$i].id" ... />
This will be mapped correctly to your command list.

Related

Grails - How to send one ID from view to controller (many-to-many relationship)

I am trying to send an ID of an iterated item but I think the whole list is being sent. How can I send just one ID?
I have STUDENT and COURSE domain class.
Domain Model
class Student {
String fullName
String toString() {
"$fullName"
}
static belongsTo = [school: School]
static hasMany = [courses:Course, studentCourses:StudentCourse]
}
class Course {
String course
String toString() {
"$course"
}
static hasMany = [studentCourses:StudentCourse]
static belongsTo = Student
}
class StudentCourse {
Student student
Course course
//Some methods...
}
And this is my edit view.
<g:if test="${studentInstance.studentCourses?.course}">
<g:each class="courseList" in="${studentInstance.studentCourses?.course}" var="courses">
<li class="courseList">
<span class="courseList" aria-labelledby="courses-label">${courses.id} ${courses}
<g:actionSubmitImage class="deleteIcon" action="deleteCourse" value="delete"
src="${resource(dir: 'images', file: 'delete.png')}" height="17"
onclick="return confirm('${message(code: 'default.button.delete.confirm.message', default: 'Are you sure?')}');"
params="${courses.id}"/></span>
</li>
</g:each>
</g:if>
I'd like to be able to delete one course from the list when user clicks delete.png image. But when I println params.course the parameter is being sent as a whole list, not as an individual item of the list even though it is within g:each tag. How can I just send one corresponding item to the controller?
My edit page has a list of courses.
Course 23 English (delete icon here)
42 Science (delete icon here)
67 Math (delete icon here)
In my println params.course this is what I see.
[ English, Science, Math ]
How can I just have [English] when user clicks the delete button next to English line?
Thank you in advance.
As far as I have understand your problem, The problem should be in your taglib
I am giving you the example of similar code and this is working.
<g:each in="${student?.studentCourses?.course}" var="course">
<div>
<span>${course.id} ${course.course}</span>
<g:link controller="login" action="delete" params="[courseId:course.id]">
Delete
</g:link>
</div>
In this block of code, by clicking delete link with params having params="${[id:course.id]}, it will redirect to delete action inside login controller. Print params in delete action and you will get output as
params: [courseId:2, action:delete, format:null, controller:login]
Here you can see your params.courseId is only a Long value not a list.
Hope it will help you understand your problem.

How to use Grails bindData when using collections in entity fields and properties

I have the following domain classes:
Airport:
class Airport {
String airportName
List<Flight> flights
static hasMany = [flights: Flight]
static mappedBy = [flights: "departureAirport"]
}
Flight:
class Flight {
String flightName
Integer numOfStaff
Airport departureAirport
}
I have in a form the following input fields which is correctly prints the saved airport and it's flights:
<input name="airportName" value="${airportInstance.airportName}" />
<input name="id" value="${airportInstance.id}" />
<input name="version" value="${airportInstance.version}" />
<g:set var="counter" value="${0}" />
<g:each in="${airportInstance?.flights?}" var="f">
<div>
name : <input name="flights[${counter}].flightName" value="${f.flightName}" />
id : <input name="flights[${counter}].id" value="${f.id}" />
numOfStaff : <input name="flights[${counter}].numOfStaff" value="${f.numOfStaff}" />
<g:set var="counter" value="${counter + 1}" />
</div>
</g:each>
I post the form to the following controller action:
#Transactional
def update() {
Airport airportInstance = Airport.get(params.id)
// doesn't work as well
// airportInstance.properties = params
bindData(airportInstance, params)
airportInstance.save flush:true
request.withFormat {
form multipartForm {
flash.message = message(
code: 'default.updated.message',
args: [
message(
code: 'Airport.label',
default: 'Airport'
),
airportInstance.id
]
)
redirect airportInstance
}
'*'{ respond airportInstance, [status: OK] }
}
}
When I modify airportName or some of the flights properties (e.g.: numOfStaff) and I click update (post it to the controller action), Grails does his work correctly and updates my changes.
But if I manually add a new instance in the view e.g.: if there were two flights flights[0] and flights[1] listed by g:each and I add flights[2] it doesn't create a new instance of Flight and doesn't save it.
The same if I try to delete an existing flight from the view and post it to the controllers update action.
I'm using grails version 2.3.9
My questions are:
Am I doing something wrong?
Is it a Grails bug or Grails developers implemented bindData method like this for some reason?
What is the best way in Grails to implement issue like above (I mean giving the name attributes like foo[index].prop where foo is a collection in the entity).
Sorry for the junk html.
Thank you guys, for the answers.
I found the solution. If I declare a controller action like:
def update(Airport airport) {
// ...
}
In this case before the controller action is executed Grails will automatically create an instance of the Airport class and populate its properties by binding the request parameters.
If there is an id request parameter then instead of invoking the domain class constructor to create a new instance a call will be made to the static get method on the domain class and the value of the id parameter will be passed as an argument. Whatever is returned from that call to get is what will be passed into the controller action. This means that if there is an id request parameter and no corresponding record is found in the database then the value of the command object will be null.
See Grails Reference Documentation: http://grails.org/doc/latest/guide/theWebLayer.html#commandObjects

How to Send a Javascript Variable to a Grails Controller to Query Neo4J

I'm a newbie trying to find uses for Neo4J on Grails.
Basically, I've made 20 grocery item nodes through the Neo4J browser and I want to create a simple Grails site that will let users search a grocery item and visually show the items related to it.
My index.gsp has:
<input id="item" />
My viz.js has:
$('#item').keyup(function() {
var item = $('#item').val();
My Item Domain class has
class Item {
static mapWith = "neo4j"
String name
My ItemController class has:
def index() {
def item = Item.list() [item:item] //No idea, just trying out whatever i find :(
and a query with something like:
def query = Item.cypherStatic ("""start n=node({Item}) match (n)-[r]->(x) where r='partner' return n, x)
Questions:
How can I properly send the JS 'item' variable into the ItemController?
How can I use the 'item' variable to properly query the node names which have a 'partner' relationship with the item?
in addition to Motilals answers, you definetly need a wrapping form with an action that points your controller
like
<g:form controller="itemController" action="index" >
<input type="text" id="item" name="item" value="" />
<input type="submit" value="submit" >
</g:form>
then on clicking submit the for will call your index action and there you could parse the value with
def item = params.item
but it looks more like you want some asynchronous stuff right after keyup-function, therefore you could do sth like this :
$('#item').keyup(function() {
var item = $('#item').val();
$.ajax({
url: "${createLink(controller:'itemController', action:'index')}",
data:"&item="+item
})
.done(function( data ) {
console.log(data)
});
});
in this case, you need to pay attention what your index-action is returning, so you can do in the .done() whatever you want with the response.
also note, that when you name an action "index" it will be available at
.../myproject/item/index
or, and thats important
.../myproject/item/
so if your index method requires the data from the input, it will miss them if a user has gone straight to that url
so your index action would rather render the page with the input
and you define another action for executing your query based on input and returning data
set the item to hidden field and then you can access it directly in your controller using params
here you go:
//in index.gsp add below hidden field and set the hidden filed in your js code
<g:hiddenField name="item" value="" />
$('#item').keyup(function() {
var item = $('#item').val();
//in your controller
def index() {
def item = params.item
print item // you see the value for item
//do your stuff
}
once you have item value you could directly use HQL query or use the domain instance
hope this helps you
Regards
Motilal

MVC3 - Submission of custom input attributes on input button when submitting a form

Basically I have a form that I am dynamically adding objects to. I am doing this with AJAX so can just initialise the object and return it with JSON. Each new object has a unique GUID assigned to it so we can identify each object in the model collection when it is passed back into the action.
However, I need to support non JavaScript so am trying to write a solution that will post back the model and add or remove the given object from the model. There can be any number of these new objects on the model so I need to pass back several things to find out which object to delete before returning the model back to the view. This could be either
a) The GUID for the object the user has deleted.
b) The button that has been clicked to identify which object to delete.
The problem is that the partial view is generic and I would like to keep it that way so I'm trying to pass the identifying GUID back with the input button on each partial view but don't know how. I can easily do this with JavaScript because I just remove the created AJAX object from the page before posting it when the user clicks the remove link but can't figure out how to do it with a submit. Basically I want to do something like this:
#using (Project.Namespace.Infrastructure.Helpers.HtmlPrefixScopeExtensions.HtmlFieldPrefixScope _scope = Html.BeginCollectionItem())
{
<ul class="ulMedicationsControl">
#Html.ActionLink("Remove This Object", "RemoveObject", null)
#Html.Input("RemoveObject", "Remove This Object", new { Prefix = _scope.Prefix, objectGUID = IdentifyingGUID })
#Html.HiddenFor(m => m.IdentifyingGUID);
<li class="liQuestion">
#Html.MandatoryLabelFor(m => m.myField)
#Html.TextBoxFor(m => m.myField)
</li>
</ul>
<div id="#(_scope.Prefix).ajaxPlaceholder"></div>
}
In the controller:
[ActionName("FormName")]
[AcceptParameter(Name = "RemoveObject", Value = "Remove This Object")]
public ActionResult RemoveObject(MyParentModel model, string Prefix, string objectGUID)
{
Guid ID = new Guid(objectGUID);
foreach (ObjectModel object in model.objects){
if (object.IdentifyingGUID == ID)
{
model.objects.Remove(object);
break;
}
}
return View(model);
}
Any help I would really appreciate as I simple can't figure out how to do this!
EDIT
Also just to add the prefix attribute simply identifies where in the form the object sits. This will be needed for me to find which object list to go through and remove the object from as there may be several lists in different placed in the model.
An HTML input only passes "name=value" when a form post occurs so that's all you have to work with. With <input type=submit> you're further limited by the fact that the button's value is its caption (i.e. "myControl=Click Me!" is posted), so you can't stick anything programmatically meaningful in the value.
Method 1: So you're left with encoding all the information you need into the input's name - an approach that works fine, but you'll have to have to go digging into the controller action method's FormCollection parameter rather than relying on model binding. For example:
<input name="delete$#(_scope.Prefix)$#objectGUID" type="submit" value="Delete me" />
Better, have a helper class that encapsulates the string format with a ToString override and has Parse/TryParse/etc static methods, which could be used like this:
<input name="#(new DeleteToken{Prefix=_scope.Prefix, objectGUID=IdentifyingGUID})" type="submit" value="Delete me" />
In your action method:
[HttpPost]
public ActionResult Foo(FormCollection formData)
{
var deleteTokens = DeleteToken.ParseAll(formData.AllKeys);
foreach (var token in deleteTokens)
{
//...do the deletion
}
}
Method 2: An alternative approach is to group each item into its own <form> (bear in mind you can't nest forms) - so when the submit happens, only its surrounding form is posted in which you can stash hidden inputs with the necessary data. e.g.
<ul class="ulMedicationsControl">
<form ... >
<!-- hidden field and submit button and whatever else here -->
...
</form>
</ul>

asp.net mvc 3 Model with complex property and checkboxes

I have a typed view as Item (one class that i created) with a form inside to add Items to my database. This Item class has one property called Categories that is a List (Category has 2 properties ID and Name)
Im using an editorfor in my view:
<div>
#(Html.EditorFor(e => e.Categories, "Categories"))
</div>
I created an EditorTemplatefor called "Categories.cshtml" render all the available categories:
#{
Layout = null;
}
#model List<Category>
#{
foreach (Category category in ((BaseController)this.ViewContext.Controller).BaseStateManager.AvailableCategories)
{
#Html.Label("test", category.Name)
<input type="checkbox" name="Categories" value="#(category.ID)" />
}
}
The checkboxes are well rendered (one for every Available category in cache), but after clicking in some, and post the form, im receiving my instance of Item but with the property Categories empty.
What i have to do to receive my List Categories completely instantiated after submit the form?
Dont loop it. Let the framework generate the code for you (then, it will know how to build it back and bind it to your controller).
Just pass the list to the editor template and mvc will do the rest. Check my blog post on something similar.
Try using an index based loop. This ensures MVC will render the item's attributes in such a way that allows the default model binder to instantiate the model on post back. Also, use the Html helper for the checkbox as well:
var categories = ((BaseController)this.ViewContext.Controller).BaseStateManager.AvailableCategories;
for (var index = 0; index < categories.Count; index ++)
{
#Html.Label("test", categories[index].Name)
#Html.Checkbox("ID", categories[index].ID)
}

Resources