MVC Model not updating - asp.net

The Model:
class Address
{
public string City { get; set; }
public string Zip { get; set; }
}
The Controller:
[HttpPost]
public ActionResult GetAddress(Address model)
{
if (!String.IsNullOrEmpty(model.Zip))
{
model.City = GetCityByZip(model.Zip);
}
return View(model);
}
The View:
<div class="formrow">
#Html.LabelFor(model => model.City)
#Html.TextBoxFor(model => model.City)
#Html.ValidationMessageFor(model => model.City)
</div>
<div class="formrow">
#Html.LabelFor(model => model.Zip)
#Html.TextBoxFor(model => model.Zip)
#Html.ValidationMessageFor(model => model.Zip)
</div>
The problem is whenever the city is being modified, it never gets reflected on the view. During debugging, the model.City contains the correct value but it doesn't show up on view. Even something as simple as #Html.TextBoxFor(model => model.City) doesn't display the correct model.City value.

HtmlHelpers get the model values from the model state and not the model when you update and return the model. In order to update and return the model, add this line of code in your post method:
ModelState.Clear();
or you could set the value of city in the ModelState itself:
ModelState["City"].Value = GetCityByZip(model.Zip);

As Tommy noted, this is, somewhat counterintuitively, the correct behavior since form data submitted on post gets first priority when binding the data to the returned view. This makes some sense as the user is likely to have made a validation error when re-returning the same view and gets to resume their form entry as is without the problems of losing form input when restoring a page
One other option is to manually insert the value for the input
So instead of this:
#Html.TextBoxFor(model => model.City)
Do this instead:
<input type="text" name="City" value="#Model.City" />
* which will grab the value directly off the model
Or even better:
<input type="text" value="#Model.City"
name="#Html.NameFor(model => model.City)"
id="#Html.IdFor(model => model.City)" />
*Note: this won't bring in data-val attributes. If you're using them on this property for client side validation, you'll need to build list of data validation attributes for a given element
Additional Resources
HiddenFor not getting correct value from view model
HTML.HiddenFor is not updating on a postback
ASP.NET MVC Postbacks and HtmlHelper Controls ignoring Model Changes

Related

Instantiating an object inside of view

I am trying to instantiate an object from inside of my view:
In my model I have an entity object called Listing that has a Property object inside of it:
public class Listing
{
...
public Property ListingProperty { get; set; }
...
}
Inside of my view I am referencing the listing model:
#model Realintory.Models.Listing
...
Inside of a form in this view I am trying to set up the properties of the "Property" object like so:
<div class="form-group">
#Html.LabelFor(model => model.ListingProperty.Address, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ListingProperty.Address)
#Html.ValidationMessageFor(model => model.ListingProperty.Address)
</div>
</div>
This throws a null value exception because it hasn't been instantiated. My question is how do I set this up, I know this must be easy but no matter what I try it's not working on the view side.
Things like this fail badly:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
Property toCreate = new Property();
#Model.ListingProperty = toCreate;
...
}
I realize this is probably a newb question but I cant seem to find the answer anywhere so I didn't want to waste anymore time and figured I would ask.
Your controller is supposed to provide a model that represents the data required by the view. If the view needs that property populated in the model then it's the responsibility of the controller to do it. The view should be using the model, not building it.
To add an arbitrary block of code in a Razor view you can do this:
#{
// ...
}
In this case, maybe something like this is appropriate:
#if (Model.ListingProperty == null)
{
Model.ListingProperty = new Property();
}

passing data by POST method between views in asp.net

I have data in a particular view page(collected from the user) which I need to send to another view page which has a form needing this data. Now I can use:-
1. post method of javascript(jquery)
$().redirect('/Events/Create', {'arg1': 'value1', 'arg2': 'value2'});
or
A form:-
$('#inset_form').html(' < form action="/Events/Create"
method="post"style="display:none;">< input type="text" name="lat"
value="' + latitude + '" />< /form>');
document.forms['vote'].submit();
Now my question is, which method should be chosen?
Also, the '/Events/Create' page has form in the following way:-
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div class="editor-label">
#Html.LabelFor(model => model.lat)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.lat)---------> THIS field accepts the POSTed data
#Html.ValidationMessageFor(model => model.lat)
</div>
<div>
....OTHER INPUT FIELDS.....
</div>
<p>
<input type="submit" value="Create" />
</p>
}
So, my next question is how do I make the text box 'lat' created by #Html.EditorFor in the form show the POSTed data as its default value. Then, the user can fill other fields and then submit the form.
As per HTTP standards, if a call is not changing the state of the website (i.e. data), you should use a GET request. This means you should provide the user with a link including the proper query string to populate the form on the /Events/Create page.
Also, keep in mind you never send data from view to view in MVC. Everything goes through a controller action so make sure your "Create" action on the "Events" controller accepts the proper arguments to populate the form for submission. If the Model.lat variable has a value in it when rendering the view, the text box will be prepopulated with that value.
Why do you want to POST the data to the second form? If you want to show the form and load it with default values, create a GET action method for it and pass the default values in the url. Although there's nothing stopping you having two POST methods for the same view, the normal approach would be to load the form using a GET, then POST back the data after editing.
[HttpGet]
public ActionResult Create(string lat)
{
MyView view = new MyView();
view.lat = lat;
View(view);
}
[HttpPost]
public ActionResult Create(MyView view)
{
//process posted data after editing
}
The view object here is a ViewModel class containing properties for the data that you will edit on the form. Make your View strongly typed to this ViewModel type.
To invoke the GET method from your jQuery (above):
window.location = "Events/Create?lat=myValue";

