passing a model to the next controller from view - asp.net-core-mvc

I have a form with few inputs, name, email, message and some checkboxes. I've created a model for these inputs and set all the validations i require.
But now I also want to pass my model (i.e. from #model MyModel) or rather some object property of my model together with those inputs.
Is populating a VewBag/viewData with my model a way to go?
#{
ViewBag.MyModel = Model;
// or ViewBag.ThatProperty = Model.ThatProperty
}
or do i still have a better way up my sleeve?

ViewBag and ViewData persist in one trip from server to client, and not the other way around.
There is no way to pass an object from the view to the controller. If it's a database object, you can pass the object Id using one of the two methods described below, then query the DB on post.
If you have no other way, you can encode the object as a JSON string (using the Newtonsoft package, for example) and pass it also using one of the two methods described below, but this isn't the best option.
To pass a property from the View to the Controller, you have two options:
Url Parameter
Hidden field
Url Parameter
<form ... asp-route-ThatProperty="#Model.ThatProperty">
...
</form>
Form Field
<form>
<input type="hidden" name="ThatProperty" value="#Model.ThatProperty" />
</form>
Controller Action
If 'ThatProperty' doesn't exist on your model, receive it as an extra parameter.
public IActionResult MyAction (MyModel model, string ThatProperty)
{
...
}

Related

Spring Boot Post Request Method not having all Object values

