Quick MVC2 checkbox question - asp.net-mvc-2

In order to get my EF4 EntityCollection to bind with check box values, I have to manually create the check boxes in a loop like so:
<p>
<%: Html.Label("Platforms") %><br />
<% for(var i = 0; i < Model.AllPlatforms.Count; ++i)
{ %>
<%: Model.AllPlatforms[i].Platform.Name %> <input type="checkbox" name="PlatformIDs" value="<%: Model.AllPlatforms[i].Platform.PlatformID %>" /><br />
<% } %>
</p>
It works, but it doesn't automatically populate the group of check boxes with existing values when I'm editing a model entity. Can I fudge it with something like?
<p>
<%: Html.Label("Platforms") %><br />
<% for(var i = 0; i < Model.AllPlatforms.Count; ++i)
{ %>
<%: Model.AllPlatforms[i].Platform.Name %> <input type="checkbox" name="PlatformIDs" value="<%: Model.AllPlatforms[i].Platform.PlatformID %>" checked=<%: Model.GameData.Platforms.Any(p => PlatformID == i) ? "true" : "false" %> /><br />
<% } %>
</p>
I figure there has to be something along those lines which will work, and am just wondering if I'm on the right track.
EDIT: I'm purposely staying away from MVC's check box HTML helper methods as they're too inflexible for my needs. My check boxes use integers as their values by design.

Close. You'll actually want to modify your server-side code so that the "checked" attribute is not emitted at all unless you want the checkbox to be checked.
checked="true"
or
checked="false"
are technically both invalid HTML. Source.
If you want a checked checkbox, you want to emit:
checked="checked"
Any value in the quotes will actually check the box (including checked="false"), but checked="checked" is considered proper.
So, updating your code, just tweak the ternary operator to use checked='checked' or nothing at all.
<%: Model.AllPlatforms[i].Platform.Name %> <input type="checkbox" name="PlatformIDs" value="<%: Model.AllPlatforms[i].Platform.PlatformID %>" <%: Model.GameData.Platforms.Any(p => p.PlatformID == i) ? "checked='checked'" : "" %> /><br />

You're on the right track, but I think you need to modify the snipet to it to
<%: Model.GameData.Platforms.Any(p => PlatformID == i) ? "checked='true'" : string.Empty %>

Related

ASP.NET Html Extension not firing?

