Spring boot addFlashAttribute not displaying message in HTML - spring

I've been searching here and online how to use addFlashAttribute in the best way and it's not working for me no matter what I do. I have a Student model which I'm trying to update and flash message of success or fail. The database and all the queries do work and I get redirected to the showStudentDetail page, it's just the message that doesn't show. Here is the code:
StudentService:
public String updateStudent(Student student) {
if(studentDAO.update(student))
return "update student was successful!";
return "error: failed to update student, please try again";
}
public Student getStudentInfo(String username) {
Student student = studentDAO.get(username);
return student;
}
StudentController:
#PostMapping("/updateStudent")
public String updateStudent(#ModelAttribute("student") Student student, RedirectAttributes redirectAttrs) {
String res = studentService.updateStudent(student);
redirectAttrs.addAttribute("username", student.getUsername()).addFlashAttribute("message", res);
return "redirect:/students/{username}/details";
}
#GetMapping("/students/{username}/details")
public String showDetailPage(#PathVariable String username, Model model) {
Student student = studentService.getStudentInfo(username);
model.addAttribute("student", student);
return "student_detail";
}
student_detail (div not showing)
<div>
<div th:if="${param.username}" class="alert alert-info" role="alert" th:text="${param.message}"></div>
</div>

The name of the model in the controller is student, but in the view you are using "param" name. Change "param" to "student".

I finally solved this problem by changing to message instead of param.message.
I really don't know why now it's working but that's the solution to my problem.
only change the html to this:
<div th:if="${message}" class="alert alert-info" role="alert" th:text="${message}"></div>

Related

How to pass the name in View Data instead of ID

