How to alternate row style without javascript for DisplayFor/EditorFor bound to a list of model - asp.net-mvc-3

How can I pass the index of the current element to the View for DisplayFor/EditorFor so that I can decide if the row should show the alternate style or not?
My main view looks like this:
<table>
#Html.EditorFor(model => model.MyListOfItems)
</table>
The view used for the EditorFor looks like:
#Html.HiddenFor(model => model.IdOfTheItem)
<tr class="shouldBeChangedDependingOnRowEvenOrNot">
<td>#Html.CheckBoxFor(model => model.MarkForBatchEdit)</td>
<td>#Html.DisplayFor(model => model.NameOfThisItem)</td>
<td>#Html.DisplayFor(model => model.StateOfThisItem)</td>
</tr>
Well I am aware of this similar question and the suggested solution: alternating row color MVC
But I can't apply them to this case.
Any suggestions?

The EditorFor is overloaded to take an additionalViewData parameter, so you could pass the index in the ViewData, which is a collection of key/value pairs.
#Html.EditorFor( model => model.MyListOfItems , new { CurrentIndex = SomeNumber } )
In your view you would get the value using ViewData["CurrentIndex"].
Also, instead of passing the element index, why not do the calculation in your controller and pass whether you have an even or odd row in your ViewData.
bool isEvenRow = ((CurrentElementIndex % 2) == 0);
ViewData["isEvenRow"] = isEvenRow;
Then you will just toggle your CSS in the view based on whether the value is true or false.

Related

Selecting an alternate EditorFor template for a List

I have an object that represents a food item to order at a restaurant. This object has a list of Modifier Groups (sides, cooking instructions, pizza toppings, whatever) and each list has a list of Modifiers.
Certain Modifier options need to be displayed differently (for example, toppings need to specify left/right/all), even though they are the same data type.
I am trying use #Html.EditorFor and specify the alternate EditorTemplate when required.
In /Views/Shared/EditorTemplates I have ModifierSelection.cshtml and ToppingSelection.cshtml. I am calling them in the following view:
#model MyApp.ViewModels.ModifierSelectionList
<div class="menugroup">
<h3 class="menuname">#Model.ModifierListName: (Select #Model.MaximumSelections)</h3>
<div class="modcountvalidation">#Model.ValidationResults</div>
#Html.HiddenFor(model => Model.ModifierListId)
<table class="menu">
#if (Model.IsToppingsList)
{
#Html.EditorFor(model => Model.ModifierSelections, "ToppingSelection")
}
else
{
#Html.EditorFor(model => Model.ModifierSelections)
}
</table>
</div>
When I try to display an item that requires the "ToppingSelection" EditorTemplate instead of the default, I get the following error:
System.InvalidOperationException was unhandled by user code
Message=The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[MyApp.ViewModels.ModifierSelection]', but this dictionary requires a model item of type 'MyApp.ViewModels.ModifierSelection'.
Source=System.Web.Mvc
So - I have a set of EditorTemplates for a data type. I am trying to use them to display a list of items and I need to be able to select which one to use.
What am I doing wrong?
Thanks!
OK, here is the real solution. Rather than iterating through the list using foreach, I had to iterate using a for loop.
#for (int i = 0; i < Model.ModifierSelections.Count; i++ )
{
if (Model.IsToppingsList)
{
#Html.EditorFor(m => Model.ModifierSelections[i], "ToppingSelection")
}
else
{
#Html.EditorFor(m => Model.ModifierSelections[i])
}
}
Solved!
Apparently, if you send a list type to Html.EditorFor and do not specify a template, it will iterate through the list and display each item using the template that it finds for the item type. If you do specify a template, it will not iterate through the list and send each item to that template, it will attempt to send the entire list to your template, which isn't the right data type.
I fixed it by manually iterating through the list:
#foreach (var modifierSelection in Model.ModifierSelections)
{
if (Model.IsToppingsList)
{
#Html.EditorFor(m => modifierSelection, "ToppingSelection")
}
else
{
#Html.EditorFor(m => modifierSelection)
}
}

mvc3 dropdownlist reload and set value