Create multiple parts of complex model in the main view

I'm a little new to ASP.Net MVC, I have a complex model.
public class BuildingPermit
{
public int ApplicationID { get; set; }
public virtual Person Applicant { get; set; }
public virtual Area ApplicantArea { get; set; }
public virtual ICollection<Owner> Owners { get; set; }
/...
}
Using scaffolding, I created the controller and all the views. However, I want to register all the details in the same page, meaning in the BuildingPermit's Create view, creating the details for Applicant of type Person, the ApplicationArea of type Area and so on. Is there any way I can accomplish this?
If it's not possible, I think it's possible to add a link to create the object. When the user clicks on it, the page goes to that view, creates it, get its information back and shows it in the BuildingPermit's view.
I'd appreciate your help.
You could achieve this by creating an editor template for Person, Area, Owner etc in:
~/Views/Shared/EditorTemplates/Person.cshtml
~/Views/Shared/EditorTemplates/Area.cshtml
~/Views/Shared/EditorTemplates/Owner.cshtml
The editor template will want to be strongly typed and should give the editor layout for the type:
#model Models.Person
<h2>Person</h2>
<p>
#Html.LabelFor(model => model.Name)
#Html.EditorFor(model => model.Name)
</p>
<p>
#Html.LabelFor(model => model.Address)
#Html.EditorFor(model => model.Address)
</p>
// And so on
Once you've done this calling #Html.EditorFor(model => model.Applicant) will pick up your template and display within your Edit view.
If you are wanting to display all of this information together then you will probably want to also create display templates for these types. These work just like the editor templates except you keep your templates in a DisplayTemplates folder.
~/Views/Shared/DisplayTemplates/Person.cshtml
~/Views/Shared/DisplayTemplates/Area.cshtml
~/Views/Shared/DisplayTemplates/Owner.cshtml
That's no problem, just make sure you initialise your complex object somehow to avoid null reference exceptions:
public BuildingPermit()
{
this.Applicant = new Person();
this.ApplicantArea = new Area();
...
}
Then in your controller action method create an instance of the model and pass it to your view:
public ActionResult Create()
{
BuildingPermit model = new BuildingPermit();
View(model);
}
For the view:
#model MyNamespace.BuildingPermit
#Html.LabelFor(m => m.Applicant.FirstName)<br />
#Html.TextBoxFor(m => m.Applicant.FirstName)<br />
...
<input type="submit" value="Create new building permit" />
Then look into examples online on how to handle a HttpPost in your MVC controller.
If you want to create specific UI partials for each object type, then you can looking into EditorFor and DisplayFor templates. From what you mention in your original post, this might be what you're looking for also.
Hope this helps.

RegularExpression attribute for my domain object of type string has strange behavior

I'm developing an MVC 4 ASP.NET application with Razor view engine. I have a property in view called 'Title' and has set its metadata like this:
[RegularExpression(#"^[\p{L}\p{N}\s\,\،\-\(\)\u200C]*$", ErrorMessage = "Some error message")]
public string Title { get; set; }
In view, as soon as I start typing any character in corresponding textbox, it shows the validation message. This is the razor view:
#Html.LabelFor(model => model.Title)
#Html.EditorFor(model => model.Title)
#Html.ValidationMessageFor(model => model.Title)
Am I missing something?
The client-side validator is using javascript, so the regex needs to be in javascript-specific syntax.
According to regular-expressions.info, the \p{...} constructs are not supported.
You might be able to replace \p{L}\p{N} with \w, depending on your target character set.

Html.EditorFor shows the correct number of items but not the data

I'm working on my first ASP.NET MVC 3 application and I'm trying to show ingredients of a particular ice cream on a create page.
I've got a viewmodel for the page which has a structure something like this:
public class IceCreamViewModel
{
...
public IEnumerable<IngredientViewModel> Ingredients { get; set; }
}
(there are other properties but they aren't germane to the discussion)
Ingredients gets populated by the Create action on the controller and I've verified that it contains the data I want.
The IngredientViewModel has the following structure:
public class IngredientViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsChecked { get; set; }
}
In the Create view I have tried to display the collection of ingredients to allow the user to check which are in the recipe (e.g., peanuts, egg, etc.) and I'm doing something like this:
#Html.EditorFor(m => m.Ingredients)
I've written and editor template for this that looks like so:
#model IceCream.ViewModels.Ingredients.IngredientViewModel
<div>
#Html.HiddenFor(m => m.Id)
#Html.LabelFor(m => m.Name)
#Html.CheckBoxFor(m => m.IsChecked)
</div>
What I'd expect to show up is a bunch of labels and checkboxes for each of my ingredients, but what shows up is the correct number of label/checkbox entries but they all say "Name" rather than the ingredient name that is in the IngredientViewModel. So I'm certainly doing something wrong here. It obviously knows that it has N items to iterate through but it isn't picking up the properties of those items. Guidance?
Update
So, all I ended up doing was switching my LabelFor to a TextBoxFor and my values showed up... as they would, of course. (tired, long day) - #LabelFor uses the name of the property, or the annotated DisplayName for the property. Things work fine now... move along, nothing to see here...
You're trying to create a label for the Name property (as if you wanted the user to edit the Ingredient Name), instead of actually showing the name as the label for the checkbox.
How about changing:
#Html.LabelFor(m => m.Name)
... to:
#m.Name
Or, better yet:
#model IceCream.ViewModels.Ingredients.IngredientViewModel
<div>
#Html.HiddenFor(m => m.Id)
<label for="#m.Id">#m.Name</label>
#Html.CheckBoxFor(m => m.IsChecked)
</div>

Resources