I'm working on a website in which users can add and edit things (it's not relevant describing what these things are in particular).
I'm implementing it using Thymeleaf for the frontend, Spring MVC for the backend and JPA for the database logic. Now I'm trying to implement the edit logic but I don't know which is the best way to do it.
What I'm thinking is: display to the user all the input fields (on a HTML page) that he is allowed to edit, already filled in with the current values. He can edit then whatever fields he wants and finally press the edit button to persist the changes.
Once I get the new object in the backend, I retrieve the old version from the database in order to check which field the user changed. For every field that got changed I update the old version and only when I've finished I call the JPA method save and I persist the new version of that object.
Is there a better way to do it?
It would be perfect if the object I put inside the model in order to display all its field to the user inside the HTML page, could mantain all the information of the old object and not just the ones the user can change. Let me explain better what I'm trying to say with an example:
Let's say the object we are trying to edit its called Person and has these attributes:
id
name
surname
money
nickname
sex
But the user can edit only the following attributes:
money
nickname
sex
so the controller which handler the get request of the page would look like this:
#GetMapping("person/{personId}/edit")
public String getEditPersonPage(#PathVariable Integer personId, Model model) {
Person person = personService.getById(personId); //person has all the attributes filled in
model.addAttribute("person", person);
and the controller which handler the put request looks like this:
#PutMapping("person/{personId}/edit")
public String editPerson(#ModelAttribute Person person, #PathVariable Integer personId){
personService.editPerson(person); //person has only the three fields filled in and all the other attributes as NULL
return "redirect:/person/" + personId;
}
the HTML page:
<form th:object="${person}" th:method="PUT">
<fieldset>
<p th:text="*{id}"></p>
<p th:text="*{name}"></p>
<p th:text="*{surname}"></p>
<input type="number" th:field="*{money}" th:value="*{money}" />
<input type="text" th:field="*{nickname}" th:value="*{nickname}" />
<input type="text" th:field="*{sex}" th:value="*{sex}" />
<input type="submit" value="Edit" />
</fieldset>
</form>
When I insert the object person inside the model to render the HTML page the object has all the attributes. Indeed I can decide which of its attributes display inside the HTML page (in this case only three: money, nickname and sex). But when the user press the submit button (edit), I receive only the fields displayed inside the HTML page (money, nickname, sex). So what I have to do is: I have to take these three fields, check if they are changed and if so, update the old version.
It would be perfect if when the user press the submit button, all the fields of person (and not just those three he is allowed to update) could be retrieved by the controller. In that case I could skip the check phase and persist directly the new version inside the database (with the old values unchanged and not set to NULL).
Any thoughts?
You can use readonly attributes for the ones that are not going to be edited.
I think is the best way in order to keep all the values visible in the form to give all the info to the user that is editing. All the values will be submitted and serialized with your object, but the form just allows to edit the three of them that you need.
A readonly element is just not editable, but gets sent when the according form submits. A disabled element isn't editable and isn't sent on submit.
Related
So I'm a little unclear on how to properly use #PathVariable in the context I'm trying to here. Let me explain.
I have a JSP page with a table. The table contains all the records in a table in my database. That all works fine.
At the bottom of the table, there are buttons. Add, Edit, Delete. They are all using separate forms because I want to have separate URL paths/ HTTP Methods for each.
To accomplish this, I'm using JQuery so that when I hit submit on the form, it retrieves the ID value of the currently selected record. All of that works perfectly, and is not what I'm asking about.
Here is the form where the submit action is taking place:
<form:form id="editForm" action="./${courseId}.html" path="courseId" method="get">
<input type="hidden" id="editCourseId" name="courseId"/>
<input class="btn btn-lg btn-default btn-shadow" type="submit"
value="Edit"/>
</form:form>
So, what I want is for this form to call a URL with courseId as the #PathVariable value. The value of courseId is contained in the hidden input field with the name courseId. Again, that input field's value is set by JQuery code, and I've already tested that and it seems to be working perfectly.
What I want is to use courseId as the #PathVariable, and have it work in the following controller method:
#RequestMapping (value="/{courseId}", method=RequestMethod.GET)
public String editCourse(Model model,
#PathVariable ("courseId") String courseId){
System.out.println("CourseID: " + courseId);
return "course-form";
}
Obviously that method is still in the early stages, just trying to get confirmation that it works.
So, what do I have to do to make this happen? Again, I want to pass the value of courseId in the URL, as a RESTful style URL command, using it as a #PathVariable. How do I do this?
Thanks so much.
You can get a very clear explanation here
#RequestMapping (path="/{courseId}", method=RequestMethod.GET)
public String editCourse(Model model,
#PathVariable String courseId){
System.out.println("CourseID: " + courseId);
return "course-form";
}
I'm creating a form to allow users to edit my objects, but it seems that only the object fields that are editable in the form are available within my controller once the form is submitted. My example is a user object that I present in a form to allow the user to edit the name field, when they submit the form my controller needs to determine if this is a new user or an exiting user. To determine this I check the ID field, but since the ID field is not editable on the form it is null, when I check it in the controller.
Have I missed something?
For this scenario, keep your id has hidden input in template. So that it will be available for you in controller after user hitting form's submit button.
<input type="hidden" name="id" value='#userForm.field("id").value' >
After thinking a lot, this is the best question title I could find. Not that representative, I'm sorry :-(.
By the way let me explain the problem.
I have to implement an administration panel. It displays a table where every line contains a user with his roles and account status. You can see the idea sketched in the following image
now, my problem is about managing the backing model object:
I have to pass a list of users to populate the administration panel but
I have to bind each user to a model object and
I have to submit each user separately.
I found many tips for managing the table as a whole so that a single button submits all the users at the same time, but what I want to do is populating the table with a list and managing every list record (the user) separately.
I thought to manage each line with javascript to keep trace of the modified values and to use them to build a uri like http://authority/app/user_management/{user_id}/{is_locked}/{is_admin}/.../. The uri will be triggered by the corresponding submit button, but I prefer to avoid this approach.
Moreover, to populate the table with the correct checkbox value it is necessary to have a binding enstablished.
Any advice?
Thanks!
I think you can use separate form for each row with a User object as a Model. You will have simple 'updateUser' handler with User object in input parameters list.
As I remember it could be a User you are iterating through in your foreach, but I need to check it with the real example..) At least you can use simple form and pass 'id', 'isLocked', etc. as request parameters.
<!-- Inside foreach loop -->
<form action="/updateUser" method="POST">
<input type="hidden" name="id" value="${user.id}"/>
<tr>
<td><input type="checkbox" name="isAdmin" value="Is Admin" checked="${user.isAdmin}"/></td>
<td><input type="submit" value="Submit"/></td>
</tr>
</form>
And something similar on server side:
#RequestMapping(value = "/updateUser", method = RequestMethod.POST)
#ResponseBody
public ResponseDTO updateUser(#RequestParam String id, #RequstParam boolean isAdmin) {
// your update logic
}
I am developing a registration web app using Struts2 and need some guidance.
Background:
On the registration form, there is a set of five form fields: 1 text box, and 4 drop down select boxes. The five fields describe a person's primary position in an educational setting: the text field allows the user to insert their job title, and the drop down menus allow the user to select what school, institution, department, and division they belong to. The drop down menus are initialized with options that are stored in a database (inside the registration action, array lists are initialized with these values before the form is displayed). For example:
<s:select emptyOption="true" key="school1.schoolId" list="schoolList" listKey="schoolId" listValue="schoolName" required="true" />
Problem:
I need to provide the user with the ability add an X number of secondary positions. On the registration form, a user can click an "add another affiliation" button, and a new set of the 5 form fields are displayed. These fields will also need to be validated, and saved when the user clicks the form's submit button.
What would be the best approach to tackling this problem?
So far, I have only declared array lists for each form field, like so:
private List<String> jobTitles = new ArrayList<String>();
private List<School> schools = new ArrayList<School>();
private List<Institution> institutions = new ArrayList<Institution>();
private List<Department> departments = new ArrayList<Department>();
private List<Division> divisions = new ArrayList<Division>();
But I do not know how to proceed. How do I display the initial 5 fields for the primary position? If I use Javascript to insert new form fields dynamically, how do I initialize the dynamic drop down menus with the options stored in the database? How do I retain these values if the page is reloaded?
Any help is appreciated - thanks!
The basic problem you need to tackle is how to get an indexed list of request parameters into your action class. This is quite simple, and I think you are on the right track by starting off by creating Lists of input parameters. I found a bit of documentation on the subject here. Basically you can have form fields with names like jobTitles[0], jobTitles[1] which would be used to populate the jobTitles List.
However, I think the concept of 'Affiliation' deserves a class of it's own:
class UserAffiliation {
private String title;
private String schoolId;
private String institutionId;
private String departmentId;
private String divisionId;
// Make sure that there is a no-args constructor (default or explicit) for Struts to create instances.
// Add getters and setters
}
In your action class:
private List<UserAffiliation> affiliations;
...
// getter and setter for affiliations
Would be enough to capture the user input.
Your jsp could look something like:
<form action=".." method="post">
<div class="affiliation">
<s:textfield name="affiliations[0].title"/>
<s:select name="affiliations[0].schoolId" list="schools" listKey="schoolId" listValue="schoolName"/>
...
</div>
<s:if test="affiliations != null && affiliations.size > 1">
<s:iterator value="affiliations" begin="1" status="status">
<s:textfield name="affiliations[%{#status.index + 1}].title"/>
<s:select name="affiliations[%{#status.index + 1}].schoolId" list="schools" listKey="schoolId" listValue="schoolName"/>
...
</s:iterator>
</s:if>
....
</form>
<div id="affilationTemplate" style="display:none;">
<div class="affiliation">
<s:textfield name="affiliations[__IDX__].title"/>
<s:select name="affiliations[__IDX__].schoolId" list="schools" listKey="schoolId" listValue="schoolName"/>
</div>
...
</div>
Note the div affilationTemplate. You could use JS to get the html of this template, replace __IDX__ with the appropriate index, and append to the form contents when the user clicks on the 'add another affiliation' button. This makes sure that the newly added select boxes are pre-populated with appropriate values.
The iterator block displays what ever the values the user had already submitted (with the exception of the 'primary affiliation', which is already displayed above it).
NOTE: You should of course, try to get rid of the repeated form elements if possible. I would try with extracting them into an include.
I'm using Struts2 to display the contents of a list of objects on a JSP.
The flow of events is as following:
GetDataAction.java -> fetches values from
the database, fills in the ArrayList
named tableList. On success, the
displayData.jsp is shown.
displayData.jsp -> uses the s:iterate tag to display the values of objects
in the tableList.
The user changes some values in the
displayData.jsp and presses on the
Update button. On the click of
Update button, the
UpdateDataAction.java is called.
Now my problem is; How do I use the same tableList in UpdateDataAction.java to get the modified values?
I tried declaring an ArrayList with the same name 'tableList' (along with getters and setters), in UpdateDataAction.java but it throws a NullPointerException.
Please suggest.
IMO the way you are updating is not a good idea.Either you should link every row to a seperate edit page or use ajax.There are many plugins available to update table values using ajax,If you need i can provide you the links
Back to your way of doing it,i guess you are doing it as follows
<s:form action="UpdateDataActionName">
<s:iterator value="tableList">
<s:textfield name="objectName.propertyName1" value="%(propertyName1)">
<s:textfield name="objectName.propertyName2" value="%(propertyName2)">
<s:textfield name="objectName.propertyName3" value="%(propertyName3)">
</s:iterator>
<s:submit value="Update"/>
</s:form>
Now declare a list in your UpdateDataAction,of type <objectNameoftableListType> i.e. the same object type which the tabeList is representing.The name of the list must be objectName.Try to Iteate and check if you are getting the right values as submitted from the jsp.