In Edit view:
I am passing ViewBag list for the ddl
Controller:
ViewBag.CountryList = new SelectList(db.Country, "CountryCode", "Desc");
in edit view:
How do I assign the viewbag to the ddl and set the value that is coming from the model
#Html.EditorFor(model => model.CountryCode) <- contains the Desc Value, currently just shows in a textbox!
thx!
use #Html.DropDownListFor. If you read the parameters that C# specifies you need, its self explanitory. The fact you have the selectList in the viewbag already means you've nearly done it all yourself already.
#Html.DropDownListFor(model => model.CountryCode, ViewBag.CountryList);
If that doesnt set the correct value for any reason, you can also use
#Html.DropDownList("CountryCode", ViewBag.CountryList, new { #value = #Model.CountryCode });

Editing a collection in a partial view

I was having problems updating child collections of my object ( Foreign key constraint, EF with collection of childobjects ) which was solved using this guide: http://www.codetuning.net/blog/post/Binding-Model-Graphs-with-ASPNETMVC.aspx
When cleaning up my code, I moved the collection editing to a partial view.
#Html.Partial("_AttendeeInformationFields", Model.CaptureAttendeeInformationFields)
The partial view looks like this
#model ICollection<EventModel.Models.AttendeeInformationField>
<table id="CaptureAttendeeInformationFields">
<tr>
<th>#Html.GetDisplayName(model => model.FirstOrDefault().Name)</th>
<th>#Html.GetDisplayName(model => model.FirstOrDefault().Required)</th>
<th>#Html.GetDisplayName(model => model.FirstOrDefault().FieldType)</th>
#*<th>#Html.GetDisplayName(model => model.FirstOrDefault().InputType)</th>*#
</tr>
#Html.EditorForModel()
</table>
#Html.LinkToAddNestedForm("Lägg till", "#CaptureAttendeeInformationFields", ".AttendeeInformationField", "CaptureAttendeeInformationFields", typeof(EventModel.Models.AttendeeInformationField))
#Html.ValidationMessageFor(model => model)
Then I have a EditorTemplate for AttendeeInformationField that looks like this
#model EventModel.Models.AttendeeInformationField
<tr class="AttendeeInformationField">
#using (Html.BeginCollectionItem("CaptureAttendeeInformationFields"))
{
<td>#Html.TextBoxFor(model => model.Name) #Html.HiddenFor(model => model.MagnetEventId) #Html.HiddenFor(model => model.Id)</td>
<td>#Html.CheckBoxFor(model => model.Required)</td>
<td>#Html.DropDownListFor(model => model.FieldType, new SelectList(Enum.GetValues(typeof(EventModel.Models.FieldType)), Model.FieldType))</td>
#*<td>#Html.TextBoxFor(model => model.InputType)</td>*#
}
</tr>
The BeginCollectionItem is from this guide: http://ivanz.com/2011/06/16/editing-variable-length-reorderable-collections-in-asp-net-mvc-part-1/
This helps me with two things
1. The index is no longer a sequential 0-based integer series, and I can reorder my items, as well as add/delete without worrying about breaking the sequence.
2. The partial seems to loose the context, and my controls get names like "[0].Required" where it should be "CaptureAttendeeInformationField[0].Required". The BeginCollectionItem takes care of this.
The current problem is that these fixes doesn't seem to be compatible. I suppose it might have something to do with this disclaimer in the first article:
within this implementation we assume that the index is an integer starting at 0
I'm hoping that someone can point me in the right direction.
Adding items in this solution works.
Ugly solution
I sure hope this is not the only way to do this, but for now I've solved the problem like this:
foreach (var attendeeInformationField in viewModel.AttendeeInformationFields)
{
var attendeeInformationFieldId = attendeeInformationField.Id;
var originalAttendeeInformationField = original.CaptureAttendeeInformationFields.FirstOrDefault(aif => aif.Id == attendeeInformationFieldId);
if (originalAttendeeInformationField==null)
{
original.CaptureAttendeeInformationFields.Add(attendeeInformationField);
}
else
{
if (originalAttendeeInformationField != attendeeInformationField)
{
originalAttendeeInformationField = attendeeInformationField;
originalAttendeeInformationField.FieldType = attendeeInformationField.FieldType;
//originalAttendeeInformationField.InputType = attendeeInformationField.InputType;
originalAttendeeInformationField.Name = attendeeInformationField.Name;
originalAttendeeInformationField.Required = attendeeInformationField.Required;
}
}
}
I don't like it at all, but it works. There must be a better way of doing this.

