Add rows on table on button click using spring mvc and bind added rows to modelAttribute

11,749

Even though this thread is older, For the benefit of others who is in need of this.

Let me explain with a minimal code and User example with firstName, email, userName and gender fields so that people won't get confused with bigger code.
Consider you are sending 3 empty users in usersList from controller this will creates 3 empty rows. If you inspect/view page source you will see

  • Rows(<input> tags) with different id's like list0.firstName list1.firstName
  • Rows(<input> tags) with different names like list[0].firstName list[1].firstName

Whenever form is submitted id's are not considered by server(added for only for helping client side validations), but name attribute will be interpreted as request parameter and are used to construct your modelAttribute, hence attribute names are very important while inserting rows.

Adding row

So, How to construct/append new rows?
If i submit 6 users from UI, controller should receive 6 user object from usersList. Steps to achieve the same is given below
1. Right click -> view page source. You will see rows like this(you can see *[0].* in first row and *[1].* in second row)

<tr>
    <td><input id="list0.firstName" name="list[0].firstName" type="text" value=""/></td>
    <td><input id="list0.email" name="list[0].email" type="text" value=""/></td>
    <td><input id="list0.userName" name="list[0].userName" type="text" value=""/></td>
    <td>
        <span>
            <input id="list0.gender1" name="list[0].gender" type="radio" value="MALE" checked="checked"/>Male
        </span>
        <span>
            <input id="list0.gender2" name="list[0].gender" type="radio" value="FEMALE"/>Female
        </span>
    </td>
</tr>

<tr>
    <td><input id="list1.firstName" name="list[1].firstName" type="text" value=""/></td>
    <td><input id="list1.email" name="list[1].email" type="text" value=""/></td>
    <td><input id="list1.userName" name="list[1].userName" type="text" value=""/></td>
    <td>
        <span>
            <input id="list1.gender1" name="list[1].gender" type="radio" value="MALE" checked="checked"/>Male
        </span>
        <span>
            <input id="list1.gender2" name="list[1].gender" type="radio" value="FEMALE"/>Female
        </span>
    </td>
</tr>
  1. Copy first row and construct a javascript string and replace '0' with variable name index. As given in below sample
'<tr>'+
    '<td><input id="list'+ index +'.firstName" name="list['+ index +'].firstName" type="text" value=""/></td>'+
    '<td><input id="list'+ index +'.email" name="list['+ index +'].email" type="text" value=""/></td>'+
    ...
'</tr>';
  1. Append the constructed row to the <tbody>. Rows get added in UI also on submission of form newly added rows will be received in controller.

Deleting row

Deleting row is little bit complicated, i will try to explain in easiest way

  • Suppose you added row0, row1, row2, row3, row4, row5
  • Deleted row2, row3. Do not just hide the row but remove it from the DOM by catching event.
  • Now row0,row1,row4,row5 will get submitted but in the controller your userList will have 6 user object but user[2].firstName will be null and user[3].firstName will be null.
  • So in your controller iterate and check for null and remove the user.(Use iterator don't use foreach to remove user object)

Posting code to benefit beginners.

//  In Controller
@RequestMapping(value = "/app/admin/add-users", method = RequestMethod.GET)
public String addUsers(Model model, HttpServletRequest request)
{
    List<DbUserDetails> usersList = new ArrayList<>();

    ListWrapper userListWrapper = new ListWrapper();
    userListWrapper.setList(usersList);


    DbUserDetails user;
    for(int i=0; i<3;i++)
    {
        user = new DbUserDetails();
        user.setGender("MALE"); //Initialization of Radio button/ Checkboxes/ Dropdowns
        usersList.add(user);
    }


    model.addAttribute("userListWrapper", userListWrapper);
    model.addAttribute("roleList", roleList);

    return "add-users";
}

@RequestMapping(value = "/app/admin/add-users", method = RequestMethod.POST)
public String saveUsers(@ModelAttribute("userListWrapper") ListWrapper userListWrapper, Model model, HttpServletRequest request)
{
    List<DbUserDetails> usersList = userListWrapper.getList();
    Iterator<DbUserDetails> itr = usersList.iterator();

    while(itr.hasNext())
    {
        if(itr.next().getFirstName() == null)
        {
            itr.remove();
        }
    }

    userListWrapper.getList().forEach(user -> {
        System.out.println(user.getFirstName());
    });
    return "add-users";
}

//POJO
@Entity
@Table(name = "userdetails")
@XmlRootElement(name = "user")
public class DbUserDetails implements Serializable
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String  firstName;
    private String  userName;
    private String  email;
    private String  gender;

    //setters and getters
}

