Mapping MVC to template using thymeleaf - spring-mvc

Teaching myself Spring 5, SpringBoot, and MVC using thymeleaf. It's a simple application. I am working with my controller first to get the view populating correctly before I move to the data access layer.
My issue is the view is not populating with the data that I have created in the Controller.
This is my Controller:
// generates a logger class for you
#Slf4j
#Controller
#RequestMapping("/select")
public class AreaCodeController {
/*
* This method is called BEFORE the #GetMapping method.
* Building a list of items to display on the select template
*/
#ModelAttribute()
public void addAreaToModel(Model model) {
// id, code, country, abbr, provStateLongName, StateCode
List<Area> listing = Arrays.asList(new Area(1000, 123, "US", "AL", "Alabama", StateCode.AL),
new Area(1001, 124, "US", "MS", "Mississippi", StateCode.MS),
new Area(1002, 125, "US", "WA", "Washington", StateCode.WA),
new Area(1003, 126, "US", "WV", "West Virgina", StateCode.WV),
new Area(1004, 127, "US", "GA", "Georgia", StateCode.GA),
new Area(1005, 128, "US", "IL", "Illonis", StateCode.IL),
new Area(1006, 129, "US", "OR", "Oregon", StateCode.OR),
new Area(1007, 121, "US", "CA", "California", StateCode.CA),
new Area(1008, 122, "US", "NV", "Nevada", StateCode.NV),
new Area(1009, 120, "US", "NM", "New Mexico", StateCode.NM),
new Area(1010, 130, "US", "LA", "WildWilly", StateCode.LA));
StateCode[] stateCodes = Area.StateCode.values();
for (StateCode stateCode : stateCodes) {
model.addAttribute("areaCodeList", filterByStateCode(listing, stateCode));
}
}
#GetMapping
public String showSelectForm(Model model) {
model.addAttribute("select", new BusinessNumber());
return "select";
}
private List<Area> filterByStateCode(List<Area> listing, StateCode sc)
{
return listing.stream().filter(x -> x.getCode().equals(sc)).collect(Collectors.toList());
}
}
This is my view:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Virtual Business Number Listing</title>
<link rel="stylesheet" th:href="#{/styles.css}" />
</head>
<body>
<h1>List of Available Business Numbers</h1>
<img th:src="#{/images/phone.png}" style="width:200px;height:125px"/>
<form method="POST" th:object="${select}">
<div class="grid">
<div class="area-group" id="abbrs">
<h3>Choose Your Business Number:</h3>
<div th:each="area : ${areaCodeList}">
<input type="checkbox" name="areaCodeList" th:value="{area.id}" />
<span th:text="${area.code}">Area Code</span><br/>
</div>
</div>
<br/>
<input type="Submit" id="submitButton" th:value="Save">
</div>
</form>
</body>
</html>
This is the behavior I am seeing:
Any suggestions would be greatly appreciated.
Thanks,
Russ

After walking through the code I have solved my own error. As painful as it was, it involved several things:
The filterByStateCode method was using the wrong attribute to filter on. It
was always returning empty.
Adding the the same key for the model attribute was overwriting each entry once the filter method was fixed.
The 'select' template was not keying on the create field.

I think your form tag does not know which action to take from the controller so you need to add it this way :
1- in your view : <form method="POST" th:object="${select}" *th:action="#{/something}"*>
2-in your controller : above your method #GetMapping should be #PostMapping(value="/something") cause of your form has a post method creating a new object select

Related

Spring restTemplate not returning anything

