Thymeleaf: populating checkboxes from an Array - spring-mvc

I have a Spring MVC application using Thymeleaf for templating. I am using enums to generate checkboxes dynamically. So if my enum file has 3 values it will generate 3 checkboxes:
My enum file:
public enum Foods {
PIZZA("Pizza"),
PASTA("Pasta"),
MAC_CHEESE("Mac and Cheese"),
ICE_CREAM("Ice Cream"),
BURGER("Burger"),
private String type;
Foods(String type) {
this.type = type;
}
public String getType() {
return this.type;
}
}
This is my checkbox generation:
<label for="decision">What is your favorite food?</label>
<div id="decision" class="row" style="margin-top:1%;">
<div class="col-md-4" th:each="option : ${T(in.app.model.enums.Foods).values()}">
<div class="checkbox checkbox-custom checkbox-circle">
<input name="decision" type="checkbox" th:id="${option.toString()}" th:value="${option}" />
<label th:for="${option.toString()}" th:text="${option.type}"></label>
</div>
</div>
</div>
This code will generate 5 checkboxes for each of the food type. All works till here. The issue I am facing is how to set the checked attribute when reading a saved record.
I am getting back an object via the model view controller. The object has a food property with its value as the array of the chosen food types.
user = {
.
.
food : ["PIZZA", "BURGER", "PASTA"],
.
.
}
Now I want to loop through this array and if the value match then set the checkbox.
I am trying to do something like this:
<label for="decision">What is your favorite food?</label>
<div id="decision" class="row" style="margin-top:1%;">
<div class="col-md-4" th:each="option : ${T(in.app.model.enums.Foods).values()}">
<div class="checkbox checkbox-custom checkbox-circle">
<input
name="decision"
type="checkbox"
th:id="${option.toString()}"
th:value="${option}"
th:each="food : ${user.food}"
th:attr="checked = ${food} == ${option} ? 'checked'"
/>
<label th:for="${option.toString()}" th:text="${option.type}"></label>
</div>
</div>
</div>
I know its wrong (since its not working) but I am unable to figure out how to loop over two arrays to show the checkboxes and to check them.

You might want to try using th:checked instead of th:attr if you can, so:
th:checked="${food == option.type}"
This post might also be helpful when looking into that. If you can't use th:checked, switching to the below statement should also work.
th:attr="checked=${food == option.type} ? 'checked'"
It also seems like you may run into some issues with checking this data due to case sensitivity while comparing, in which case this post might be helpful.

The safe option is to go with
th:attr
and do compare like #Artichoke
th:attr="checked=${food == option.type} ? 'checked'"
The problem with "th:checked" is, its simply do not go well when you need to post the data unless you change the value. You see its value as null if you do not switch it.

Related

ASP.NET CORE MVC CRUD operation. Create form doesn't work anymore

Im am currently working on my CRUD operations for the website. On the attached pictures you can see that im using the create form for 2 purposes. 1. to create a user and 2 to edit a user. My Query is made well but when im adding a hidden id to get a certain user for editing. I cannot press create user anymore because i passed the piece of code in at picture 1. (the line i selected)
But when i remove this piece of code i can create a user but when im trying to edit a user when removing this piece of code it just edits the user and then creates a new one with the edited content. Does anyone know how to fix this issue?
p.s. i made screenshots of the controller and the piece of code where it goes wrong.
Kind regards,
Sem
[Piece of code where it goes wrong][1] [User controller][2] [User
controller][3] [Data transfer objects][4] [CreateOrUpdate
query/function][5]
[1]: https://i.stack.imgur.com/5XxDe.jpg [2]:
https://i.stack.imgur.com/s5pqh.jpg [3]:
https://i.stack.imgur.com/2vLXM.jpg [4]:
https://i.stack.imgur.com/R2OBn.jpg [5]:
https://i.stack.imgur.com/ADyv5.jpg
As per your screenshot you are calling same action method from UI, in that case you need to validate id in your action method to see if it valid then its update otherwise it will be add. Scenario first time your page loaded id will be empty and second time if you load then it will have id and same you can use for edit.
Hope this solve your problem 👍
First, you can copy your code using '{}' in above toolbar, so it will be more convenient for others to test your code.
but when im trying to edit a user when removing this piece of code it just edits the user and then creates a new one with the edited content.
You can check the database for the passed model, if id exists, create one, but if not, remove the old one and create this new one.
Here is a demo with linq, you can check the logic and modify my code to yours:
public async Task<IActionResult> ProcessCreate(UsersDto user)
{
if (!_context.UsersDtos.Any(x => x.Id == user.Id)) //if not exist, create one directly
{
_context.Add(user);
await _context.SaveChangesAsync();
}
else { //else, delete old one and create new one
var therow = _context.UsersDtos.Where(x => x.Id == user.Id).FirstOrDefault();
_context.Remove(therow);
_context.Add(user);
await _context.SaveChangesAsync();
}
return RedirectToAction("Index");
}
In Create.cshtml,you can show the id:
<form method="post" asp-action="ProcessCreate">
<div class="form-group" hidden>
<label asp-for="Id" class="control-label"></label>
<input asp-for="Id" class="form-control" />
<span asp-validation-for="Id" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="First_name" class="control-label"></label>
<input asp-for="First_name" class="form-control" />
<span asp-validation-for="First_name" class="text-danger"></span>
</div>
//.......
<div class="form-group">
<input type="submit" value="CreateOrEdit" class="btn btn-primary" />
</div>
Result:

Cannot upload an image

I'm trying to update an image to my database, I defined as property model (bounded by database) the following:
public byte[] AvatarImage { get; set; }
then I created another property which store the value in the ViewModel:
public IFormFile AvatarImage { get; set; }
this steps are also described here in the doc.
Iside my form, I added the following html:
<div class="form-group text-center col-lg-12">
<img src="#Model.AvatarImage" class="avatar img-circle" alt="avatar" />
<h6>#Localizer["UploadNewAvatar"] ...</h6>
<input type="file" class="form-control" id="avatarUrl" asp-for="#Model.AvatarImages" />
</div>
when I submit the form the property AvatarImage is even null. But I don't understand why happen this, because all the other form properties are valorized correctly
Sounds like you are missing the form enctype.
Make sure you have:
<form enctype="multipart/form-data">
... inputs
<form>
Your <input type="file"> element assignment below seems to be wrong, because it uses #Model directive which outputs value of AvatarImages property (and the property is not exist in viewmodel class):
<input type="file" class="form-control" id="avatarUrl" asp-for="#Model.AvatarImages" />
The correct way is just using the property name like example below, because asp-for="PropertyName" is equivalent to model => model.PropertyName in HTML helper (assumed you have #model directive set to a viewmodel class):
<input type="file" class="form-control" asp-for="AvatarImage" />
Also don't forget to specify enctype="multipart/form-data" attribute in <form> tag helper:
<form asp-controller="ControllerName" asp-action="ActionName" method="post" enctype="multipart/form-data">
<!-- form contents here -->
</form>
Reference: Tag Helpers in forms in ASP.NET Core
First add enctype="multipart/form-data" to form ;
Then,check your #model, two situations :
1.Use Model directly, since the image is a byte array type, you need to convert the file type to byte[] during the submission process.
2.Or you could use ViewModel, and change the parameter type to viewmodel in the method.

What is the best way to validate input (Skeleton + Vue.js)?

I realy like this tools, because of their simplicity and compactness. But sometimes I face lack of plugins / snippets (my JS skills also pretty weak, usually I'm backend developer), so here's one of those cases:
For example, I have simple form like this one:
<div class="row">
<div class="six columns">
<label for="email">Contact email*</label>
<input v-model="email" class="u-full-width" type="email" id="email" placeholder="admin#exchange.com" name="email">
</div>
<div class="six columns">
<label for="date">Launch date*</label>
<input v-model="date" class="u-full-width" type="text" id="date" placeholder="June, 2014">
</div>
</div>
As you can see, I want to make this fields required, email input should be in email format, something like ***#***.***, date field can be anything.
What is the best way to realize it? Also I've found 3 Vue plugins, who's your favorite?
vue-validator
vee-validate
vue-form
Thanks for any examples/snippets/etc
Since you come from backend and are not expert with JS ( neither am I :D ) I suggest you do it yourself. You will learn more.
This is how I would do it:
<input name="email" v-model="email" #keyup="validateEmail()" />
... vue component or instance ...
data: function() { // if you are doing this on Vue instance and not component then data property will look differently but if in component it has to be a function that returns an object
return {
email: ""
}
},
methods: {
validateEmail: function() {
console.log(this.email)
// here you have access to email input as it changes so you can use either regex or substring or some other string manipulation to determine if the string satisfies your criteria
}

How to get reference to json key name inside {{#with}} helper?

I want to draw fields using a Handlebars partial that have the key name and value. My json is like this:
{ "someKnownField" : { "textValue" : "the value I want" } }
I want a label with someKnownField as its text, and an input with the value of textValue in it. I use a partial because I have hundreds of these fields and don't want to have to hard code their names.
Here's my partial code, called textfield
<div class="control-group">
<label class="control-label" for="input{{#key}}">{{#key}}</label>
<div class="controls">
<input type="text" id="input{{#index}}" placeholder="{{#key}}" value="{{textValue}}">
</div>
</div>
Now, I can't use a {{#with}} helper, a-la {{#with someKnownField}}{{> textfield}}{{/with}} since a with doesn't give you a #key. {{#each}} has a #key but its of the context within the each node (textValue); So how do you key the key name OF the each node itself?
This does not work, but demonstrates what I need to grab:
<label>{{../#key}}</label>
since it's expecting an id in the parent path, not a calculated value (which doesn't exist anyway, since it's not an array itself).
You've done well, but made a typo. The correct way:
<label>{{#../key}}</label>

More than one form in one view. Spring web flow + displaytag + checkbox

I have a table, using display tag, in my application, that is using spring web flow. I would like to have a check box in each row, a button that allows me to select/uselect all and a button to execute a function. After clicking the button, the action will perform some database actions and the page should be render, so we can see these changes.
I don´t know which could be the best option, submitting the whole table
<form method="POST" (more params)>
<display:table id="row">
....
</display:table>
</form>
Or only the checkbox column. I this case I wouldn´t know how to implement it.
I have tryed two different approaches:
1. Using a simple input text, checkbox type. This is not possible, because when I submit the form, I need to set a path to another page.jsp (I am working with flows). Besides, I wouldn´t know how to send these values to java backend.
Using spring tags.
In this case, the problem comes whith the class conversationAction
I found some examples, but allways using MVC and controller cases.
How could I implement this issue??
EDIT
I have found a kind of solution, but I faced a new problem...
flow.xml
var name="model1" class="com.project.Model1"/>
var name="model2" class="com.project.Model2"/>
view-state id="overview" model="formAggregation">
...
</view-state>
page.jsp
form:form modelAttribute="formAggregation.model1" id="overviewForm">
...
/form:form>
...
form:form method="POST" modelAttribute="formAggregation.model2">
display:table id="row" name="displayTagValueList" requestURI="overview?_eventId=tableAction">
display:column title="">
form:checkbox path="conversationIds" value="${row.threadId}"/>
/display:column>
/display:table>
input type="submit" name="_eventId_oneFunction" value="Send>>"/>
/form:form>
FormAggregation.java
#Component("formAggregation")
public class FormAggregation {
private Model1 model1;
private Model2 model2;
//Getters and setters
I need this aggregator, because I need both models. I have tested it one by one and it is working as wished. Any idea about that??
Thanks!!
I couldn´t find a solution to add two model in a view-state. So I made a workaround, adding the fields I needed to the model I was using, com.project.Model1. So the result is:
page.jsp
<form:form method="POST" id="tableForm" modelAttribute="model1">
<display:table id="row">
<display:column title="">
<form:checkbox path="chosenIds" value="${row.id}"/>
</display:column>
<display:footer>
<div class="tableFooter" >
<input type="submit" name="_eventId_workIds" value="Send"/>
</div>
</display:footer>
</display:table>
</form:form>
flow.xml
<var name="model1" class="com.project.Model1"/>
...
<transition on="workIds" to="overview" validate="false">
<evaluate expression="actionBean.workIds(model1.chosenIds)" />
</transition>
java class
public void workIds(List<Long> ids) {
Hope it helps

Resources