//list wrapper
public class ListWrapper
{
    private List<DbUserDetails> list;

    //setters and getters
}

In JSP

<form:form method="post" action="${pageContext.request.contextPath}/app/admin/add-users" modelAttribute="userListWrapper">
    <table class="table table-bordered">
        <thead>
            <tr>
                <th><spring:message code="app.userform.firstname.label"/></th>
                <th><spring:message code="app.userform.email.label"/></th>
                <th><spring:message code="app.userform.username.label"/></th>
                <th><spring:message code="app.userform.gender.label"/></th>
            </tr>
        </thead>
        <tbody id="tbodyContainer">
            <c:forEach items="${userListWrapper.list}" var="user" varStatus="loop">
                <tr>
                    <td><form:input path="list[${loop.index}].firstName" /></td>
                    <td><form:input path="list[${loop.index}].email" /></td>
                    <td><form:input path="list[${loop.index}].userName" /></td>
                    <td>
                        <span>
                            <form:radiobutton path="list[${loop.index}].gender" value="MALE" /><spring:message code="app.userform.gender.male.label"/>
                        </span>
                        <span>
                            <form:radiobutton path="list[${loop.index}].gender" value="FEMALE" /><spring:message code="app.userform.gender.female.label"/>
                        </span>
                    </td>
                </tr>
            </c:forEach>
        </tbody>
    </table>
    <div class="offset-11 col-md-1">
        <button type="submit" class="btn btn-primary">SAVE ALL</button>     
    </div>
</form:form>

Javascript needs to be included in JSP

var currentIndex = 3; //equals to initialRow (Rows shown on page load)
function addRow()
{
    var rowConstructed = constructRow(currentIndex++);
    $("#tbodyContainer").append(rowConstructed);
}

function constructRow(index)
{
    return '<tr>'+
    '<td><input id="list'+ index +'.firstName" name="list['+ index +'].firstName" type="text" value=""/></td>'+
    '<td><input id="list'+ index +'.email" name="list['+ index +'].email" type="text" value=""/></td>'+
    '<td><input id="list'+ index +'.userName" name="list['+ index +'].userName" type="text" value=""/></td>'+
    '<td>'+
        '<span>'+
            '<input id="list'+ index +'.gender1" name="list['+ index +'].gender" type="radio" value="MALE" checked="checked"/>Male'+
        '</span>'+
        '<span>'+
            '<input id="list'+ index +'.gender'+ index +'" name="list['+ index +'].gender" type="radio" value="FEMALE"/>Female'+
        '</span>'+
    '</td>'+
'</tr>';
}
Share:
11,749
Sandeep
Author by

Sandeep

Updated on June 18, 2022