#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
#GetMapping("/printJson")
public String getJson(#RequestParam(name="reports", required=false, defaultValue="None") RestTemplate restTemplate, Model model) throws Exception {
List<DailyReport> reports = restTemplate.getForObject("http://localhost:8080/pwa/services/dailyReport/all",
(Class<List<DailyReport>>) ((Class)List.class));
model.addAttribute("reports", reports);
return "printJson";
}
Hi, I am trying to consume my own webservice with restTemplate. On my webservice I have stored data with JSON and I want to print it or use it with a web application
This is what my json file looks like :
{
"serial_number": 202102,
"report_date":"2019-11-23T23:00:00.000+0000",
"status": "en service",
"automate_information":{
"state": "ok",
"temperature": 20,
"payment_state":{
"coins": "normal",
"smart_card": "normal",
"card": "normal"
}
},
"errors":[
{"type": "ax23", "description": "azerty"},
{"type": "cx400", "description": "qzerty"}
],
"articles":[
{"name": "cafe", "quantity": 20},
{"name": "chocolat", "quantity": 30},
{"name": "oasis", "quantity": 10}
],
"income": 300
}
I have made a matching structure with the same names and all, but when I launch my application and I go on the corresponding view :
<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"
xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>JSON</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<tr th:each="report: ${reports}">
<td th:text="${report.id}" />
<td th:text="${report.serial_number}" />
</tr>
<form th:action="#{/logout}" method="post">
<input type="submit" value="Sign Out"/>
</form>
</body>
</html>
Nothing at all is printed. When I tried to call the .size() on the variable reports, I had the error : Cannot call .size() function on null.
I have searched everywhere, and looked back at my structure to see if the names were exactly alike and didn't find anything.
Is there something wrong I am doing with resttemplate ?
Thank you a lot for your help, I might have done a lot of mistakes I am new to spring.

ASP.NET MVC 5 Form Validation and Error Handling

