I am working on an ASP.NET MVC3 application and I cannot get a DropDownListFor to work with my property in a particular editor view.
My model is a Person and a person has a property that specifies it's "Person Type". PersonType is a class that contains a name/description and an ID. I can access the available PersonTypes within the application through my static/shared class called ApplicationSettings.
In the Edit Template view for my Person I have created a SelectList for debugging purposes:
#ModelType MyNamespace.Person
#Code
Dim pTypesSelectList As New SelectList(MyNamespace.ApplicationSettings.PersonTypes, "ID", "Name", Model.PersonType.ID)
End Code
I am then providing this SelectList as a parameter to the DropDownListFor that is bound to the PersonType property of my Person.
I am also printing the Selected property of each item in the SelectList for debugging purposes:
<div style="text-align: center; margin: 5px 0 0 0;">
<div>
#Html.LabelFor(Function(model) model.PersonType)
</div>
<div>
#Html.DropDownListFor(Function(model) model.PersonType, pTypesSelectList)
#Html.ValidationMessageFor(Function(model) model.PersonType)
<br />
<br />
<!-- The following is debugging code that shows the actual value-->
#Model.Type.Name
<br />
#Model.Type.ID
<br />
<br />
<!--This section is to show that the select list has properly selected the value-->
#For Each pitem In pTypesSelectList
#<div>
#pitem.Text selected: #pitem.Selected
</div>
Next
</div>
</div>
The view is bound to a Person whose PersonType property is "Person Type # 2" and I expect this to be selected; however the HTML output of this code looks like this:
<div style="text-align: center; margin: 5px 0 0 0;">
<div>
<label for="PersonType">PersonType</label>
</div>
<div>
<select id="PersonType" name="PersonType">
<option value="7e750688-7e00-eeee-0000-007e7506887e">Default Person Type</option>
<option value="87e5f686-990e-5151-0151-65fa7506887e">Person Type # 1</option>
<option value="a7b91cb6-2048-4b5b-8b60-a1456ba4134a">Person Type # 2</option>
<option value="8a147405-8725-4b53-b4b8-3541c2391ca9">Person Type # 3</option>
</select>
<span class="field-validation-valid" data-valmsg-for="PersonType" data-valmsg-replace="true"></span>
<br />
<br />
<!-- The following is debugging code that shows the actual value-->
Person Type # 2
<br />
a7b91cb6-2048-4b5b-8b60-a1456ba4134a
<br />
<br />
<!--This section is to show that the select list has properly selected the value-->
<div>
Default Person Type selected: False
</div>
<div>
Person Type # 1 selected: False
</div>
<div>
Person Type # 2 selected: True
</div>
<div>
Person Type # 3 selected: False
</div>
</div>
</div>
As you can see the printed Selected properties for the items in the SelectList shows that the 3rd item is "Selected". But what is driving me crazy is that the option that corresponds with this is Not Selected.
Generally, the Selected property in SelectList will be totally ignored by the HTML helpers unless there's no other option. If DropDownListFor can find the value by other means, it will insist on using that value.
In this case, it will use the value of model.PersonType(.ToString()) - but that's not what you want, judging by the model.PersonType.ID you pass to the SelectList.
More info in the answer here.
Workaround
One easy workaround that should work would be to set:
ViewData["PersonType"] = model.PersonType.Id.
The helper looks in ModelState first if it exists - i.e. on POST. This should work already, since ModelState["PersonType"] will be populated with the actual selected value that was posted.
After ModelState it will look in ViewData - with ViewData["PersonType"] first, and only then ViewData.Model.PersonType. In other words, you can "override" the value on your model with a value set directly on ViewData.
Better (IMO) solution
The more general, "better practice", way to solve it (which also avoids having a custom model binder in order to translate the POST'ed ID back to PersonType) is to use a ViewModel instead of working with full models in your view:
Have a PersonTypeID property - instead of PersonType.
Populate it with PersonType.ID
use this in your view
VB.NET: Html.DropDownListFor(Function(model) model.PersonTypeID), or
C#: Html.DropDownListFor(model => model.PersonTypeID)
When form is POST'ed, translate the ViewModel (including PersonTypeID => PersonType) back into the actual model in your POST Action.
This may seem like more work, but generally there tend to be many occasions in a project where you need more view-specific representations of your data to avoid too much inline Razor code - so translating from business objects to view models, while it may seem redundant and anti-DRY at times, tends to spare you of a lot of headaches.
Are you sure that your ModelState for "PersonType" key before rendering the view is empty? As JimmiTh commented is going to search for the value in the ModelState first. It happened to me too, you can try
#Html.DropDownList("PersonTypeFake", Function(model) model.PersonType, pTypesSelectList) and it should select the right option.
Related
I have a strongly typed partial view. The model has a property that is a list of users in Active Directory represented by a class called ADUser.
I have a partial view that represents a drop down list for this property. While I use the value of this drop list for some other things, I have no need to submit its value, so I thought I would remove the name attribute. However, once the Html loads, the name attribute is always set to what the Html helper wants to assign it. Is there a way I can remove that attribute so that the drop down's value doesn't submit?
In my main partial view (_adusers is the name of the list):
<%: Html.Partial("ADUserDropDown", Model._adusers)%>
In my drop down's partial view:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<List<MyProject.Models.ADUser>>" %>
<%= Html.DropDownList("", new SelectList(Model, "ValueText", "DisplayText"), new { name = "", size = "12" })%>
<input type=button value="Search..." />
<input type=button value="Close" />
This results in:
<select name="_master" size="12">
_master being the name of the parent model. If I add something to the first parameter of Html.DropDownList, it results in:
<select name="_master.WhateverINamedIt" size="12">
I want to achieve:
<select size="12">
There is no point of using the HTML helper then. You can change the code of the partial view to:
<select size="12">
#foreach(var item in Model)
{
<option value="#item.ValueText">#Html.DisplayFor(m => item.DisplayText)</option>
}
</select>
However if you want to stick with the DropDownList helper the name parameter in the html attributes has to be specified with the capital letter:
<%= Html.DropDownList("", new SelectList(Model, "ValueText", "DisplayText"),
new { Name = string.Empty, size = "12" })%>
As an alternative you can remove the name with jQuery:
$('[name="_master.WhateverINamedIt"]').removeAttr('name');
Don't like or use jQuery, the plain javascript will do the job:
document.getElementsByName('_master.WhateverINamedIt')[0].removeAttribute('name');
is there a way to remove the table out of rendered html that is created by the h:selectManyCheckbox tag in server faces?
I am using twitter bootstrap and i placed the checkboxes inside a dropdown menu:
<ul class="dropdown-menu dropdown-menu-form">
<li><label class="checkbox"> <input type="checkbox" />
Activated
</label></li>
<li><label class="checkbox"> <input type="checkbox" />
Deactivated
</label></li>
</ul>
So the generated html table destroys the layout ...
You could just render a bunch of <h:outputLabel><h:selectBooleanCheckbox> inside <ui:repeat>. This way you've markup freedom. You'll only need to alter the model from e.g. List<T> to Map<T, Boolean> to represent the checked values and then loop over the map afterwards to collect checked ones.
A ready-to-use component is Tomahawk's <t:selectManyCheckbox> which has an additional layout attribute value of spread.
I am looking for a solution which creates dynamic properties to be create in Model.
I want to use them in my View and Controller.
Can any one have idea, how to create it?
I am having scenario in my project where one page will be having options to be lets say Profile2, Profile5 etc.
Profile 2 can have two URLs to be submit from user.
Profile 5 can have five URLs to be submit from user.
and
So on........
Is there any solution or alternative to do this????
He Amit , For your situation "I am having scenario in my project where one page will be having options to be lets say Profile2, Profile5 etc.
Profile 2 can have two URLs to be submit from user.
Profile 5 can have five URLs to be submit from use"
You want to put this url in your properties ok.
SO do one thing create a property like this.
public List<string> urlList {get;set;}
use this in your property andd add url in the list.
you can add n no of urls.
That is exactly what ViewBag is for. Its a dynamic property on Controller and View.
public ActionResult SomeAction()
{
ViewBag.Message = "Hello, world";
}
<p>#ViewBag.Message</p>
This will allow you to send anonymous property values from your Controller to your View. However, if you're looking to post different numbers of values (urls in your example), you should use an IList as your model.
#model IList<string>
#for (int i = 0; i < Model.Count; i++)
{
#Html.EditorFor(model => model[i])
}
Your model should probably store the values in a list. Here is an example explaining how to display and save data for a list property.
How to interact with List<t> in MVC
see this is what i have done as an alternative.
Make all divs and other fields in MODEL and use jQuery to work around.
I guess this is an alternative, but not exactly what i want. Still looking for answer. I post this as this can be helpful to some one in future.
Please check below.
<div>
#for (var i = 0; i < ProfileCount; i++)
{
<label>
URL:</label>
<input type="text" id=#string.Format("URL{0}", i) />
<label>
CheckName:</label>
<input type="text" id=#string.Format("URL{0}CheckName", i) />
<label>
Run Check From:</label>
#Html.DropDownList(string.Format("URL{0}Region", i), (IEnumerable<SelectListItem>)ViewBag.Regions)
<br />
<span id=#string.Format("URL{0}Result", i)></span>
<input type="button" value="Create check" id=#string.Format("URL{0}CheckSetup", i) onclick="getResponseFromUrl('#string.Format("URL{0}')", i);" />
<input type="button" value="Delete check" id=#string.Format("URL{0}Delete", i) onclick="DeleteCheck('#string.Format("URL{0}')", i);"
style="display: none" />
<input type="hidden" id=#string.Format("URL{0}Hidden", i) />
<br />
<br />
<br />
}
</div>
How can I add an index, to input names and ids in forms that are used multiple times in one view?
I have created a form for photo rotator that provides the ability to edit a context-specific caption for each photo (billboard). I need to be able to include multiple instances of the form fields for this so the admins can edit all of the captions for a rotator's set of photos in one view, and so I need a way to keep ids and field names unique.
Editor templates automatically add a prefix, but when I loop over the photos like this:
<% foreach (var billboard in Model.Billboards ) { %>
<%: Html.EditorFor(x => billboard, "BillboardForm")%>
<% } %>
It simply adds "billboard_" as the prefix, which doesn't solve my problem.
I'd like to append the rotator id billboard id to the end of each input name and id:
<form action="/Rotators/Edit/5" method="post">
<input id="billboard_21_RotatorId" name="billboard_21_RotatorId" type="hidden" value="5" />
<input id="billboard_21_ImageId" name="billboard_21_ImageId" type="hidden" value="19" />
<label for="billboard_21_Title">Title</label>
<textarea cols="20" id="billboard_21_Title" name="billboard_21_Title" rows="2">Title</textarea>
<label for="billboard_21_Caption">Caption</label>
<textarea cols="20" id="billboard_21_Caption" name="billboard_21_Caption" rows="2">This is the caption</textarea>
<select id="billboard_21_TopicId" name="billboard_21_TopicId">
<option value="1">1st option</option>
</select>
</form>
Any easy way to do this??
plz download this sampel code from steve sanderson's blog post. it does not directly relate to your question. But in demo project you will find BeginCollectionItem html helper that is changing the prefix scope for input or series of inputs. This can give u a starting point
Edit:
in ur editor template u can use following method from steve's code like
using(Html.BeginHtmlFieldPrefixScope("BillBoard" + Model.ID.ToString())){
<label>Image<label>
#Html.TextBoxFor(x=>x.Image)
<label>Caption</label>
#Html.TextBoxFor(x=>x.Caption)
}
if ID is property of your model and has value e.g 4 then u will have html like
<label>Image</label>
<input name = "BillBoard4.Image" .../>
<label>Caption</label>
<input name = "BillBoard4.Caption" .../>
Note: The features used below may not have existed 4 years ago...
Firstly, you don't have to use beestings any more, the # syntax used
in the Razor examples is far cleaner.
The method you're calling is in System.Web.Mvc.Html.EditorExtensions:
public static MvcHtmlString EditorFor<TModel, TValue>(
this HtmlHelper<TModel> html,
Expression<Func<TModel, TValue>> expression
)
...
Your approach:
#foreach (var billboard in Model.Billboards ) {
#Html.EditorFor(x => billboard, "BillboardForm")
}
The body of expression x => billboard is a ConstantExpression.
This approach results in the appropriate scope applying in the EditorTemplate:
#for (var i = 0; i < Model.BillBoards.Count(); i++)
{
#Html.EditorFor(x => Model.BillBoards[i], "BillboardForm")
}
If Model.BillBoards is an array, the expression x => Model.BillBoards[i] can be described as
SimpleBinaryExpression{NodeType:ArrayIndex}(
Left: ConstantExpression,
Right: ConstantExpression
)
If Model.BillBoards is an IList<T>, the expression x => Model.BillBoards[i] can be described as
InstanceMethodCallExpressionN(
Method:RuntimeMethodInfo(IList<T>.get_Item (Int32 index)),
Object:PropertyExpression(ConstantExpression),
Arguments:[ConstantExpression]
)
The overloads of EditorFor() that accept Expressions check the expression body's Type and NodeType and construct the scope accordingly.
This code should be equivalent if you don't have anything else inside the loop:
#Html.EditorFor(x => Model.BillBoards, "BillboardForm")
If you only have a read-only view and an editing view, you can rename your templates and remove the second parameter. Assuming BillBoard is your Model class, rename BillboardForm.cshtml to EditorTemplates/BillBoard.cshtml, then change the code to
#Html.EditorFor(x => Model.BillBoards)
The code
<%=Html.CheckBox("SendEmail") %>
evaluates to two HTML elements when it's rendered
<input id="SendEmail" name="SendEmail" type="checkbox" value="true" />
<input name="SendEmail" type="hidden" value="false" />
Is this by a bug? Or by design? If it's by design, why?
I was having some trouble this morning with retrieving selected values from a checkbox group in my MVC app. I sent this out to my team and thought I'd share it with everyone else.
When posting back values for checkboxes, the standard behaviour of all browsers is to completely leave out un-checked checkboxes from the post back. So if you have a checkbox that isn’t checked then nothing shows up for this in Request.Form. This is a fairly well-known phenomenon that most developers account for.
In ASP.Net MVC when you use Html.Checkbox, it attempts to get around this and ensure that you have a value posted back (in this case ‘false’) for un-checked checkboxes. This is done by adding a hidden field containing the value ‘false’.
Eg.
<%= Html.CheckBox("Sites", s.Selected, new { value = s.Value })%>
Produces the HTML
<input id="Sites" name="Sites" type="checkbox" value="1" /><input name="Sites" type="hidden" value="false" />
This is all good and well, until you attempt to use checkbox groups. That is more than one checkbox with the same name where the values are sent back in a single comma separated string.
MVC can split this string up for you automatically if you define the value as an array (string[] Sites).
Here’s the view code:
<% foreach(var s in Model) { %>
<li><%= Html.CheckBox("Sites", s.Selected, new { value = s.Value })%>
<label><%= s.Name %></label>
</li>
<% } %>
The appropriate controller action:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int Id, string Name, string SubmissionUrl, string[] Sites)
Unfortunately, because the Html.Checkbox produces this hidden field value as well, the resulting array contains not only values for the selected checkboxes but also ‘false’ for every un-checked checkbox. You get an array that looks something like this:
[0] 'false'
[1] 'false'
[2] '110'
[3] '50'
[4] 'false'
Where 'false' is for checkboxes that are not selected, and integers are the values for the checkboxes that are selected.
This can throw your code out quite a bit if you have only integers for the values of your checkboxes and want to define the result as an array of integers, like so:
public ActionResult Edit(int Id, string Name, string SubmissionUrl, int[] Sites)
Which results in an exception being thrown because it can’t convert the string value ‘false’ to an integer.
The solution is very simple, just avoid Html.Checkbox and manually create the checkboxes in your view code like this:
<% foreach(var s in Model) { %>
<li><input type="checkbox" name="Sites" value="<%=s.Value%>" <% if (s.Selected) { %>checked="checked"<% } %> />
<label><%= s.Name %></label>
</li>
<% } %>
Hope this helps someone else!
When all else fails, read the source code. :) this is from HtmlHelper.cs:
// Render an additional <input type="hidden".../> for checkboxes. This
// addresses scenarios where unchecked checkboxes are not sent in the request.
// Sending a hidden input makes it possible to know that the checkbox was present
// on the page when the request was submitted.
I'm not exactly sure how useful that is, but at least you know the intention.
I think I found something on the web that is directly related to my question.