Search code examples
grailsgrails-orm

Using checkboxes in many to many relationships in grails


I am having a typical issue with using checkboxes. What I have is Employees and Skills. Each Employee can have many skills, and skills can belong to many Employees. This makes it a many to many relationship.

This are my Domain classes

class Skill {
    String name
    static hasMany = [users:Employee, resources:Resource]
    static belongsTo = [Employee]

    static constraints = {
        name unique: true, nullable: false, blank: false
    }

    String toString() { return name }
    String getDisplayName() { return name }
}

class Employee {

String name
String surname
Date birthdate
BigDecimal salary
Address address

static hasMany = [skills: Skill]
static belongsTo = [team:Team]

static constraints = {
    address nullable: false, blank: false
    name nullable: false, blank: false, matches: '[a-zA-Z\\-\'\\s]+'
    surname nullable: false, blank: false, matches: '[a-zA-Z\\-\'\\s]+'
    skills nullable: true, blank: true
    salary nullable: false, min: 16000.0, max: 60000.0
    team nullable: true
    birthdate max:new Date(use(TimeCategory){18.years.ago.getTime()})
}

String toString() { return "\nEmployee Information:\n Name: $name\n Surname: $surname\n " +
        "Date of Birth: $birthdate\n Salary: $salary\n"}

}

Employee domain works fine, I can create an employee with an address without any issues.

But when I try to add a skills to an employee, it doesn't do what I tell it too. For instance, when I check two particular skills, it try to run and save, it would add all of the skills to the employee.

How do I tell the controller to only add what is been checked.

This is my code in the controller

@Transactional
def create() {
    params.each { println("$it.key -> $it.value") }
    def employee = new Employee(params)
    def address = new Address(params)
    def skills = Skill.list()

    if (params["create"]) {
        if (address.validate()) {
            address.save()
            employee.address = address
            params.remove("_s")
            skills.each { skill -> if (params["s"] != null ) {
                employee.addToSkills(skill)
                }
            }
            if (employee.validate()) {
                employee.save()
                flash.message = "Employee has been successfully created"
                redirect(action: "index")
            }
        } else {
            flash.message = "Ooops Error!!!!"
            render(view: "create", model: [employee: employee, address: address, skill: skills])
        }
    }
    [employee: employee, address: address, skill: skills]
}

This is my create.gsp

<div class="col-md-6">
<label>Select Your Competences:</label>
   <g:each in="${skill}" var="s" status="i">
    <br/>
    <g:checkBox name="s" value="${s.id}" checked="false"/>
     <label for="s">${s.name}</label>
 </g:each>
 </div>

Solution

  • modify your gsp as :

    <label>Select Your Competences:</label>
       <g:each in="${Skill.list()}" var="skill" status="i">
        <br/>
        <g:checkBox name="s.${skill}" value="${skill.id}" checked="false"/>
         <label for="s">${skill}</label>
     </g:each>
     </div>
    

    when you submitt this form you will get a params on your controller having values like this : [skill:[_MsOffice:'', MsOffice:1, Managing:10, AgileCoach:3, _Coding:'']

    to get the ids of the checked values from the params do the following on your controller:

    List<Long> skillIds = new ArrayList<Long>()
    
      params.skill?.each { s ->
    
       if(!s.getKey().startsWith("_")) {
    
        skillIds.add(s.getValue())
       }
      }
    

    now on skillIds list you have a list of Skill ids for an Employee, then pass this to your service and fetch skills to add to an Employee (using Skill.getAll(skillIds) or some other gorm operation)

    you can see example here http://groovyconsole.appspot.com/script/5722903798611968