Trying to implement data validation and error handling on a simple contact form. When I add the check for ModelState.IsValid I'm in a chicken and egg situation. I have looked at other similar questions and am just not getting this. Moving from Web Forms to MVC and struggling. Trying to toggle HTML elements based on what's happening - success/error message, etc. RIght now, not even the validation is working.
Right now I'm just trying to get server-side validation working but would welcome advice on how to add client-side validation also; for example, is it necessary to use jQuery for this or is there something baked in?
View:
#using (Html.BeginForm("Contact", "Home", FormMethod.Post))
{
if (ViewData["Error"] == null && ViewData["Success"] == null)
{
<h3>Send us an email</h3>
Html.ValidationSummary(true);
<div class="form-group">
<label class="sr-only" for="contact-email">Email</label>
<input type="text" name="email" placeholder="Email..."
class="contact-email" id="contact-email">
</div>
<div class="form-group">
<label class="sr-only" for="contact-subject">Subject</label>
<input type="text" name="subject" placeholder="Subject..."
class="contact-subject" id="contact-subject">
</div>
<div class="form-group">
<label class="sr-only" for="contact-message">Message</label>
<textarea name="message" placeholder="Message..."
class="contact-message" id="contact-message"></textarea>
</div>
<button type="submit" class="btn">Send it</button>
<button type="reset" class="btn">Reset</button>
}
else if (ViewData["Error"] == null && ViewData["Success"] != null)
{
<h4>We will get back to you as soon as possible!</h4>
<p>
Thank you for getting in touch with us. If you do not hear
from us within 24 hours, that means we couldn't contact you
at the email provided. In that case, please feel free to call
us at (xxx) xxx-xxxx at any time.
</p>
}
else if (ViewData["Error"] != null)
{
<h3>Oops!</h3>
<p>
We apologize. We seem to be having some problems.
</p>
<p>
Please come back and try again later. Alternatively,
call us anytime at (xxx) xxx-xxxx.
</p>
}
}
Model:
public class ContactModel
{
[Required(ErrorMessage = "Email address is required")]
[EmailAddress(ErrorMessage = "Invalid Email Address")]
public string Email { get; set; }
[Required(ErrorMessage = "Subject is required")]
public string Subject { get; set; }
[Required(ErrorMessage = "Message is required")]
public string Message { get; set; }
}
Controller:
[HttpPost]
public ActionResult Contact(ContactModel contactModel)
{
if (ModelState.IsValid)
{
try
{
MailMessage message = new MailMessage();
using (var smtp = new SmtpClient("mail.mydomain.com"))
{
// Standard mail code here
ViewData["Success"] = "Success";
}
}
catch (Exception)
{
ViewData["Error"]
= "Something went wrong - please try again later.";
return View("Error");
}
}
return View();
}
Error View:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Error</title>
</head>
<body>
<hgroup>
<h1>Error.</h1>
<h2>An error occurred while processing your request.</h2>
</hgroup>
</body>
</html>
UPDATE - 05/09/2017
Per Guruprasad's answer, if ModelState.IsValid evaluates to false, then no validation error messages are being reported on the form.
Note I had to change the AddModelError signature to not use the "Extension ex" parameter:ModelState.AddModelError("Error", "Server side error occurred"); as I do not want system errors being reported to users.
Note also that at this point I am only trying out validation on the server side (have yet to work through client-side validation).
I have updated the Contact.cshtml view as follows as no model errors were being displayed - I have included the Bootstrap .has-error and .help-block CSS rules for the validation errors:
#using (Html.BeginForm("Contact", "Home", FormMethod.Post))
{
<h3>Send us an email</h3>
Html.ValidationSummary(true);
<div class="form-group has-error">
<label class="sr-only" for="contact-email">Email</label>
#Html.TextBoxFor(m => m.Email, new { type = "text", name = "email",
placeholder = "Email..", #class = "contact-email" })
#Html.ValidationMessageFor(model => model.Email, String.Empty,
new { #class="help-block" })
</div>
<div class="form-group has-error">
<label class="sr-only" for="contact-subject">Subject</label>
#Html.TextBoxFor(m => m.Subject, new { type = "text",
name = "subject",
placeholder = "Subject..", #class = "contact-subject" })
#Html.ValidationMessageFor(model => model.Subject, String.Empty,
new { #class = "help-block" })
</div>
<div class="form-group has-error">
<label class="sr-only" for="contact-message">Message</label>
#Html.TextAreaFor(m => m.Message, new { name = "message",
placeholder = "Message..", #class = "contact-message" })
#Html.ValidationMessageFor(model => model.Message, String.Empty,
new { #class = "help-block" })
</div>
<button type="submit" class="btn">Send it</button>
<button type="reset" class="btn">Reset</button>
if (ViewData["Success"] != null)
{
<h4>We will get back to you as soon as possible!</h4>
<p>
Thank you for getting in touch with us. If you do not hear
from us within 24 hours, that means we couldn't contact you
at the email provided. In that case, please feel free to
call us at (xxx) xxx-xxxx at any time.
</p>
}
}
There are multiple things you need to understand here. Let me go point by point.
Its good to know that you have your model designed, but how your view gets to know that it has a model to bind for itself and when posting the form contents, how would server comes to know that, there is a model to be received. So on the first instance, you need to construct your view binding the model. To bind a model in a view, you need to first get a reference/declare it at the top, letting view know that, ok, here is a model for you to generate my view.
Well, you have ValidationSummary to true, then I would suggest that, instead of using ViewData to pass error message, you can use ModelState.AddModelError and let ValidationSummary take care of that. As a side note, you might also want to take care of this issue and you can resolve the same with answers mentioned in the same post. If you are not using or do not want to use Html.ValidationSummary, then you can stick to your current view.
Now, to display Success message, you can either use TempData or ViewData and follow the same structure as you have in your view now. Here is one more post to let you work on that.
Last and most important on View part is binding model properties to View elements. Use Razor View extension helpers to generate View for your model. You have #Html.TextBoxFor,#Html.TextAreaFor etc., You also have #Html.TextBox, #Html.TextArea which is not for binding model properties, but just to generate a plain HTML view. You can add other html properties within these helpers as shown in the updated view below. I would suggest to dig down more on the overloads available for these helpers.
So here is your updated view.
#model SOTestApplication.Models.ContactModel #*getting model reference*#
#using (Html.BeginForm("Contact", "Home", FormMethod.Post))
{
<h3>Send us an email</h3>
Html.ValidationSummary(true);
<div class="form-group">
<label class="sr-only" for="contact-email">Email</label>
#Html.TextBoxFor(m => m.Email, new { type = "text", name = "email", placeholder = "Email..", #class = "contact-email" })
#*Usage of helpers and html attributes*#
</div>
<div class="form-group">
<label class="sr-only" for="contact-subject">Subject</label>
#Html.TextBoxFor(m => m.Subject, new { type = "text", name = "subject", placeholder = "Subject..", #class = "contact-subject" })
</div>
<div class="form-group">
<label class="sr-only" for="contact-message">Message</label>
#Html.TextAreaFor(m => m.Message, new { name = "message", placeholder = "Message..", #class = "contact-message" })
</div>
<button type="submit" class="btn">Send it</button>
<button type="reset" class="btn">Reset</button>
}
if (ViewData["Success"] != null)
{
<h4>We will get back to you as soon as possible!</h4>
<p>
Thank you for getting in touch with us. If you do not hear
from us within 24 hours, that means we couldn't contact you
at the email provided. In that case, please feel free to call
us at (xxx) xxx-xxxx at any time.
</p>
}
Controller Side validation
Not much to say on this part as it looks good. But based on few of my points above, I would suggest you to add ModelState.AddModelError instead of using ViewData for error messages. Eliminate your if conditions in view, so that contact form remains, even after postback. Now if you want to persist the values after server side validation, then just pass back the model to your view in your post method. Updated Controller would be:
[HttpPost]
public ActionResult Contact(ContactModel contactModel)
{
if (ModelState.IsValid)
{
try
{
MailMessage message = new MailMessage();
using (var smtp = new SmtpClient("mail.mydomain.com"))
{
// Standard mail code here
ViewData["Success"] = "Success";
}
}
catch (Exception)
{
ModelState.AddModelError("Server side error occurred", ex.Message.ToString());
}
}
return View(contactModel); //this will persist user entered data on validation failure
}
Client Side Validation
As far as this portion is considered, you have few more things to set up in your application.
You need to add Html.EnableClientValidation(true); and Html.EnableUnobtrusiveJavaScript(true); to your application. There are various possible ways to add this. You can add this on Web.config file under appSettings for global implication Or you can add this in particular view as mentioned in below updated View example.
Global Implication in Web.Config ex:
<appSettings>
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
</appSettings>
If you have noticed your BundleConfig.cs file under App_Start directory, you would have seen below entries created by default. These are the jquery stuffs responsible for your Client Side validation.
jQuery and jQueryVal entries
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.unobtrusive*",
"~/Scripts/jquery.validate*"));
Next Step is to add reference to these files/use #section Scripts to render these bundles either in _Layout.cshtml or in any specific view. When you include this in _Layout.cshtml. these scripts/bundles are rendered wherever you use this layout with other views. So basically, its your call on where to render these.
For example here, I would render these in Contact.cshtml view soon after adding reference to model.
#section Scripts
{
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/jqueryval")
}
One Last thing to make this work here is that you need to use #Html.ValidationMessageFor razor extension and let MVC do the binding of error messages on particular properties. Also for these error messages to be displayed in the View, you need to specify ErrorMessage for each property in your model as you are doing it now with Required(ErrorMessage=... for each properties in model. There are more to know about these stuffs if you explore it in detail.
Your updated view with proper validations added.
#model SOTestApplication.Models.ContactModel
#section Scripts
{
#Scripts.Render("~/bundles/jquery")
#Scripts.Render("~/bundles/jqueryval")
}
#using (Html.BeginForm("Contact", "Contacts", FormMethod.Post))
{
<h3>Send us an email</h3>
Html.ValidationSummary(true);
Html.EnableClientValidation(true);
Html.EnableUnobtrusiveJavaScript(true);
<div class="form-group">
<label class="sr-only" for="contact-email">Email</label>
#Html.TextBoxFor(m => m.Email, new { type = "text", name = "email", placeholder = "Email..", #class = "contact-email" })
#Html.ValidationMessageFor(m => m.Email)
</div>
<div class="form-group">
<label class="sr-only" for="contact-subject">Subject</label>
#Html.TextBoxFor(m => m.Subject, new { type = "text", name = "subject", placeholder = "Subject..", #class = "contact-subject" })
#Html.ValidationMessageFor(m => m.Subject)
</div>
<div class="form-group">
<label class="sr-only" for="contact-message">Message</label>
#Html.TextAreaFor(m => m.Message, new { name = "message", placeholder = "Message..", #class = "contact-message" })
#Html.ValidationMessageFor(m => m.Message)
</div>
<button type="submit" class="btn">Send it</button>
<button type="reset" class="btn">Reset</button>
if (ViewData["Success"] != null)
{
<h4>We will get back to you as soon as possible!</h4>
<p>
Thank you for getting in touch with us. If you do not hear
from us within 24 hours, that means we couldn't contact you
at the email provided. In that case, please feel free to call
us at (xxx) xxx-xxxx at any time.
</p>
}
}
Hope I have clarified most of your doubts with these points. Happy Coding.. :)