I'm working on an application on .NET Core MVC and I'd like to have the name of the navigated property "Hobbies" instead of the ID in my create view
I have this:
And I would like this, intead:
That I manage to get with that code for the create action in the controller:
public IActionResult Create()
{
ViewData["HobbiesName"] = new SelectList(_context.Set<Hobbies>(), "HobbiesName", "HobbiesName");
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("PersonID,PersonName,PersonSurname,HobbiesID")] Person person)
{
if (ModelState.IsValid)
{
_context.Add(person);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewData["HobbieName"] = new SelectList(_context.Set<Hobbies>(), "HobbiesID", "HobbiesName", person.HobbiesID);
return View(person);
}
And in the view :
<div class="form-group">
<label asp-for="Hobbies.HobbiesName" class="control-label"></label>
<select asp-for="Hobbies.HobbiesName" class ="form-control" asp-items="ViewBag.HobbiesName"></select>
</div>
But that doesn't work because, I need the ID.
Does anybody know how I can manage that?
The following worked for me:
<select asp-for="Name" asp-items="#Model.NameList">
In my view model:
public string Name { get; set; }
public SelectList NameList { get; set; }
It is better to work with a view model and avoid ViewBag.
It is essential to have two different fields: the list of items (with their ids already there, behind the scenes), and a field representing the text (not the id) of the item selected

use RemoteAttribute in modelview for Edit and Create Action

i use RemoteAttribute in my modelview for Check Instantly If Username Exists .
[Remote("ValidUsername","UsersManagement",ErrorMessage ="this usernaem is duplicate")]
public string Username { get; set; }
This idea is useful when inserting a new record, but prevents the update from being edited. because the usernaem is exists. What is the solution to that proposal?
Option 1 - Use Additional fields:
You can use the AdditionalFields argument to your remote validation attribute in your model and combine that with a hidden field in your view .
In your model class :
[Remote("ValidUsername", "Home", ErrorMessage = "this usernaem is duplicate", AdditionalFields = "PageType")]
public string Username { get; set; }
In your edit/create page , add hidden field inside the same form as the field your are validating :
<label asp-for="Username">Username</label>
<input asp-for="Username" />
<span asp-validation-for="Username"></span>
<input type="hidden" name="PageType" value="Edit" />
Then on server side validation , you could get the Additional value(edit/create) and validate base on that , if it is edit ,just skip the validation :
[AcceptVerbs("Get", "Post")]
public IActionResult ValidUsername(string Username, string PageType)
{
if ("Edit".Equals(PageType))
{
return Json(true);
}
if (Username.Equals("21"))
{
return Json(false);
}
return Json(true);
}
Option 2 - Use different view model
You can also use different view model in create and edit pages.

Model not updated from view to controller

I'm trying to do that:
Create a Model, add it on a session and send it to the view.
Change Model fields on my view
Get the Model from session updated on my controller
The problem is that my model is never updated when I'm changing values on textboxes, I'm sure that I'm missing something with razor,
View:
#model MvcTestApp.Models.Car
<div class="b1">
<div class="b2">#Html.EditorFor(e => e.KM)</div>
<div class="b2">#Html.EditorFor(e => e.RegistrationNumber)</div>
</div>
#Html.ActionLink("Car", "sendCar")
Controller:
On SendCar, I would like to get the model updated.
namespace MvcTestApp.Controllers
{
public class CarController : Controller
{
public ActionResult Show()
{
var model = new MvcTestApp.Models.Car()
{
RegistrationNumber ="12345",
KM = "12345"
};
Session["temp"] = model;
return View("Show",Session["temp"]);
}
public ActionResult sendCar()
{
return View("Show", Session["temp"]);
}
}
}
Model:
namespace MvcTestApp.Models
{
public class Car
{
[DataType(DataType.Text)]
public string KM { get; set;}
[DataType(DataType.Text)]
public string RegistrationNumber { get; set;}
}
}
You need to make your sendCar controller to update the model. Currently, all the changes you do will only persist locally until you navigate away from the page. You need to post the changed model back to the server.
Take a look at the "Handling edits" part of this example to see how it can be done:
Asp.net tutorials
The way to do this is by wrapping your model details in a form with a submit function. Then in your sendCar method take in a Car object and the model binding will take care of setting everything on the new object.
If you're wanting to persist this (I assume this is just for testing purposes?) then perhaps make your car that you're returning in your show method a class variable.
You should read a beginner tutorial about ASP.NET MVC, which will explain you how to send data from a form to a controller, as it seems you are absolutely not aware of how to do this.
You are not missing 'something', you are missing all about sending data from forms to controllers.

MVC3 Razor - How to pass data from controller to view - and back to controller

I have a view where everything will be populated by the user - but relates to a parent entity. I pass that Id to my view using ViewBag, but I don't know how to get it back to the post action in the controller. I have tried hidden form fields, but it isn't showing in the post, or I do not know how to grab it...
Controller:
public ActionResult AddCar(int id)
{
ViewBag.Id = id;
return View();
}
View (tried):
#using (Html.BeginForm("AddReturn", "DealerAdmin", new { id = carId }))
{
View (tried):
#Html.Hidden(carId.ToString())
HOw do I retrieve the value in my post action in my controller? Or is there a better/different way to approach it?
THanks
Create a ViewModel for post, it would be as follows
public class Post
{
int id {get;set;}
//other properties
}
and in your controller action send a post object
public ActionResult AddCar(int id)
{
Post post = new Post();
post.Id = id;
return View(post);
}
your view should use the Post class as the model
#model namespace.Post
#using (Html.BeginForm("AddReturn", "DealerAdmin", FormMethod.Post)
{
#Html.HiddenFor(model => model.Id)
}
and your controller action which expects result should have a post object as the input parameter
public ActionResult AddReturn(Post post)
{
//your code
}
The hidden field should works. The problem is that your controller did not accept it.
You can use ViewModel to achieve this. Or, use the code below in your action:
id = Request.Form["id"]
Try like this:
#using (Html.BeginForm("AddReturn", "DealerAdmin", new { id = ViewBag.Id }))
{
...
}
there some way
1. send the value with query string if there is only one value to send to the controller
2. if you want collect many field from view you can use formcollection
sample::
public actionresult method1()
{
int id = //everything you want
viewbag.id=id;
....
//and other field to collect
}
in view
<form method="post" action="method1" enctype="now i dont remeber the value of this option" >
#html.hidden("id")
.....
<input type="submit" value"Send"/>
</form>
[httpPost]
public actionresult method1(fromcollection collection)
{
int id = collection.get("id");
....
//and other field to collect
}

Need help to explain Readonly\ScaffoldColumn(false)

Please help with such a question and do not judge strictly because I'm a newbie in MVC:
I've got a model for storing names of users by ID in my DB
public class Names
{
public int NameId { get; set; }
public string Username { get; set; }
}
,
a conrtoller
[HttpPost]
public ActionResult EditforModel(Names Name)
{
if (ModelState.IsValid)
{
db.Entry(Name).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(Name);
}
adding and editing view
adding is working well, the question is about editing
I use
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend> legend </legend>
#Html.EditorForModel()
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
to edit my model.
when trying to go to this view I see an editor for both Id and Username, but if i fill Id - I've got error, because there is no Entry in DB with such Id.
Ok.Let's look for attributes to hide an editor.
[ScaffoldColumn(false)] is something like a marker whether to render an editor for Id or not.
applaying it to my model I've got "0" id posting from my View.Try another attr.
[ReadOnly(true)] makes a field a readonly-field. But at the same time I've got "0" in posting Id.
Modifying a view I placed an editors for each field in model
#Html.HiddenFor(model => model.NameId)
#Html.EditorFor(model => model.Username)
but using it is dangerous because some user can post wrong Id throgh post-request.
I can't use [ScaffoldColumn(false)] with applying Id at [Httppost] action of the controller,by searching appropriate user-entry in DB, because the name was changed..
I can't believe #Html.HiddenFor is the only way out.But can't find one :(
As you mentioned "[ScaffoldColumn(false)] is something like a marker whether to render an editor for Id or not", and [ReadOnly(true)] means that this property will be excluded by the default model binder when binding your model.
The problem is that the HTTP protocol is a stateless protocol, which means that when the user posts the edit form to the MVC Controller, this controller has no clue which object he was editing, unless you include some identifier to your object in the request received from the user, though including the real object Id isn't a good idea for the reason you mentioned (that someone could post another Id).
A possible solution might be sending a View Model with an encrypted Id to the View, and decrypting this Id in the controller.
A View Model for your object might look like this :
public class UserViewModel
{
[HiddenInput(DisplayValue = false)]
public string EncryptedId { get; set; }
public string Username { get; set; }
}
So your HttpGet action method will be
[HttpGet]
public ActionResult EditforModel()
{
// fetching the real object "user"
...
var userView = new UserViewModel
{
// passing the encrypted Id to the ViewModel object
EncryptedId = new SimpleAES().EncryptToString(user.NameId.ToString()),
Username = user.Username
};
// passing the ViewModel object to the View
return View(userView);
}
Don't forget to change the model for your View to be the ViewModel
#model UserViewModel
Now the HttpPost action method will be receiving a UserViewModel
[HttpPost]
public ActionResult EditforModel(UserViewModel Name)
{
if (ModelState.IsValid)
{
try
{
var strId = new SimpleAES().DecryptString(Name.EncryptedId);
var id = int.Parse(strId);
// select the real object using the decrypted Id
var user = ...Single(p => p.NameId == id);
// update the value from the ViewModel
user.Username = Name.Username;
db.Entry(user).State = EntityState.Modified;
}
catch (CryptographicException)
{
// handle the case where the encrypted key has been changed
return View("Error");
}
db.SaveChanges();
return RedirectToAction("Index");
}
return View(Name);
}
When the user tries to change the encrypted key, the decryption will fail throwing a CryptographicException where you can handle it in the catch block.
You can find the SimpleAES encryption class here (don't forget to fix the values of Key and Vector arrays):
Simple insecure two-way "obfuscation" for C#
PS:
This answer is based on the following answer by Henry Mori:
Asp.net MVC 3 Encrypt Hidden Values

Resources