I have a couple of Extension classes, borrowed from various places, and they both work - individually. When I try to use both on the same page it appears one does not work. Here is the setup:
MVC 2 (no path for upgrading it to MVC 3 or 4)
HtmlPrefixScopeExtensions - http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/
FileBoxHtmlHelperExtension -
http://forums.asp.net/p/1566760/4033836.aspx
The .ascx page code looks like:
<%# Control Language="C#" AutoEventWireup="true" Inherits="System.Web.Mvc.ViewUserControl<PB.WMATA.ApplicationServices.ViewModels.Files.CIPDocumentAndFile>" %>
<%# Import Namespace="Company.Web.Extensions"%>
<div class="editorRow">
<% using(Html.BeginCollectionItem("docs")) { %>
<%= Html.Hidden("CIPDocument.Id", (Model != null) ? Model.Id : 0) %>
<label for="CIPNumber">Document Name:</label>
<%= Html.TextBox("CIPNumber", (Model != null) ? Model.CIPNumber : "", new { #size = "50", #maxlength = "255" })%>
<%= Html.ValidationMessage("CIPNumber")%>
<% if (Model != null && Model.FileName != null && Model.FileName.Length > 0) { %>
<label>Current File:</label>
<%= Model.FileName %>
<% } else { %>
<label>
File Upload:
<%= Html.FileBoxFor(m => m.HttpPostedFileBase) %>
</label>
<% } %>
delete
<% } %>
</div>
The output for this looks like:
<div class="editorRow">
<input name="docs.index" autocomplete="off" value="1809201d-2143-4da3-ba34-e443a332c516" type="hidden">
<input id="docs_1809201d-2143-4da3-ba34-e443a332c516__CIPDocument_Id" name="docs[1809201d-2143-4da3-ba34-e443a332c516].CIPDocument.Id" value="0" type="hidden">
<label for="CIPNumber">
Document Name:
</label>
<input id="docs_1809201d-2143-4da3-ba34-e443a332c516__CIPNumber" maxlength="255" name="docs[1809201d-2143-4da3-ba34-e443a332c516].CIPNumber" size="50" value="" type="text">
<label>
File Upload:
<input id="HttpPostedFileBase" name="HttpPostedFileBase" type="file">
</label>
<a href="#" class="deleteRow">
delete
</a>
</div>
Notice the FileUpload control did not get the HtmlPrefixScope. I expected it to be:
<input id="docs_1809201d-2143-4da3-ba34-e443a332c516__HttpPostedFileBase" name="docs[1809201d-2143-4da3-ba34-e443a332c516].HttpPostedFileBase" type="file">
I am not quite savvy enough with extensions to see what may be going on. I suspect that the collection extension is being handled before it tries to handle the filebox extension. Any ideas?
After digging in it turns out that I needed to get at the TemplateInfo.HtmlFieldPrefix value as the Html.BeginCollectionItem("docs") call had altered it. Was really simple once I understood the lifecycle of the TemplateInfo object. Here is the altered code for the FileBox & FileBoxFor code pieces:
public static MvcHtmlString FileBox(this HtmlHelper htmlHelper, string name, IDictionary<String, Object> htmlAttributes)
{
// If the HtmlFieldPrefix has been altered (see HtmlPrefixScopeExtensions class!!) then this will work with it...
var htmlFieldPrefix = htmlHelper.ViewData.TemplateInfo.HtmlFieldPrefix;
name = (!string.IsNullOrEmpty(htmlFieldPrefix) ? string.Format("{0}.", htmlFieldPrefix) : "") + name;
var tagBuilder = new TagBuilder("input");
tagBuilder.MergeAttributes(htmlAttributes);
tagBuilder.MergeAttribute("type", "file", true);
tagBuilder.MergeAttribute("name", name, true);
tagBuilder.GenerateId(name);
ModelState modelState;
if (htmlHelper.ViewData.ModelState.TryGetValue(name, out modelState))
{
if (modelState.Errors.Count > 0)
{
tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
}
}
return MvcHtmlString.Create(tagBuilder.ToString(TagRenderMode.SelfClosing));
}

Why Html.Hidden won't generate hidden fields with the same name but different value

I am trying to generate a hidden list, so I use hidden fields with same name, however Html.Hidden outputs the existing value instead of new one. So this code...
<%
for (int i = 0; i < Model.ProductIds.Count; i++)
{ %>
<%: Html.Hidden("ProductIds", Model.ProductIds[i], new { id=""})%>
<br />
Iteration:<%:i %>
Guid:<%:Model.ProductIds[i]%>
<br />
<% } %>
generates this HTML
<input name="ProductIds" type="hidden" value="48906f4c-1719-43ab-9d7e-c336a71b8624">
<br>
Iteration:0
Guid:48906f4c-1719-43ab-9d7e-c336a71b8624
<br>
<input name="ProductIds" type="hidden" value="48906f4c-1719-43ab-9d7e-c336a71b8624">
<br>
Iteration:1
Guid:b4f01496-dddf-41f2-a05b-43392d779a44
<br>
Note how even though the ids are different, the generated hidden fields got the same value. Why is this happening, and is there any way to work this around?
I'm not sure why this is happening, but an easy workaround is to just construct the html yourself:
<%
for (int i = 0; i < Model.ProductIds.Count; i++)
{ %>
<input name="ProductIds" type="hidden" value="<%:Model.ProductIds[i]%>">
<br />
Iteration:<%:i %>
Guid:<%:Model.ProductIds[i]%>
<br />
<% } %>
What is the purpose of a hidden list or multiple hidden fields with the same name? Couldnt you make it a single hidden field with all the values?

ASP.NET MVC - DropDownList Validation Problem

I've got two DropDownLists in a form on a page that contain the same values (a list of languages). I want to ensure that the user doesn't select the same value in each of the drop downs.
I've tried using JavaScript to ensure the selected values aren't the same, it works fine but the form submits anyway.
What's the best way to accomplish this?
Here's the code from my View:
<script type="text/javascript">
function CheckLanguageDDL()
{
var form = document.getElementById("form0");
var sourceLangIndex = form.SourceLanguage.selectedIndex;
var targetLangIndex = form.TargetLanguage.selectedIndex;
var strSourceLanguage = form.SourceLanguage.options[sourceLangIndex].text;
var strTargetLanguage = form.TargetLanguage.options[targetLangIndex].text;
if (strSourceLanguage == strTargetLanguage)
{
alert("Source Language and Target Language must be different!");
return;
}
}
</script>
<% Html.BeginForm("Index", "Translate", FormMethod.Post, new { enctype = "multipart/form-data" }); %>
<fieldset>
<legend>Request</legend>
<br />
<div class="editor-label">
<%: Html.LabelFor(m => m.SourceLanguage) %>:
</div>
<div class="editor-field">
<%: Html.DropDownList("SourceLanguage", (IEnumerable<SelectListItem>)ViewData["SourceLanguages"]) %>
<%: Html.ValidationMessageFor(m => m.SourceLanguage) %>
</div>
<br />
<div class="editor-label">
<%: Html.LabelFor(m => m.TargetLanguage) %>:
</div>
<div class="editor-field">
<%: Html.DropDownList("TargetLanguage", (IEnumerable<SelectListItem>)ViewData["TargetLanguages"]) %>
<%: Html.ValidationMessageFor(m => m.TargetLanguage) %>
</div>
<input type="submit" value="Submit Request" onclick="CheckLanguageDDL();" />
</p>
</fieldset>
Thanks.
Make the function return a true/false value that the form submit use that return value
function CheckLanguageDDL()
{
var form = document.getElementById("form0");
var sourceLangIndex = form.SourceLanguage.selectedIndex;
var targetLangIndex = form.TargetLanguage.selectedIndex;
var strSourceLanguage = form.SourceLanguage.options[sourceLangIndex].text;
var strTargetLanguage = form.TargetLanguage.options[targetLangIndex].text;
if (strSourceLanguage == strTargetLanguage)
{
return false;
}
return true;
}
And on the button:
onclick="return CheckLanguageDDL();"

How can I create a DropDownList?

I have two tables: Area and Boss.
An Area has a Boss, it has a foreign key relationship of Area.IDBoss and that is Boss.ID. I hope I'm explaining my question properly. :P
As of now I can manually type in a number in the textbox and it saves correctly, I can also display the name correctly because I'm using Entity Framework, something like "item.Boss.ID" in the View and it works fine.
I really need to display a DropDownList though.
I'm guessing I have to return a collection of available "Boss" rows from my database using the ViewData[] dictionary, but I'm honestly stuck. :D
Any suggestions? This is a very simple use case, so hopefully I don't spend too much time on it. If you guys need any code from my part just say, but I doubt it would help for such a simple question.
As always this site is fantastic, thanks for the help.
Edit:
Maybe posting some code would help. Right now I'm receiving this error:
Edit 2:
Edited with more recent code that is still not working.
There is no ViewData item of type
'IEnumerable' that has
the key 'Jefes'.
And here's the code:
<fieldset>
<legend>Fields</legend>
<div class="editor-label">
<%: Html.LabelFor(model => model.IDJefe) %>
</div>
<div class="editor-field">
<%: Html.DropDownList("Jefes", (SelectList)ViewData["Jefes"]) %>
<%--<%: Html.TextBoxFor(model => model.IDJefe) %>--%>
<%: Html.ValidationMessageFor(model => model.IDJefe) %>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.Nombre) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Nombre) %>
<%: Html.ValidationMessageFor(model => model.Nombre) %>
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
JefeRepository jefeRepo = new JefeRepository();
var jefes = jefeRepo.FindAll().OrderBy(x => x.Nombre);
var jefeList = new List<SelectListItem>();
foreach (var jefe in jefes)
{
jefeList.Add(new SelectListItem()
{
Text = jefe.Nombre,
Value = jefe.ID.ToString()
});
}
ViewData["Jefes"] = jefeList.AsEnumerable();
By the way I translated some variable names from Spanish to English so they would make more sense, sorry for the confusion.
Edit 3:
If you guys need any more information please let me know. I've looked over every line of code but it just doesn't work.
You can put an IEnumerable<SelectListItem> in your ViewData[].
var bossDropDown = from boss Area.Boss
select new SelectListItem
{
Value = boss.ID,
Text = boss.Name
};
ViewData["BossDropDown"] = bossDropDown;
and in your View you can call directly your DropDown like this
<%=Html.DropDownList("BossDropDown", (SelectList)ViewData["BossDropDown"]) %>
EDIT:
With your code, try to change this line
ViewData["jefes"] = jefeList;
to this
ViewData["jefes"] = jefeList.AsEnumerable();
In your view, Change this line
<%: Html.DropDownList("IDJefe", (SelectList)ViewData["jefes"]) %>
to this
<%: Html.DropDownList("jefes", (SelectList)ViewData["jefes"]) %>
since, the name you are calling in your dropdownlist should be the Key of your ViewData that has the IEnumerable<SelectListItem>
do this
JefeRepository jefeRepo = new JefeRepository();
var jefes = jefeRepo.FindAll().OrderBy(x => x.Nombre);
List<SelectListItem> jefeList = new List<SelectListItem>();
foreach (var jefe in jefes)
{
jefeList.Add(new SelectListItem()
{
Text = jefe.Nombre,
Value = jefe.ID.ToString()
});
}
ViewData["Jefes"] = jefeList;
then
<div class="editor-label">
<%: Html.LabelFor(model => model.IDJefe) %>
</div>
<div class="editor-field">
<%: Html.DropDownList("Jefes",ViewData["Jefes"] as List<SelectListItem>, "Select a boss ...") %>
<%: Html.ValidationMessageFor(model => model.IDJefe) %>
</div>
your mistake: you cant converst a List to a Selectlist, this doesnt work.
but honestly, i'd do it like this:
JefeRepository jefeRepo = new JefeRepository();
var jefes = jefeRepo.FindAll().OrderBy(x => x.Nombre);
ViewData["Jefes"] = new SelectList(jefes,"ID", "Nombre");
then
<div class="editor-label">
<%: Html.LabelFor(model => model.IDJefe) %>
</div>
<div class="editor-field">
<%: Html.DropDownList("Jefes",ViewData["Jefes"] as SelectList, "Select a boss ...") %>
<%: Html.ValidationMessageFor(model => model.IDJefe) %>
</div>
let me know which one works best for ya ;)

In ASP.NET MVC 2 RC 2, Is it possible to remove the name="" attribute and leave the id attribute?

If you do this in Asp.Net MVC 2 RC 2:
<% for (int count = 0; count < Model.Students.Count; count++ )
{ %><%=
Html.EditorFor(m => m.Students[count]) %><%
}
%>
where Students is a List<Student>, and it produces this:
<input class="text-box single-line" id="Students_0__Name" name="Students[0].Name" type="text" value="Harry" />
<input class="text-box single-line" id="Students_1__Name" name="Students[1].Name" type="text" value="Tom" />
<input class="text-box single-line" id="Students_2__Name" name="Students[2].Name" type="text" value="Richard" />
Is there a way to remove the name="" attribute without breaking the model binding when they post?
In html, for an element within a form to be posted, it requires a name attribute. So, no, there is no way to remove the name attribute.
Here's where it is defined in the official definition...
http://www.w3.org/MarkUp/html-spec/html-spec_8.html
:-)