ASP.NET MVC Binding to View Model as well as routed values - asp.net-mvc

I know similar questions have been asked regarding complex model binding in ASP.NET MVC, but I am having a problem binding because of a lack of a sufficient prefix coming back on the POST and wondered if there were an easy solution.
I have a view Model that looks something like this:
public class ViewModel<Survey, Contact>
{
public Survey Model { get; set; }
public Contact Model2 { get; set; }
}
I then have an action method like this that accepts the POSTed
public ActionResult Survey(
string id, string id2, SurveyViewModel<Survey, Contact> model)
{
// code goes here...
}
In my form, the first two id's are from the URL route and I then have form code (using #Html.EditorFor(x => x.Model.SurveyName) or similar), generated with names like this:
<input class="text-box single-line" id="Model_Email"
name="Model.Email" type="text" value="" />
A post works if I change the name from Model.Email to model.Model.Email, but I am trying to avoid having to create a custom model binder.
Is there
A setting I can make in the view to change the name for all fields rendered in a view using the #Html.EditorFor typed view helpers?
Something I can change using the Bind attribute on the action that would allow it to default binding to that object?
The answer may be "build a custom binder", but I just wanted to pose the question before biting that off.
Thanks for the help. Best Regards,
Hal

You can pass custom viewdata with custom HtmlFieldPrefix to view. Every control rendered with helper will have that prefix.
ViewData.TemplateInfo.HtmlFieldPrefix = "prefix here";
Take a look at this: Forcing EditorFor to prefix input items on view with Class Name?

Related

Model usage within View

I think I'm not clear with the #model that can be part of the view
For example
#model MyModel
Is it the input argument that I can populate and call the view with?
return View("MyView", MyModel);
Is it the output variable I can populate during the post of the view (for next control action)
[HttpPost]
public ActionResult SomePostAction(MyModel myModel) //(and in post action)
Is it both ??
3, it's both!
It is called model binding. A feature of ASP.NET which makes it trivial to bind a mode to a view. Hence the name 'view model', which those models are usually called.
Assigning a model to your view gives you a so-called strongly typed view, which fully exposes the power over the Razor syntax.
The model binder is capable of binding the values of every input field back to the model when posting the form, as long as the name attribute of the form matches the name of the property on the view model. Html helpers such as Html.EditorFor(m => m.SomeProperty) makes this a trivial task.
As Mystere Man mentions, it's also possible to do this without an actual model in your view. For instance, this works:
Html (I omitted the form tag and submit button):
<input type="text" name="SomeString" />
with this method in your controller:
[HttpPost]
public ActionResult SomeAction(string someString)
{
// ...
}
The #model declaration at the top of your view is related to the model object you passed to the View() method in your controller (option 1 in your question). The #model declaration is your way of telling the Razor view engine that the view is strongly typed. That means the C# compiler can double check any properties of your view accesses.
Suppose you had the following class
public class MyModel
{
public string Name { get; set; }
}
Without a strongly typed view you might have something like this in your view
<div>
Hello, #Model.Nmae
</div>
Notice that there's a typo in Name. ASP.Net has no idea what your model is so it has to use a dynamic object. You won't find that error until runtime. If you had delcared #model MyModel you would have an error at build time because MyModel doesn't have a Nmae property.
However, it's not uncommon to use the same model type as a parameter of the action. Imagine your page is an HTML form. In that case the model that your view is strongly typed to and the model that's passed to an MVC action could be the same.

Confused about Usage of LabelFor() html helper in MVC 2.0

I am working on an MVC 2.0 C# web Applciation. In one of my form, i am using LabelFor() html helper.
I am using the following syntax:
<%=Html.LabelFor(model=>model.Addedon)%>
Here, for this label i would like to associate a initial value that is DateTime.Now
I tried some thing like this:
<%=Html.LabelFor(model=>model.Addedon,new{value=DateTime.Now})%>
But, i am getting an error saying that there is no over load for this helper taking two arguments.Please help
UPDATED:
The form is create form/ add form which makes an insert operation. So, i am building a model and updating that model to the database.
In that model, i have a field called createdby. So, i need to associate this value with the username logged in and doing the insert operation.
So, how to associate this username value with the model field and i need to display as label so that it will be read only field.
Hope this makes clear..
LabelFor is only for, you guessed it, rendering a <label> element.
It also uses the [Display] and [DisplayName] attributes, so you can have a strongly-typed label with custom name.
What you're after is probably this:
<div>
<%= Html.LabelFor(model => model.Addeon) %>
</div>
<div>
<%= Html.DisplayFor(model => model.Addeon) %>
</div>
So the LabelFor will generate the property name description (e.g. 'Addeon'), while the DisplayFor will render the property value. DisplayFor can use the [DisplayFormat] attribute if you need custom formatting. You can set the default property value in the view model's constructor:
public class ViewModel
{
[Display(Name = "My awesome date")]
public DateTime Addeon {get;set;}
public ViewModel()
{
Addeon = DateTime.Now;
}
}
[EDIT]
Actually, your edit would make for a good second question instead of putting it here. Anyway, in your situation I'd create a dedicated view model that would hold the properties you need (e.g. user name) and would be filled in controller. Everything else would be conceptually the same - view would bind to the view model.