Does angular ui select (bootstrap version) support optgroup?

Does anyone know if Angular-Ui-Select Bootstrap version support an optgroup?
Can't seem to find any documentation for that on https://github.com/angular-ui/ui-select?
Here is their example:
plnkr.co/edit/QCwSM75ilH2Vh6D9aMA4?p=preview
How to add an optgroup?
In this example, lets say, group persons by countries.
You can use group-by attribute.
See "Demo Multiselect" (last example "Array of objects (with groupBy)") at
https://github.com/angular-ui/ui-select
It's multiselect demo, but group-by works for single select too.
This is group-by using string
app.js:
$scope.countries = [
{
"code": "AD",
"name": "Andorra",
"continent": "Europe"
},
{
"code": "AE",
"name": "United Arab Emirates",
"continent": "Asia"
},
{
"code": "AF",
"name": "Afghanistan",
"continent": "Asia"
}
];
html:
<div>
<label>COUNTRY</label><br>
<ui-select ng-model="user.country" style="min-width: 300px;">
<ui-select-match placeholder="Select Country">
<span ng-bind="$select.selected.name"></span>
</ui-select-match>
<ui-select-choices repeat="country in countries | filter: {name: $select.search}" group-by="'continent'">
<span ng-bind="country.name"></span>
</ui-select-choices>
</ui-select>
</div>
Generated JSON of all countries with their continent using
http://peric.github.io/GetCountries/

