Incrementing session attribute on button click in thymeleaf and call url - spring-boot

i am using thymeleaf in spring boot and reading the session attributes like this on the index page
<p th:text="${session.counter}" th:unless="${session == null}">[...]</p>
where the counter is coming from the below function
#RequestMapping({"/"})
String index(HttpSessbelow request ion session) {
session.setAttribute("counter", "0");
return "index";
}
Whenever someone click on the button on page , we should be able to increase the counter and call a url in the application , how can we achieve this
<button onclick="/activate}">...</button>
#RequestMapping({"/"})
String activate(HttpSession session) {
if(session.getAttribute(counter) == 1){
activate();
return "thanksPage" ;
}
}

Here's how I would structure it.
#RequestMapping("/")
public String start(HttpSession session) {
session.setAttribute("counter", 0);
return "redirect:/current";
}
#RequestMapping("/current")
public String current() {
int counter = (Integer) session.getAttribute("counter);
if (counter == 1) {
activate();
return "thanksPage";
} else {
return "index";
}
}
#RequestMapping("/increment")
public string increment(HttpSession session) {
session.setAttribute("counter", ((Integer) session.getAttribute("counter)) + 1);
return "redirect:/current";
}
And the button should go to /increment
<button onclick="/increment">...</button>

Related

Does the IClientValidator support input file?

Edit
I found that the problem is that View Components are unable to have an #section (see ViewComponent and #Section #2910 ) so adding custom client-side validation using the unobtrusive library seems imposible (or very complex). Moreover, the inability of including the required javascript into a View Component makes me regret of following this approach to modularize my app in the first place...
I am learning to make custom validation attributes with client-side support. I was able to implement a custom validator for a string property and it works pretty well, but when I tried to make one for input file it doesn't work (i.e. when I select a file in my computer, the application doesn't display the validation messages. The server-side validation works. Here is some code that shows my implementation.
The class of the model
public class UploadPanelModel
{
public int? ID { get; set; }
public string Title { get; set; }
public string Description { get; set; } //Raw HTML with the panel description
[FileType(type: "application/pdf")]
[FileSize(maxSize: 5000000)]
public IFormFile File { get; set; }
public byte[] FileBytes { get; set; }
public ModalModel Modal { get; set; } //Only used if the Upload panel uses a modal.
The validator
public class FileSizeAttribute : ValidationAttribute, IClientModelValidator
{
private long _MaxSize { get; set; }
public FileSizeAttribute (long maxSize)
{
_MaxSize = maxSize;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
UploadPanelModel panel = (UploadPanelModel)validationContext.ObjectInstance;
return (panel.File==null || panel.File.Length <= _MaxSize) ? ValidationResult.Success : new ValidationResult(GetFileSizeErrorMessage(_MaxSize));
}
private string GetFileSizeErrorMessage(long maxSize)
{
double megabytes = maxSize / 1000000.0;
return $"El archivo debe pesar menos de {megabytes}MB";
}
public void AddValidation(ClientModelValidationContext context)
{
if(context == null)
{
throw new ArgumentNullException(nameof(context));
}
MergeAttribute(context.Attributes, "data-val", "true");
MergeAttribute(context.Attributes, "data-val-filesize", GetFileSizeErrorMessage(_MaxSize));
var maxSize = _MaxSize.ToString();
MergeAttribute(context.Attributes, "data-val-filesize-maxsize", maxSize);
}
private bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
{
if (attributes.ContainsKey(key))
{
return false;
}
attributes.Add(key, value);
return true;
}
}
The javascript in the Razor View
#section Scripts{
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script type="text/javascript">
$.validator.addMethod('filesize',
function (value, element, params) {
var size = $((params[0]).val()).size(),
maxSize = params[1];
if (size < maxSize) {
return false;
}
else {
return false;
}
}
);
$.validator.unobtrusive.adapters.add('filesize',
['maxSize'],
function (options) {
var element = $(options.form).find('input#File')[0];
options.rules['filesize'] = [element, options.params['maxSize']];
options.messages['filesize'] = options.message;
}
);
</script>
I always return false in the javascript method to force the application to show the validation error regardless the chosen file, but it still doesn't work.
Your addMethod() function will be throwing an error because params[0] is not a jQuery object and has no .val() (you also have the $ in the wrong place). You would need to use
var size = params[0].files[0].size;
However I suggest you write you scripts as
$.validator.unobtrusive.adapters.add('filesize', ['maxsize'], function (options) {
options.rules['filesize'] = { maxsize: options.params.maxsize };
if (options.message) {
options.messages['filesize'] = options.message;
}
});
$.validator.addMethod("filesize", function (value, element, param) {
if (value === "") {
return true;
}
var maxsize = parseInt(param.maxsize);
if (element.files != undefined && element.files[0] != undefined && element.files[0].size != undefined) {
var filesize = parseInt(element.files[0].size);
return filesize <= maxsize ;
}
return true; // in case browser does not support HTML5 file API
});

Is it possible to go to another page with #ResponseEntity in Spring MVC?

I have a page with many items on it. Each one has a button, that is supposed to take user to another jsp with another layout for detailed information about the current item. Can I even do this with ResponseEntity, as it doesn't redirect anywhere? Or may be there's some better way to do it and send my Object to the page? I tried "ResponseEntity.created(location).body(object)" but it doesn't do the job, I stay on the same page. May be I'm just using it wrong?
My method:
#RequestMapping(value = {"/details+{id}"}, method = RequestMethod.GET)
public ResponseEntity<Item> details(#PathVariable("id") int id) {
Item item = itemService.findById(id);
if(item == null){
return new ResponseEntity<Item>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<Item>(item, HttpStatus.OK);
}
Have a look at ModelAndView. It's purpose is to return a view with attached model to it. So for each value of the id, you can decide a pair of view and model to return.
#RequestMapping(value = {"/details+{id}"}, method = RequestMethod.GET)
public ModelAndView details(#PathVariable("id") int id) {
String viewToUse;
Map<String, Item> modelToUse;
if(id == ...) {
viewToUse = ...
modelToUse = ...
} else if (id == ...) {
viewToUse = ...
modelToUse = ...
} else if (id == ...) {
viewToUse = ...
modelToUse = ...
}
return new ModelAndView(viewToUse, modelToUse);
}

spring mvc checking checkbox does not set object

Hi I am new to spring MVC and am trying to implement a form:checkboxes tag and have run into a few issues. All the examples I have googled work with Strings and i want to work with objects so am hoping someone can advise.
I have a List of objects set in my DTO as follows:
TestDTO
private List<Barrier> barriers;
public List<Barrier> getBarriers() {
return barriers;
}
public void setBarriers(List<Barrier> barriers) {
this.barriers = barriers;
}
in my controller class I fetch the barrier objects from the database and add them to my DTO which will be passed to the jsp
savedRecord.setBarriers(dataService.getBarriers());
mav.addObject("testDto", savedRecord);
in my JSP I use the form:checkboxes tag as follows:
<form:checkboxes path="barriers" items="${testDto.barriers}" element="label class='block-label'"
itemLabel="barrier"/>
I also tried with adding
itemValue="id"
but that did not work either
this is wraped in a from element
<form:form method="post" accept-charset="UTF-8" action="${action}"
onsubmit="return checkAndSend()" id="create"
novalidate="" modelAttribute="testDto">
So the issues I am having are as follows:
The checkboxes when displayed all seem to be checked. I have implemented a hashcode and equals method on the barrier object but they still all seem to be checked when I want them unchecked.
Barrier.java
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((barrier == null) ? 0 : barrier.hashCode());
result = prime * result + ((display == null) ? 0 : display.hashCode());
result = prime * result + id;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Barrier other = (Barrier) obj;
if (barrier == null) {
if (other.barrier != null)
return false;
} else if (!barrier.equals(other.barrier))
return false;
if (display == null) {
if (other.display != null)
return false;
} else if (!display.equals(other.display))
return false;
if (id != other.id)
return false;
return true;
}
When I click submit and i look at the testDto my barriers object list is null. How do I get the checked boxes that represent objects to be set on my testDto.
Any pointers and advice is appreciated
Thanks
UPDATE:
Thanks for the pointers. I went with the following. your suggestion helped.
I created the folloiwng in my controller
#InitBinder
public void initBinder(WebDataBinder binder)
{
binder.registerCustomEditor(Barrier.class, new BarrierPropertyEditor(barrierService));
}
and then added a class to do the conversion
public class BarrierPropertyEditor extends PropertyEditorSupport {
private BarrierService barrierService;
public BarrierPropertyEditor(BarrierService barrierService) {
this.barrierService = barrierService;
}
#Override
public void setAsText(String text) {
Barrier b = barrierService.findById(Integer.valueOf(text));
setValue(b);
}
}
This sets the barrier objects on my DTO.
(Sorry for the caps) IT DOES NOT SOLVE WHY THE CHECKBOXES ARE CHECKED ON INITIAL LOAD.
Any ideas how to set the checkboxes unchecked on intitial load?
You can use #ModelAttribute in your Controller to provide the list of values in checkboxes.
#ModelAttribute("barrierList")
public List<Barrier> populateBarrierList() {
List<Barrier> barrierList = dataService.getBarriers();
for(Barrier barrier: barrierList )
{
barrierList.add(barrier);
}
return barrierList ;
}
In JSP, use following :
<form:checkboxes path="barriers" items="${barrierList}" element="label class='block-label'" itemLabel="barrier"/>

Display One record at a time in MVC through List

I have 5 records coming from a simple select stored procedure.
ID Name
1 RecordOne
2 RecordTwo
3 RecordThree
4 RecordFour
5. RecordFive
Requirement is to display one record at a time example:
Record One
Previous Next
Two Action links or buttons with Previous and Next text.
If user clicks Next user will see
RecordTwo
and so on,same for previous case.
My model
namespace MVCLearning.Models
{
public class VMNews
{
public List<Student> StudentDetails { get; set; }
}
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
}
}
Action
public ActionResult Index()
{
VMNews objnews = new VMNews();
objnews.StudentDetails = db.Database.SqlQuery<Student>("usp_studentdetails").ToList();
return View(objnews);
}
View
<div>
#foreach (var item in Model.SD.Take(1))
{
<h3>#item.Name</h3>
<h3>#item.Age</h3>
}
#Html.ActionLink("Next", "index", new { Model.SD[0].ID})
#Html.ActionLink("Previous", "index", new { Model.SD[0].ID })
The way I have written the view is totally wrong am not getting how and what to write on the action and what to write on the View.
What will be one of the way to achieve this.
Change you method to
public ActionResult Index(int? index)
{
int max = 5; // modify based on the actual number of records
int currentIndex = index.GetValueOrDefault();
if (currentIndex == 0)
{
ViewBag.NextIndex = 1;
}
else if (currentIndex >= max)
{
currentIndex = max;
ViewBag.PreviousIndex = currentIndex - 1;
}
else
{
ViewBag.PreviousIndex = currentIndex - 1;
ViewBag.NextIndex = currentIndex + 1;
}
VMNews objnews = new VMNews();
Student model = db.Database.SqlQuery<Student>("usp_studentdetails")
.Skip(currentIndex).Take(1).FirstOrDefault();
return View(model);
}
Note that the query has been modified to return only one Student since that is all that you require in the view. Also I have asssumed if a user enters a value greater than the number of records it will return the last record (you may in fact want to throw an error?)
The view now needs to be
#model Student
<h3>#Model.Name</h3>
<h3>#Model.Age</h3>
#if (ViewBag.PreviousIndex != null)
{
#Html.ActionLink("Previous", "Index", new { index = ViewBag.PreviousIndex })
}
#if (ViewBag.NextIndex != null)
{
#Html.ActionLink("Next", "Index", new { index = ViewBag.NextIndex })
}

how to use two submitt in one jsp and call two actions

i am working with spring mvc framework. i have two submit buttons on a page. that are forwarding request to two different controller. how can i use two action on single jsp page .
please suggest.
my controller are as
1.
#RequestMapping(value = "/user/reset", method = RequestMethod.POST)
public String editUser(#ModelAttribute("users") User user,
BindingResult result) {
Integer uid=user.getId();
User resetUser = usersService.findUser(uid);
resetUser.setActive(0);
ResetPasswordLog resetPasswordLog=new ResetPasswordLog();
usersService.addUsers(resetUser);
resetPasswordLogService.setTempHash(uid);
String TEMPHASH= resetPasswordLog.getTempHash();
System.out.println("www.lacas.com/reset?uid="+uid+"&th="+TEMPHASH);
return "redirect:/secure/user/" + uid;
}
2.
#RequestMapping(value = "/user/edit", method = RequestMethod.POST)
public String addUser(#ModelAttribute("users") UserForm userForm,
BindingResult result) {
Map<String, String> map = new LinkedHashMap<String, String>();
User user = usersService.findUser(userForm.getId());
Integer userId = userForm.getId();
User newUser = usersService.findUser(userForm.getEmail());
user.setName(userForm.getName());
if (newUser == null) {
user.setEmail(userForm.getEmail());
user.getRoles().clear();
Integer[] roleIds = userForm.getRoleIds();
for (Integer roleId : roleIds) {
if (roleId != 0) {
Role role = roleService.findRole(roleId);
user.getRoles().add(role);
}
}
usersService.addUsers(user);
return "redirect:/secure/users/index";
} else {
edit_exist_user = true;
return "redirect:/secure/user/" + userId;
}
}
You can by using JavaScript, and changing form's action attribute dynamically. If this is your form:
<form id="myform" action="#" onsubmit="return pickDestination();">
<input type="submit" name="sbmitbtn" value="edit" onclick="document.pressed=this.value"/>
<input type="submit" name="sbmitbtn" value="reset" onclick="document.pressed=this.value"/>
</form>
Then your pickDestination JS function would look like:
function pickDestination()
{
var a = "/user/" + document.pressed;
document.getElementById("myform").action = a;
return true;
}
I'm going to preface this by saying that I'm not very familiar with spring applications, however in many other java based MVC systems I've accomplished this by simply giving my submit buttons a name and parsing off this in the action class by checking the request.
For example find which submit button was used by it's parameter name, call the appropriate methods. The following is an example of a struts based solution I use on occasion. If you can access the servlet request object in your spring controller, you could do something similar.
#Override
public String execute() throws Exception {
try {
// Check the request for a someone clicking the logout submit button
if (found("logout")) {
user.logout(); //invoke the logout method
session.remove("user");
return SUCCESS;
}
// Check the request for a someone clicking the login submit button
if (found("login")) {
user.login();
session.put("user", user);
return "login";
}
// Catch any login exceptions
} catch (Exception e) {
user = null;
addActionError(e.getMessage());
return INPUT;
}
return SUCCESS;
}
// The following method checks for a request paramater based on the key (String)
// provided. If the key is not found or the value for the parameters is empty, it
// returns false;
private boolean found(String param) {
Object temp = request.getParameter(param);
if (temp != null) {
if (temp.toString().isEmpty()) {
return false;
}
return true;
} else {
return false;
}
}

Resources