Comments

  • Sandeep
    Sandeep almost 2 years

    I have a table for which I am passing list of student objects from my spring controller method, On page load 3 rows are populated. I want the user to be able to add more rows delete existing rows on button click. Can anyone tell me how to achieve this. See below my controller and jsp code. On clicking add I want to add 3 more rows selecting check box and clicking delete row should delete the row. i want the the added columns to be binded

    I am very new to jQuery is this possible without jQuery. If not please tell me in detail how to achieve this using jQuery

    My Table Snap shot

    Student Entity

    @Entity
    @Table(name="STUDENT_REGISTRATION")
    public class Student {
    
        private int studentId;
        private String firstName;
        private String lastName;
        private Date dob;
        private String sex;
        private String status;
        private Date doj;
        private int deptId;
        private String deptName;
        private int batchId;
        private String batchName;
        private int roleId;
        private String roleName;
        private String regNo;
        public Student(){
    
        }
    
        public Student(int studentId, String firstName, String lastName, Date dob,
                String sex, String status, Date doj, int deptId,
                String deptName, int batchId, String batchName, int roleId,
                String roleName, String regNo) {
            super();
            this.studentId = studentId;
            this.firstName = firstName;
            this.lastName = lastName;
            this.dob = dob;
            this.sex = sex;
            this.status = status;
            this.doj = doj;
            this.deptId = deptId;
            this.deptName = deptName;
            this.batchId = batchId;
            this.batchName = batchName;
            this.roleId = roleId;
            this.roleName = roleName;
            this.regNo = regNo;
        }
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name="STUDENT_ID")
        public int getStudentId() {
            return studentId;
        }
    
        public void setStudentId(int studentId) {
            this.studentId = studentId;
        }
        @Column(name="STUDENT_FIRST_NAME")
        public String getFirstName() {
            return firstName;
        }
    
        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }
        @Column(name="STUDENT_LAST_NAME")
        public String getLastName() {
            return lastName;
        }
    
        public void setLastName(String lastName) {
            this.lastName = lastName;
        }
        @Column(name="DOB")
        public Date getDob() {
            return dob;
        }
    
        public void setDob(Date dob) {
            this.dob = dob;
        }
        @Column(name="SEX")
        public String getSex() {
            return sex;
        }
    
        public void setSex(String sex) {
            this.sex = sex;
        }
        @Column(name="STATUS")
        public String getStatus() {
            return status;
        }
    
        public void setStatus(String status) {
            this.status = status;
        }
        @Column(name="DOJ")
        public Date getDoj() {
            return doj;
        }
    
        public void setDoj(Date doj) {
            this.doj = doj;
        }
    
        @Column(name="DEPT_ID")
        public int getDeptId() {
            return deptId;
        }
    
        public void setDeptId(int deptId) {
            this.deptId = deptId;
        }
        @Column(name="DEPT_NAME")
        public String getDeptName() {
            return deptName;
        }
    
        public void setDeptName(String deptName) {
            this.deptName = deptName;
        }
        @Column(name="BATCH_ID")
        public int getBatchId() {
            return batchId;
        }
    
        public void setBatchId(int batchId) {
            this.batchId = batchId;
        }
        @Column(name="BATCH_NAME")
        public String getBatchName() {
            return batchName;
        }
    
        public void setBatchName(String batchName) {
            this.batchName = batchName;
        }
        @Column(name="ROLE_ID")
        public int getRoleId() {
            return roleId;
        }
    
        public void setRoleId(int roleId) {
            this.roleId = roleId;
        }
        @Column(name="ROLE_NAME")
        public String getRoleName() {
            return roleName;
        }
    
        public void setRoleName(String roleName) {
            this.roleName = roleName;
        }
    
        @Column(name="REG_NO")
        public String getRegNo() {
            return regNo;
        }
    
        public void setRegNo(String regNo) {
            this.regNo = regNo;
        }
    
    
    }
    

    Student DTO

    public class StudentDTO {
    
        private int studentId;
        private String firstName;
        private String lastName;
        private Date dob;
        private String sex;
        private String status;
        private Date doj;
        private int deptId;
        private String deptName;
        private int batchId;
        private String batchName;
        private int roleId;
        private String roleName;
        boolean select;
        private String regNo;
    
        public StudentDTO(){
    
        }
    
    
    
        public StudentDTO(int studentId, String firstName, String lastName,
                Date dob, String sex, String status, Date doj, int deptId,
                String deptName, int batchId, String batchName, int roleId,
                String roleName, boolean select, String regNo) {
            super();
            this.studentId = studentId;
            this.firstName = firstName;
            this.lastName = lastName;
            this.dob = dob;
            this.sex = sex;
            this.status = status;
            this.doj = doj;
            this.deptId = deptId;
            this.deptName = deptName;
            this.batchId = batchId;
            this.batchName = batchName;
            this.roleId = roleId;
            this.roleName = roleName;
            this.select = select;
            this.regNo = regNo;
        }
    
    
    
        public int getStudentId() {
            return studentId;
        }
    
        public void setStudentId(int studentId) {
            this.studentId = studentId;
        }
    
        public String getFirstName() {
            return firstName;
        }
    
        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }
    
        public String getLastName() {
            return lastName;
        }
    
        public void setLastName(String lastName) {
            this.lastName = lastName;
        }
    
        public Date getDob() {
            return dob;
        }
    
        public void setDob(Date dob) {
            this.dob = dob;
        }
    
        public String getSex() {
            return sex;
        }
    
        public void setSex(String sex) {
            this.sex = sex;
        }
    
        public String getStatus() {
            return status;
        }
    
        public void setStatus(String status) {
            this.status = status;
        }
    
        public Date getDoj() {
            return doj;
        }
    
        public void setDoj(Date doj) {
            this.doj = doj;
        }
    
        public int getDeptId() {
            return deptId;
        }
    
        public void setDeptId(int deptId) {
            this.deptId = deptId;
        }
    
        public String getDeptName() {
            return deptName;
        }
    
        public void setDeptName(String deptName) {
            this.deptName = deptName;
        }
    
        public int getBatchId() {
            return batchId;
        }
    
        public void setBatchId(int batchId) {
            this.batchId = batchId;
        }
    
        public String getBatchName() {
            return batchName;
        }
    
        public void setBatchName(String batchName) {
            this.batchName = batchName;
        }
    
        public int getRoleId() {
            return roleId;
        }
    
        public void setRoleId(int roleId) {
            this.roleId = roleId;
        }
    
        public String getRoleName() {
            return roleName;
        }
    
        public void setRoleName(String roleName) {
            this.roleName = roleName;
        }
    
        public boolean isSelect() {
            return select;
        }
    
        public void setSelect(boolean select) {
            this.select = select;
        }
    
        public String getRegNo() {
            return regNo;
        }
    
        public void setRegNo(String regNo) {
            this.regNo = regNo;
        }
    
    
    }
    

    Here I am adding 3 Student Objects

        public List<StudentDTO> addStudentToList(){
            List<StudentDTO> studentList = new ArrayList<StudentDTO>();
            StudentDTO stud = new StudentDTO();
            for(int i=0; i<3; i++){
                studentList.add(stud);
            }
    
            return studentList;
    
        }
    
    Student Controller class
    
        @RequestMapping(value="/addStudent", method=RequestMethod.GET)
        public ModelAndView getStudentForm(ModelMap model)
        {   List<StudentDTO> studentList = studentService.getStudentAttributesList();
            //List<Integer> userIdForDropDown = userDAO.getAllUserIdForDropDown();
            //model.addAttribute("userIdDropDown",userIdForDropDown);
    
            List<String> deptList = configDAO.getDeptListForDropDown();
            model.addAttribute("deptlist",deptList);
            List<String> batchList = configDAO.getAllBatchForDropDrown();
            model.addAttribute("batchList", batchList);
            ModelAndView mav = new ModelAndView("add_student");
            Student stu = new Student();
            mav.getModelMap().put("add_student", stu);
            StudentForm studentForm = new StudentForm();
            studentForm.setStudentList(studentList);
            model.addAttribute("studentForm",studentForm);
            return mav;
        }
    
    
    @RequestMapping(value="/addStudent", method=RequestMethod.POST)
        public String saveStudent(@ModelAttribute("add_student") StudentForm studenfForm, BindingResult result, SessionStatus status, ModelMap model) throws ParseException{
            /*if(result.hasErrors()){
                return "add_student";
            }*/
            List<StudentDTO> newList = (List<StudentDTO>) studenfForm.getStudentList();
            List<Student> newList1 = new ArrayList<Student>();
            for(StudentDTO stud:studenfForm.getStudentList()){
                Student student = new Student();
                student.setBatchId(stud.getBatchId());
                student.setBatchName(stud.getBatchName());
                student.setDeptId(stud.getDeptId());
                student.setDeptName(stud.getDeptName());
    
                SimpleDateFormat sdf = new SimpleDateFormat("DD/MM/YYYY");      
                Date dateWithoutTime = sdf.parse(sdf.format(new Date()));
    
                student.setDob(stud.getDob());
                student.setDoj(stud.getDoj());
                student.setFirstName(stud.getFirstName());
                student.setLastName(stud.getLastName());
                student.setRegNo(stud.getRegNo());
                student.setRoleId(stud.getRoleId());
                student.setRoleName(stud.getRoleName());
                student.setStatus(stud.getStatus());
                student.setSex(stud.getSex());
                student.setStudentId(stud.getStudentId());
                newList1.add(student);
            }
            Integer saveStatus = studentDAO.saveStudentInfo(newList1);
            //Integer res = roleDAO.saveRole(role);
            if(saveStatus!=null){
                status.setComplete();
                model.addAttribute("savedMsg", "Student record saved Successfully.");
    
            }
            return "redirect:addStudent";
    
        }
    

    See my jsp page

    <table  bgcolor="white" width="1200" height="300" align="center" style="border-collapse: collapse;" border="1" bordercolor="#006699" >
        <form:form action="addStudent" method="post" commandName="add_student" modelAttribute="studentForm">
        <tr>
            <td align="center" style="background-color: lightblue"><h3>Add Student</h3></td>
        </tr>
        <tr align="left">
                    <td align="left">
                    <input type="button" id="addrows" name="addrows" class="addperson" value="Add Rows">
    
    
    
                    <input type="button" id="removerows" class="removerows" value="Delete Rows" />  
    
                    <input type="submit" value="Save" />
                    </td>       
                    </tr>
        <tr valign="middle" align="center">
    
        <td align="center" valign="middle">
    
    
    
                <table width="1200" height="200" style="border-collapse: collapse;" border="0" bordercolor="#006699" cellspacing="0" cellpadding="0">
    
                    <thead>
    
                    <tr height="1" bgcolor="lightblue">
                        <th colspan="1">
                            No
                        </th>
                        <th width="5">
                            Select
                        </th>
                        <th>
                            Reg No
                        </th>
                        <th>
                            First Name
                        </th>
    
                        <th>
                            Last Name
                        </th>
    
                        <th>
                            Sex
                        </th>
    
                        <th>
                            DOB
                        </th>
    
                        <th>
                            DOJ
                        </th>
    
                        <th>
                            Dept Name
                        </th>
    
                        <th>
                            Role Name
                        </th>
                        <th>
                            Batch Name
                        </th>
    
                        <th>
                            Status
                        </th>
    
                    </tr>
                    </thead>
                    <tbody>
                    <c:forEach var="rows" items="${studentForm.studentList}" varStatus="status">
                    <tr class="${status.index % 2 == 0 ? 'even' : 'odd'}"  >
    
                        <td width="15">
                            <b>${status.count}</b>
                        </td>
    
                        <td width="10">
                            <form:checkbox path="studentList[${status.index}].select"/>
                        </td>
    
                        <td><form:input  path="studentList[${status.index}].regNo"/></td>
    
                        <td><form:input  path="studentList[${status.index}].firstName"/></td>
    
    
                        <td><form:input  path="studentList[${status.index}].lastName"/></td>
    
    
                        <td><form:select  path="studentList[${status.index}].sex">
                            <form:option value="NONE" label="--- Select ---"/>
                            <form:option value="M" label="Male"/>
                            <form:option value="F" label="Female"/>
                            </form:select></td>
    
    
                        <td><form:input  path="studentList[${status.index}].dob"/></td>
    
    
                        <td><form:input  path="studentList[${status.index}].doj"/></td>
    
    
                        <td><form:select  path="studentList[${status.index}].deptName">
                            <form:option value="NONE" label="--- Select ---"/>
                            <form:options items="${deptlist}" />
                            </form:select></td>
    
    
                        <td><form:select  path="studentList[${status.index}].roleName">
                            <form:option value="NONE" label="--- Select ---"/>
                            <form:option value="ROLE_STUDENT" label="Student"/>
                            <form:option value="ROLE_BATCHREP" label="Batch Rep"/>
                            </form:select></td>
    
    
                        <td><form:select  path="studentList[${status.index}].batchName">
                            <form:option value="NONE" label="--- Select ---"/>
                            <form:options items="${batchList}" />
                            </form:select>
    
                        </td>
    
    
                        <td><form:select  path="studentList[${status.index}].status">
                            <form:option value="NONE" label="--- Select ---"/>
                            <form:option value="E" label="Enable"/>
                            <form:option value="D" label="Disable"/>
                            </form:select></td>
    
                    </tr>
                        </c:forEach>
    
                            </tbody>
                </table>
    
                </td>
                </tr>
                <tr align="center">
                            <td width="100" align="center"><B>
                            ${savedMsg}
                            </B>
                            </td>
                            </tr>   
    
    
            </form:form>
    
    </table>
    
  • PraveenKumar Lalasangi
    PraveenKumar Lalasangi over 4 years
    @Sandeep This question may be older, you could have solved it yourself in any of the way, But as you have done a notable question with 10k views i considered to answer.