MVC3 HTML Helper for large Text area

I have an html helper:
#Html.EditorFor(model => model.Description)
But it is too small for the data in that property of my Model. Descriptino is a 1000 character string. I need the user to be able to enter several lines of text and have it wrap in the HTML object. How do I do this?
Try
Html.TextAreaFor(model => model.Description, new {#cols="80" , #rows="4" })
Use:
#Html.TextAreaFor(model => model.Description)
// or a full option-list is:
#Html.TextAreaFor(model => model.Description,
rows, // the rows attribute of textarea for example: 4
columns, // the cols attribute of textarea for example: 40
new { }) // htmlAttributes to add to textarea for example: #class = "my-css-class"
Notice: you can use null instead of new { } for htmlAttributes but it is not recommended! It's strongly recommended that use a blank new { } -that represents a new object-
You can use EditorFor, but in that case it's better to define your own EditorTemplate for rendering your TextArea, using TextAreaFor or whatever it's needed.
The main difference between the TextAreaFor and EditorFor is that, if I've understood well how everything works, when using EditorFor, Templates are taken into account, while when using TextAreaFor you choose the HTML Input used for rendering.
Templates seems interesting, I'm just starting digging into writing my own.
Sounds like you're after Html.TextAreaFor.

MVC 3 EditorTemplate - Model properties are empty

I have a number of custom EditorTemplates for various model classes. Inside these templates I obviously need to reference the properties of the model. My problem is that when I use the direct syntax of Model.Id (for example), the value is null. Another example is Model.Name which returns an empty string. However, when I reference the model in an expression (eg. #Html.TextBoxFor(i => i.Name)) then the values are there.
To further illustrate, here is a code snippet:
#model Vigilaris.Booking.Services.CompanyDTO
<div>
<fieldset class="editfieldset">
<legend class="titlelegend">Company Details</legend>
<ol>
<li>
#Html.TextBox("tb1", #Model.Id)
#Html.TextBox("tb2", #Model.Name)
</li>
<li>
#Html.LabelFor(i => i.CreatedDate)
#Html.DisplayFor(i => i.CreatedDate)
</li>
<li>
#Html.LabelFor(i => i.Name)
#Html.TextBoxFor(i => i.Name)
</li>
<li>
#Html.LabelFor(i => i.Description)
#Html.TextAreaFor(i => i.Description)
</li>
<li>
#Html.LabelFor(i => i.Phone)
#Html.TextBoxFor(i => i.Phone)
</li>
</ol>
</fieldset>
</div>
In this example, all the code that is using the LabelFor and DisplayFor helper functions displays the data from the model. However, the Html.TextBox code portion returns 0 for Model.Id and empty string for Name.
Why does it not access the actual model data when I reference Model directly?
I am unable to reproduce this. You might need to provide more context (controllers, views, ...). Also shouldn't your textbox be named like this:
#Html.TextBox("Id", Model.Id)
#Html.TextBox("Name", Model.Name)
and also why not using the strongly typed version directly:
#Html.TextBoxFor(x => x.Id)
#Html.TextBox(x => x.Name)
I managed to figure this one out. One thing I left out in my problem description was that I am using Telerik MVC Grid extension and the EditorTemplate is being using for In-form editing. So, the Model properties are not available at this point and this is understandable behaviour. I had to use a client side onEdit event on the Telerik MVC Grid and then set these values as necessary.
How I remember solving this is that I added a ClientEvent in my Telerik MVC Grid as follows:
.ClientEvents(events => events.OnEdit("Users_onEdit"))
This tells the grid to run my javascript function called Users_onEdit when an edit is triggered. Then, in my javascript function I find the field I want and then set its value. Here is an code excerpt:
function Users_onEdit(e) {
if (e.mode == "insert") {
$("#UserName").removeAttr("readonly");
$("#UserName").removeAttr("title");
$("#divNewUserMessage").show();
var formId = String(e.form.id);
var formIndex = formId.indexOf("form");
var companyId = formId.substr(6, formIndex -6);
var hiddenCompanyId = $(e.form).find("#CompanyId");
hiddenCompanyId.val(companyId);
}
}
I hope this helps others out there.

Resources