I am wondering how to properly use the HTML helper method HiddenFor when I am dynamically pulling in data to populate my view.
Prior to trying to use HiddenFor, I was using the regular hidden element using the following code:
#Html.Hidden("Answers["+i+"].FormEntryId", entry.FormEntryId)
This produces the following HTML:
<input id="Answers_0__FormEntryId" name="Answers[0].FormEntryId" type="hidden" value="d318afa2-42ba-4205-9f8a-9d7e6ad59ea4">
As you can see, it is quite fragile in that it relies on a string literal. Myself and another developer then decided to try and using HiddenFor as follows:
#Html.HiddenFor(x => x.Answers[i].FormEntryId, entry.FormEntryId)
This produces the following HTML, notice the empty value field:
<input data-val="true" data-val-required="The FormEntryId field is required." id="Answers_0__FormEntryId" name="Answers[0].FormEntryId" type="hidden" value="">
With x being a stand-in for our ViewModel. However, once we switched to this approach, we hit a snag where our id value is not populated what-so-ever. Thus, I am wondering, what is the correct way to use HiddenFor, and if it is even possible to do so when dealing with a dynamic view. The view model looks as follows:
You are actually using wrong overload of HiddenFor helper method, you are already iterating on the Answers collection, you just need to use this overload which just takes expression as a single parameter like:
#for(int i=0; i<Model.Answers.Count; i++)
{
#Html.HiddenFor(x => x.Answers[i].FormEntryId)
}
This will itself take care of generating correct id and name property of input elements so that their values are posted back in Model/ViewModel object back to controller action.
This should work for you fine.
SideNote:
If you have strongly typed view with a Model/ViewModel, you should always be using the Stringly Typed Helper Methods like TextBoxFor,HiddenFor,DropDownListFor etc instead of normal helper methods, as those require a little more work, and strongly typed ones will take care of posting back the new values for input elements via Form post.
Hope it helps!
Related
I'm trying to send back the contents of the input field as follows.
#model Bike
#using (Html.BeginForm("BikeStore", "Home", FormMethod.Post))
{
<input type="text" value="#Model.Color" />
<input type="submit" value="Save"/>
#Html.ActionLink("Cancel", "Bikes", "Home")
}
The action and the model are declared as follows.
public ActionResult BikeStore(Bike bike)
{
...
return RedirectToAction("Bikes");
}
public partial class Bike
{
[Key] public Guid Id{get; set;}
[Required, StringLength(999)] public string Color { get; set; }
}
I'm hitting the breakpoint in the method BikeStore but bike passed in is empty, i.e. it's not null but all the strings are, the guids are 00..00 etc.
I've tried different variable types. I also tested FormMethod.Get and (not at the same time, of course) adding HttpPost attribute. No luck.
Asp.Net MVC binder system uses the name of the input elements to bind to the appropriate property or parameter. So, change this line:
<input type="text" value="#Model.Color" />
to:
#Html.TextBoxFor(m => m.Color)
This will generate following html(for example):
<input type="text" id="Color" name="Color" value="Black" />
Keep in mind that, you can use another helper which offers you to hard-type the name of the value:
#Html.TextBoxFor("Color")
Or, you can write plain html as you did and add name attribute, but let Asp.Net decide what must be the name of the element.
<input type = "text"
name = "#Html.NameFor(m => m.Color)"
value = "#Model.Color" />
If we want to summarize the result of the answer, then let's write Pros and Cons of each version:
Strongly typed version - These helpers can be used only with strongly typed views. The HTML generated by these helpers is not any different, but we use the strongly typed helper methods in our projects because they reduce the chances of causing an error by mistyping a property name.
Hard-typed version - The string argument is used to search the ViewData, ViewBag, and view model to find a corresponding data item that can beused as the basic for the input element. So, for example, if you call
#Html.TextBox("DataValue"), the MVC Framework tries to find some item of data that corresponds with the key DataValue. The following locations are checked: ViewBag.DataValue and Model.DataValue.
The first value that is found is used to set the value attribute of the generated HTML. (The last check, for #Model.DataValue, works only if the view model for the view contains a property or field called DataValue.)
If we specify a string like DataValue.First.Name, the search becomes more complicated. The MVC Framework will try different arrangements of the dot-separated elements, such as the following:
• ViewBag.DataValue.First.Name
• ViewBag.DataValue["First"].Name
• ViewBag.DataValue["First.Name"]
• ViewBag.DataValue["First"]["Name"]
Also keep in mind that, the first value that is found will be used, terminating
the search.
I am a newbie to MVC 4, (after 10 yrs of webforms) and have a question that I have not been able to figure out.
When writing code in the cshtml file, I am walking through a tutorial that has the following line:
#Html.DisplayNameFor(model => model.City)
What does the model => model.City imply? Why can't I use #Html.DisplayNameFor(model.City) ? I understand this is Linq query, but I would like to understand why would I need the model goes to model.city ?
Generally, that is called a lambda expression.In your scenario, you are telling the DisplayNameFor method that "take my model, and create a display element for this property.".You can't use model.City, because it just returns the value of the property.The method needs more than that in order to create a display element for your property.For example, it needs to know it's type and also it's attributes (like DisplayName attribute) and then it creates a display element for your element(it should be label I guess) .
DisplayName method is doing that using Expression Trees.The method takes an Expression<Func<TModel, TValue>> and uses it to get the name, value and the metadata information (attributes) about your property.
If you want to use model.City you can still use it, but then you won't need the functionality that DisplayNameFor provides.If you just need to display value of the property you can always do it like this:
<label> #model.City </label>
I understand this is Linq query,
Btw, this is incorrect, that is not a LINQ query.That is just an extension method.
I am using ASP.NET MVC4 with .NET Framework 4.5. I have a controller action that accepts a model of one type with a property named 'Name' but renders a view using a model of another type. I am still able to use #Html.TextBox("Name") and #Html.ValidationMessage("Name").
I want the textbox to display the sanitized input, that is, the input without leading/trailing/extra spaces the user may have entered. The setter for my model sanitizes the value for me, and I am successfully obtaining the sanitized value using the getter within the controller action. It's just that upon submitting the form, the textbox still displays the unclean input.
Is there some mechanism I am missing? Is the #Html.TextBox(string name) helper looking at the raw request data and not the model? If so, how come the validation message is working?
Update
I have just tried defining a new view model that includes my textbox field so I could hopefully just use the #Html.TextBoxFor helper. Everything is still working as it was after a re-build, I am still not getting sanitized input appearing in the textbox. I still don't know a solution for this.
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
public ActionResult MyAction(MyViewModel model)
{
if (this.ModelState.IsValid)
{
using (var service = new MyService())
{
model.MyResults = service.DoSomething(model.MySanitizedProperty);
}
}
return this.View("MyView", model);
}
Then, in "MyView":
#Html.TextBoxFor(m => m.MySanitizedProperty)
<input type="submit" value="Go" />
#Html.ValidationMessageFor(m => m.MySanitizedProperty)
In the controller, invoking model.MySanitizedProperty returns the sanitized value while the textbox goes on to display the unsanitized data.
It sounds like a problem with the models; make sure you are properly accessing the value from the model you wish to populate it with, i.e., possibly discretely specifying the model "Name" is coming from.
Also, check to see that the setter has a chance to operate on the value - if the controller is activating before the setter function is used, then you'll only get the original input value.
Realize you have to go to the server for the setter to work, possibly you need a async postback or such, and the value reloaded.
I'm struggling with the following problem and I can't find an acceptable way to solve it.
My challenge: write out HTML comments just before the actual property value in a Razor view.
This is my (simplyfied) Viewmodel:
public class Article
{
public string Title {get;set;}
}
To write out this title I simply do this in my Razor view:
<h2>#Model.Title</h2>
Now I want to write out a html comment just before the actual title so the generated HTML looks like this (simplyfied):
<h2><!-- some parameters for a 3th party system --> This is my title</h2>
The HTML comment comes from an Attribute I applied to the 'Title' attribute. It's value is generated, so the attribute-value is added at runtime using the TypeDescriptor from the .NET framework.
Now I know I could achieve this by simply writing out all my properties using an HTML helper. Like this: #MyHelper.Write(m => m.Title)
But since potentially ALL my properties need this HTML comment I want to avoid the use of an HTML helper since it clutters the View and doesn't make the view look nice and (more) readable.
This is what I have tried:
Created a custom Razor base page (Inheriting from WebViewPage<TModel>). And overwriting it's 'Write' method.
This kind of works but the BIGGEST problem here is that I don't know which property is been written out at that moment. There is no way of getting the current property name in the 'Write' method. So now I dynamically search my Model to find a property with the value that's been written out and prepend the HTML comment from the attribute.
My question: is there another approach to accomplish what I want. As sais before: I want to avoid using an HTML helper to write out all my properties. (Think about loops, etc. It's just not nice).
Also, adding this HTML comment in my Controller is no option since:
it's not part of the actual value. Is a sort of metadata.
The HTML comment should be added to int's, double's and DateTime's. There is no way to adjust a double property to include a string. (Image a List<DateTime>. All date's need this HTML comment)
the HTML comment should be added based on a web.config setting. Yes or No. (The actual HTML comment is different for each value of a property)
I realize this question is rather long. Sorry for that. Any thoughts are appreciated.
You can use the existing #Html.Raw(Model.Title)
Alternatively you can use a display templates. Add a UIHintAttribute to the properties you wish to behave this way.
public class MyModel
{
[UIHint("Raw")]
public string MyString { get; set; }
}
Create a new display template called Raw.cshtml that accepts model of type string:
#model string
#Html.Raw(model)
Then in your view you can use:
#Html.DisplayFor(m => m.MyString)
This still requires that you use a helper (DisplayFor). This is a recommended practice that allows you to easily change the behavior of one or many fields with minimal code changes.
I have an ASP.NET MVC application. At a certain point I get a FormCollection in a Controller method that I want to use to update a model. In the collection not all of the values are properties of that model and the property to be updated is an item from a list, and that list is also an item from another list. Something like this (I hope this is clear):
propertyToUpdate --> model.Items[0].Subitems[0].SomePropertyClass.Value;
I tried this in my Controller:
UpdateModel(model);
The problem is that this is not working and I assume it has something to do with the fact that the reflection is not working. I went searching and stumbled upon this article. So I understand that using the prefix-parameter solves the problem. But not in my case, as the properties lie "deeper" in the model as items from a list.
Does anyone know how I can solve this?
Update:
Here's the EditorTemplate for the property:
#model Q95.Domain.Property
<li>
#Html.DisplayFor(p => p.Description) :
#Html.DisplayFor(p => p.Quantity.Value)
#Html.DisplayFor(p => p.Quantity.Unit.Description)
<br />
#Html.TextBoxFor(p => p.Quantity.Value)
</li>
This template is called like this:
<ul>
#Html.EditorFor(model => model.SegmentRequirement.MaterialRequirements[j].Properties)
</ul>
Is this enough code or is there something still missing?
Update2:
Ok, in all the sub-properties I defined parameterless constructors and now I call:
UpdateModel(segmentRequirement, "SegmentRequirement", form.ToValueProvider());
This updates the model, but everything from MaterialRequirements is re-instantiated... :S
UpdateModel works fine on "Deep properties".
The problem is probably the data in the collection you get isn't equal to the properties names.
Check 3 places to see the values you get from the page
The form values.
The route data
The query string
In exact that order.
The keys should match you model properties names.
Update:
How to match the keys to properties names?
The input id will be the key you will get, change the the ids to match your properties names, or even better, use the HtmlTextBoxFor helper: see this article:
Maybe you should create flattened ViewModel and then use that to populate the view, and later synchronize it with the real model.
Can you show us your model and your view, if you are not using htmlhelper, you then have to understand the naming convention very well in order to make the model binding work with your model. so the first thing in first is to show us your model and view.