Using Spring Boot, Hibernate JPA and Thymeleaf.
I have an Order database table which currently only holds 1 record. This record has a few columns and some of the columns are not seen on any forms, they are set upon saving the Order, for instance the creation date.
On the GET request below I select the specific Order and all values are returned into the Order object as expected.
This is my GET Request method:
#RequestMapping(value = "/editorder/{orderId}", method = RequestMethod.GET)
public String editOrderGet(Model model, #PathVariable long orderId)
{
Order order = orderService.findById(orderId);
model.addAttribute("order", order);
return "/editorder";
}
This is a small snippit of my edit order html form using Thymeleaf, binding the Order object to the form using th:object as below:
<form role="form" th:action="#{/editorder}" th:object="${order}" method="post">
<input type="hidden" th:field="*{orderId}"/>
<button type="submit" class="btn btn-primary">Update Order</button>
.
.
</form>
And this is my POST Request method:
#RequestMapping(value = "/editorder", method = RequestMethod.POST)
public String editOrderPost(Model model,
#Valid #ModelAttribute("order") Order order, BindingResult bindingResult)
{
//rest of code here
}
As you can see, on the GET request I am adding the Order object to the model.
On the html form, I am binding the Order object to the entire form. Then on the POST request I am getting the Order object.
But on the POST it is seen as a new Order and only contains the fields as specified in the form, it does for instance not contain the creation date as seen in the GET request.
My question is this:
Am I missing something or do I explicitly need to go set each of those fields as hidden fields on my form?
In your GET response you may be returning the whole Order object into the Model, but Thymeleaf when trying to build the actual html from template will pick only the items it needs to build the template. So only the fields that are used in the form are used to build the form in your html page.
So when u resubmit the form to the POST service only those fields that are available in the form is reposted.
If u want these fields to be displayed on the page then add these fields in the Form. Thymeleaf picks them and displays in the form. If you dont want them to be shown in the Page then just ignore them. The Order object which u receive in the POST would not have that fields as they were not available in original form.
U can get them by querying the database, any how you do have the order id saved as the Hidden field.
public String editOrderPost(Model model,
#Valid #ModelAttribute("order") Order order, BindingResult bindingResult){
Order orderFromDB = orderService.findById(order.getId());
// Code to update the orderFromDB from order object
orderService.save(order);
}
This will save the updated fields to the database.
Generally its not a good practice to expose the Entity objects to the API. Try using a DTO/value object. This can have only fields that define your business fields. Also u can use BeanMapper frameworks like dozer/mapstruct/modelmapper to copy from DTO to Entity and vice versa.

Spring: Registration form remove object from model

In my Spring MVC Project I created a registration page.In this page there is a form in which the user insert his information(name,surname and so on).I have used the Spring tag form to bind the object "cliente" to the form. In my controller I have:
#RequestMapping(value="/registration",method=RequestMethod.GET)
public String viewRegistration(ModelMap model){
model.addAttribute("cliente",clienteFactory.createCliente());
return "registrazione";
}//registrazione
In registration.jsp
<form:form method="post" action="add" modelAttribute="cliente">
....
</form:form>
In this project I have not used Spring Security,because I'm a student and I hadn't learned this part yet.
If the user leaves the page without register, I want to delete the object "Cliente" from the model.How can I solve it?Thanks
At first you don't need to delete object from model as if client leaves page with mapping /registration model ref will be overriden with model of another mapping method.
Second in more cases it's no good idea to call your method clienteFactory.createCliente()) in GET request method. Better to call it in POST after user fill all form fields and post his request than you know that you need to call clientFactory.Also use #ModelAttribute annotation as method argument.
As you have in your form form:form method="post" it will not working without such method
#RequestMapping(value="/registration",method=RequestMethod.POST)
public String makeRegistration(ModelMap model){
.....
Also see simple Sring tutorial for handling forms.

Model Validation in asp .net MVC

I am developing an application where I have a form with a model "StudentListModel".
I have a button in the same page which is not a submit button. I have written an ajax function which calls an action method in the controller specified.
Now my problem is there is a textbox for the studentname ,
[StringLength(160, MinimumLength = 3)]
[Display(Name = "First Name")]
[Required]
[Remote("CheckDuplicateNames", "AddStudent")]
public string StudentName { get; set; }
None of these validations are firing.However if I make the button as submit ,these will work.
Is there any way to do model validation other than using formsubmission?
Model validation is done automatically before your ActionMethod is executed and ModelState will be populated with information about that validation. You do not need to call ValidateModel as long as you are running your Controller and ActionMethods in the default MVC lifecycle.
An action method that has parameters will have the values for parameters populated using MVC Model Binding. This means that any values posted in a Form or QueryString (and a few other sources) will be name matched to simple parameters or properties in complex parameters. Using an HTML form and the MVC HtmlHelper methods for creating input types you get the desired behavior with very little work, but as you have noted it requires a form submit to send the data.
An ajax call will also populate the model using Model Binding but it requires the fields to be sent to the ActionMethod. Using jQuery it is as simple as performing a post or get request on the buttons click event passing a JavaScript object with your model's properties on it.
$('#yourButtonId').click(function() {
var student = {};
student.StudentName = $('#StudentName').val();
$.post('#Url.Action("ActionMethodName")', student).done(function (data) {
//handle returned result from ActionMethod}
});
});
You can call model validation manually in the controller method. The syntax is simply ValidateModel(model). This validates the model based on its current property values, and populates the ModelState dictionary with any errors.
If your model does not get populated with the values, but you have got them at hand, you can populate it using UpdateModel(model, values), another method inherited from the Controller class.

Enum unbinds from the model on ajax call

I am reading an enum value from the db then bind it to the model. When i post the form with ajax, somehow the enum is unbound or the model property in null or zero but it display properly on the view. I have posted code below. Im using entityframework and mvc3
//model code constructor
public CarModel(Car car)
{
State=(CarState)car.State;
//car.State comes in as an int
//etc setting other variables
}
//CarState property
public CarState {get;set;}
//model code
#Html.DisplayFor(m=>m.CarState)
//Controller code()
Save(CarModel car)
{
//I have code that saves the changes
}
The minute i get to "car", CarState has no value.
It's not quite clear how you are passing this value to the controller action. You have only shown some #Html.DisplayFor(m=>m.CarState) but obviously this only displays a label in the view. It doesn't send anything back to the server. If you want to send some value back you will have to use an input field inside the form.
For example:
#Html.EditorFor(m => m.CarState)
or use a HiddenFor field if you don't want the user to edit it.
In any case you need to send that value to the server if you expect the model binder to be able to retrieve it. The model binder is not a magician. He cannot invent values. He binds values to your model from the Request.

What, exactly, does a modelbinder do? How to use it effectively?

I was researching something and came across this blog post at buildstarted.com about model binders. It actually works pretty darn well for my purposes but I am not sure exactly whats going on behind the scenes. What I did was create a custom ModelBinder called USerModelBinder:
public class UserModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
ValueProviderResult value = bindingContext.ValueProvider.GetValue("id");
MyEntities db = new MyEntities();
User user = db.Users.SingleOrDefault(u => u.UserName == value.AttemptedValue);
return user;
}
}
Then in my Global.asax.cs I have:
ModelBinders.Binders.Add(typeof(User), new UserModelBinder());
My understanding is that using the model binder allows me to NOT have to use the following lines in every controller action that involves a "User". So instead of passing in an "id" to the action, the modelbinder intercepts the id, fetches the correct "item"(User in my case) and forwards it to the action for processing.
MyEntities db = new MyEntities();
User user = db.Users.SingleOrDefault(u => u.UserName == value.AttemptedValue);
I also tried using an annotation on my User class instead of using the line in Global.asax.cs:
[ModelBinder(typeof(UserModelBinder))]
public partial class User
{
}
I'm not looking for a 30 page white paper but I have no idea how the model binder does what it does. I just want to understand what happens from when a request is made to the time it is served. All this stuff "just working" is not acceptable to me, lol. Also, is there any difference between using the annotation versus adding it in Global.asax.cs? They seem to work the same in my testing but are there any gotchas?
Usually the Model Binder (in MVC) looks at you Action method and sees what it requires (as in, the objects types). It then tries to find the values from the HTTP Request (values in the HTTP Form, QueryString, Json and maybe other places such as cookies etc. using ValueProviders). It then creates a new object with the parameters that it retrieves.
IMO What you've done is not really "model binding". In the sense that you've just read the id and fetched the object from the DB.
example of usual model binding:
// class
public class SomeClass
{
public int PropA {get;set;}
public string PropB {get;set;}
}
// action
public ActionResult AddSomeClass(SomeClass classToBind)
{
// implementation
}
// pseudo html
<form action="">
<input name="PropA" type="text" />
<input name="PropB" type="text" />
</form>
if you post a form that contains the correct values (lets say you post a form with PropA and PropB ) the model binder can identify that you've sent those values in the form and build a SomeClass object.
If you really want to create a real working example you should use a strongly typed View and use HtmlHelper's EditorFor (or EditorForModel) to create all the correct names that MVC needs.
--
for reference MVC's default binder is the DefaultModelBinder, and some (there are more, you can look around in the System.Web.Mvc namespace) ValueProviders that it uses by default are the FormValueProvider and the QueryStringValueProvider
So, as I already said, how this basically works is that the default model binder reads the model that the action is recieving (say SomeClass in the example) reads what are the values that it can read (say PropA and PropB) and asks the ValueProviders for the correct values for the properties.
Also, if I recall correctly, you can also see the value providers in runtime using the ValueProviderFactories static class.
A ModelBinder looks at the arguments of the selected Controller action's method signature, then converts the values from the ValueProviders into those arguments.
This happens when the ControllerActionInvoker invokes the action associated with the ControllerContext, because the Controller's Execute() method told it to.
For more about the ASP.NET MVC execution process, see Understanding the MVC Application Execution Process

Resources