WinJS Two way Binding returning undefined

I am new to window8 development, I am basically trying to implement this link
http://msdn.microsoft.com/en-us/magazine/jj651576.aspx
I am using the view model as in figure8 in the link example, but I am unable to display the data, it shows the undefine, but if I only give one element of array I am able to bind it.
My UI is
<body>
<section aria-label="Main content" role="main">
<!-- display each person -->
<div id="nameLabel">Name</div>
<input id="name" readonly="true" type="text" data-win-bind="value: name" />
<div id="ageLabel">Age</div>
<input id="age" readonly="true" type="text" data-win-bind="value: age" />
<div id="colorLabel">Favorite Color</div>
<div id="color" data-win-bind="style.backgroundColor:favoriteColor"></div>
<div id="buttons">
<button id="previousButton"></button>
<button id="birthdayButton"></button>
<button id="nextButton"></button>
</div>
</section>
</body>
and the JavaScript contains
var people = [
// Notify binding listeners when these objects change
WinJS.Binding.as({ name: "John", age: 18, favoriteColor: "red" }),
WinJS.Binding.as({ name: "Tom", age: 16, favoriteColor: "green" }),
WinJS.Binding.as({ name: "Chris", age: 42, favoriteColor: "blue" }),
];
// Bind the current person to the HTML elements in the section
var section = document.querySelector("section[role=main]");
var current = 0;
var viewModel = WinJS.Binding.as({ person: people[current+1] });
WinJS.Binding.processAll(section, viewModel);
nextButton.onclick = function () {
current = (people.length + current + 1) % people.length;
viewModel.person = people[current];
};
This is the result:
Please help me to bind the UI with data model. Thanks in advance.
The problem happens because you've double wrapped a person with a WinJS.Binding. When you did that, you need to change the property path to:
data-win-bind="value: person.name"
When you created the viewModel property, it created a new property containing the actual person instance:
var viewModel = WinJS.Binding.as({ person: people[current+1] });
Also, note that there isn't two-way binding in WinJs.

Bullet behaving like soft hyphen [duplicate]

This question already has answers here:
Add a pipe separator after items in an unordered list unless that item is the last on a line
(11 answers)
Closed 9 years ago.
I want to create list of inline items, separated by bullets, but I want to hide all bullets on the end of line. Example:
France • Germany • Greece
Hungary • Iceland • Ireland
(As you can see, there's no bullet between Greece and Hungary).
Now, the problem is, I need this to work in any viewport width. The text should wrap naturally and bullets should appear only when they are not at the line break.
This is somehow similar to soft hyphen (­) behaviour.
Is there any method to achieve this? Maybe some kind of unicode control character..?
Thanks!
You Just need to write simple css code:
.someclass{
list-style:none;
}
add class in your
<ui>
<li class="someclass">
</li>
</ul>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Display Progress</title>
<script src="Scripts/jquery-1.3.2.js"
type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
var geoList = [
{ Country: "India", State: "Delhi" },
{ Country: "US", State: "Alabama" },
{ Country: "US", State: "Georgia" },
{ Country: "US", State: "Texas" },
{ Country: "US", State: "NewYork" }
];
$('#LoadPage').append("<ul id='cList'></ul>");
for (i = 0; i < geoList.length; i++) {
$("#cList").append("<li>" +
geoList[i].Country + "-" +
geoList[i].State + "</li>");
}
});
</script>
</head>
<body>
<form id="form1">
<div>
<div id="LoadPage">
</div>
</div>
</form>
</body>
</html>

Resources