How does #Html.BeginForm() works?

I'm very new to ASP.NET, just started the MVC tutorial today on asp.net. I got here http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/examining-the-edit-methods-and-edit-view
So far so good, the problem:
In my View I have the following code
(Model is set to the view with #model MyFirstMVC4.Models.Movie)
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Movie</legend>
#Html.HiddenFor(model => model.ID)
//... bla bla html input
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
My MovieController
// Shows the view
public ActionResult Edit(int id = 0)
{
Movie movie = db.Movies.Find(id);
if (movie == null)
{
return HttpNotFound();
}
return View(movie);
}
//
// POST: /Movie/Edit/5
[HttpPost] // Handles the view above
public ActionResult Edit(Movie movie)
{
if (ModelState.IsValid)
{
db.Entry(movie).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(movie);
}
And here is the question - How the heck does it pass the Movie object to the POST method above?! When I observe the client side there is
<form action = "/Movie/Edit/1" ... />
Here I don't understand why action = url of the very same view page?!1
Also on the server side there is just Html.BeginForm() :(
How does it realize to what action method to post and what route parameters to pass?
It works, I just don't know why
The version of BeginForm in the code,
with no parameters, sends an HTTP POST to the current URL, so if the view is a response to
/Movie/Edit/5, the opening form tag will look like the following:
< form action="/Movie/Edit/5" method="post">
The BeginForm HTML helper asks the routing engine how to reach the Edit action of the
MovieController. Behind the scenes it uses the method named GetVirtualPath on the Routes
property exposed by RouteTable — that’s where your web application registered all its routes in
global.asax. If you did all this without an HTML helper, you’d have to write all the following
code:
#{
var context = this.ViewContext.RequestContext;
var values = new RouteValueDictionary{
{ "controller", "movie" }, { "action", "edit" }
};
var path = RouteTable.Routes.GetVirtualPath(context, values);
}
<form action="#path.VirtualPath" method="get">
...
</form>
You asked how is movie object is passed. That is called model binding.
When you have an action with a parameter, the MVC runtime uses a model binder to build the
parameter. You can have multiple model binders registered in the MVC runtime for different types
of models, but the workhorse by default will be the DefaultModelBinder.
In the case of an Movie
object, the default model binder inspects the Movie and finds all the movie properties available
for binding. Following the naming convention you examined earlier, the default model binder can automatically convert and move values from the request into an movie object (the model binder can
also create an instance of the object to populate).
In other words, when the model binder sees an Movie has a Title property, it looks for a value
named “Title” in the request. Notice the model binder looks “in the request” and not “in the form
collection.” The model binder uses components known as value providers to search for values in
different areas of a request.
The model binder can look at route data, the query string, and the form
collection, and you can add custom value providers if you so desire.
When you call BeginForm() without any parameters it default to using the same controller/action used to render the current page. It assumes you'll have an action with the correct name on your controller that will accept postbacks (which you do). It uses the RouteValues to do this.
It automatically binds each input control (by name) to the parameters of the action accepting the postback - or in your case, the properties of the object parameter for the action accepting the postback.
[HttpPost] attribute is given to the action that you want to be called on the POST submit of the form.
to understand how #using (Html.BeginForm()) works , you need to know what page it is already on . using #using (Html.BeginForm()) in 2 different views will come back to two different controllers
We can create forms by typing simple html or by html helpers.
One of them Html.BeginForm(); it is a little bit odd because you actually can wrap it in a using statement because this particular helper returns an object that implements IDisposable in C#. First it writes out with opening tag. And at the bottom when the generated code calls dispose on that object, that’s when it will write out closing form tag . So BeginForm gives me an object that will write out my opening form tag and my closing from tag. After that you don't worry about anything you can just focus on labels and inputs

best practice for mvc razor view layout

I am looking for best practices for design razor view with MVC.
which would be better option:
HtmlHelper extension methods
#Html.TextBox("txtName")
or
write the html directly
<input type"text" id="txtName" name="txtName" />
I found 2 diferent links.
The first one http://blogs.msdn.com/b/aspnetue/archive/2010/09/17/second_2d00_post.aspx says DO use HTMLHelper extension methods.
and the second one http://codeclimber.net.nz/archive/2009/10/27/12-asp.net-mvc-best-practices.aspx says 10 – Write HTML each time you can
so i am a little cofused
Even the name HtmlHelper should already give you a hint whether you should use it or not. Do you want help? If not, just write html from the scratch. It does not really matter how the html was generated: from the scratch or using html helper. What matter is that it was generated with correct names of the inputs so that model binder can bind these inputs to the model.
For example, suppose you have the following Model that will be passed to the view and that will be received on the POST:
public class SomeModel
{
public Customer Customer { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
In order to make sure that your inputs will be binded to the model you need three inputs on your page:
<input type="hidden" id="whatever" name="Customer.Id" />
<input type="text" id="whatever" name="Customer.FirstName" />
<input type="text" id="whatever" name="Customer.LastName" />
Having this html markup will assure proper model minding. However, you can achieve this markup by using HtmlHelpers, which is a lot easier:
#Html.HiddenFor(m => m.Customer.Id)
#Html.TextBoxFor(m => m.Customer.FirstName)
#Html.TextBoxFor(m => m.Customer.LastName)
This will not only give you proper name attributes on every input, but also assign id attributes accordingly so you don't have to do that all by your self.
It appears that the author from the second article suggests to never use HtmlHelpers for two reasons:
the learning purposes: I assume by saying "web developers have to be
comfortable writing HTML" he means that developer should know
exactly what html markup is required for proper model binding.
the fear of black box: It seem that author is afraid that improper html
markup will be generated by using HtmlHelpers or he just does not
know what html will be generated.
I disagree with his phrase: "HtmlHelpers whose only reason of living is hiding the HTML away". I'd rather say "HtmlHelpers whose only reason of living is helping writing Html markup"
Summary:
HtmlHelpers help you write proper html markup, which is why I suggest you using it.
Since you're using Razor, I would make the most of what it has to offer, and the HtmlHelper extensions allow you to write html quicker and easier in a lot of places.
There may be times when you have to use Html instead, where you might want to include tags in an anchor and cannot use #Html.ActionLink, for example.
But where you can achieve the same result with either approach, I'd recommend you go with Razor.
Html helpers are not cosmetic code snippets that just save time. Consider choosing the appropriate editor based on model property types, and - what is also very important - they help in client validation, provided you include jquery.unobtrusive-ajax.js and jquery.validate.js.
The last cannot be achieved by simply writing HTML.
From what I said it can be easily derived that Html helpers "know" about the model, while plain markup does not.
At the end it is up to you to decide what to use, but knowing more about Html helpers is better when making a decision.

Model with List - approaches to add new item to the list from a Razor view

I have a model with various properties but the one of interest is a List of another type of Model.
For example:
public class User
{
public string Name { get; set; }
public string Description { get; set; }
public IEnumerable<UserInterest> Interests { get; set; }
}
I then use an Editor Template within my view to render out a view for each item of the model items.
#Html.EditorFor(x => x.Interests)
The EditorFor template looks something like:
#model Interest
<div>
#Html.HiddenFor(x => x.Id)
#Html.TextBoxFor(x => x.InterestText)
#Html.CheckBoxFor(x => x.Delete)
....
</div>
Something very similar to the accepted answer here: Model Containing List of Models (MVC-3, Razor)
My question is - how would you from the client-side (jQuery) create a new item within the property without going back to the server. I currently have a rough way of doing it whereby I post the data back to my controller which returns the model back with a new blank item within the Interests property.
This seems to be overkill making a HTTP request and not very elegent. I was thinking of using jQuery .Clone() but not entirely sure on what I'd need to do in terms of naming the elements and clearing existing values.
So does anybody have any suggestions. I'm hoping to get more opinions and different approaches.
You can simply create the Textbox and checkbox on the fly and add that to the DOM. When saving it, Use jQuery ajax to post that data ( new record data) to an action method and save it there. Return a status back (Succcess /Falied) from your action method to your client side code ( your callback function of jQuery ajax/post) and check it there. If it is success, Show a success message to the user and append the new item to the existing list.
Sample jSFiddle : http://jsfiddle.net/carwB/2/
If you want to return some complex data ( ex : All new records with its id etc..) along with the status, you may return JSON from your action method.
EDIT : To keep your Model binding works with the newly added dynamic elements, you need to follow the naming convention of the elements.
The trick is to keep the id property value of the html element in this format.
CollectionName_ItemIndex__PropertyName
and name property value in this format
CollectionName[ItemIndex].PropertyName
I created a sample working program and explained it how it works Here based on your requirements.
In such situations I prefer to use client templating. You send data to server with ajax and then receive JsonResult. Look at JsRender this is javascript lib without jQuery dependency.
1.Create two partial view one is for list item and second one is creation
2.First partail view should be inside the div which has id 'divMdeolList'
3.and Creation view will have the code like that
#using (Ajax.BeginForm("SubmitData", new AjaxOptions { UpdateTargetId = "divMdeolList" }))
{
#Html.TextBoxFor(x => x.InterestText)
<p>
<input type="submit" value="Create" />
</p>
}
4. And then create a ActionResult type action on controller that will render the partialview
public ActionResult SubmitData(YourModel model)
{
//Do : save the record
return PartialView("FirstPartailView", model);
}
This will update the View without postback

Resources