I have an mvc page with a displaytemplate.
How do I get the index of the current item being rendered in the displaytemplate.
it produces correct bindable results in the name attributes.
<input name="xxx[0].FirstName"/>
<input name="xxx[1].FirstName"/>
I want that index value in the display template. Is it in the ViewContext somewhere?
#* page.cshtml #
#model ...# property Contacts is IEnumerable *#
<table id="contacts" class="editable">
<thead>
<tr><th>Name</th><th>Surname</th><th>Contact Details</th></tr>
</thead>
<tbody>
#Html.DisplayFor(x => x.Contacts)
</tbody>
in the display template we have
#* contact.cshtml *#
#model ...#* This is the T of the IEnumerable<T> *#
<tr>
#* I NEED THE INDEX OF THE CURRENT ITERATION HERE *#
<td>#Html.DisplayFor(x => x.FirstName)</td>
</tr>
I am afraid there is no easy way to get the index. It's buried inside the internal System.Web.Mvc.Html.DefaultDisplayTemplates.CollectionTemplate method and not exposed. What you could use is the field prefix:
#ViewData.TemplateInfo.HtmlFieldPrefix
Another possibility is replace #Html.DisplayFor(x => x.Contacts) with:
#for (int i = 0; i < Model.Contacts.Length; i++)
{
#Html.DisplayFor(x => x.Contacts[i], new { Index = i })
}
and then inside the template #ViewBag.Index should give you the current index but I must admit it's kinda ugly.
Depending on why you need the index, there may be an easier way.
I was doing something similar and wanted the index just so I could easily number the list of items such as:
First items in List
Second item in List
etc.
The maybe obvious solution was to render my display template as a List Item within an Order List, and let the browser handle the display of the index. I was originally doing this in a table and needed the index to display in the table, but using a ordered list makes it much easier.
So my parent view had the following:
<ol>
#Html.DisplayFor(m => m.Items)
</ol>
And my template wrote on the items in an list item using divs - note you could use floating divs, or better yet display: inline-style to get a table like effect
<li>
<div class="class1">#Html.DisplayFor(m => m.ItemType)</div>
...
</li>
And the css:
.class1 {
display: inline-block;
width: 150px;
}
Extending #DarinDimitrov's answer, I wanted to submit a complete solution:
Extention:
namespace ApplicationName.Helpers
{
public static class RazorHtmlHelpers
{
public static Int32 GetTemplateIndex(this TemplateInfo template)
{
var index = 0;
var match = Regex.Match(template.HtmlFieldPrefix, #"\d");
Int32.TryParse(match.Value, out index);
return index;
}
}
}
Parent View Markup:
#Html.DisplayFor(model => model.Items)
Template Markup:
#using ApplicationName.Helpers
#model ApplicationName.Models.ModelName
<span>Item #ViewData.TemplateInfo.GetTemplateIndex()</span>
Related
I'm new to umbraco and i want to be able to click in a content field, add content, then select specialty nav item. This navigation would be queried from a custom doc type.
I have the doc type list setup and i have a generic custom grid editor setup, however im not sure how to get the grid editor render portion to actually query the doc type and output it.
I did this by using a macro and a partial template like so:
#inherits Umbraco.Web.Macros.PartialViewMacroPage
#{
var selection = CurrentPage.Site().FirstChild("benefitNav").Children("benefitNavItem").Where("Visible");
}
<div id="benefitmenu">
<table class="blink">
#foreach (var item in selection)
{
var img = Umbraco.Media(item.GetPropertyValue("benefitIcon")).Url;
foreach (var link in #item.linkToPage)
{
<tr>
<td class="bicon"><img alt="#link.caption" src="#img" /></td>
<td class="btext">#link.caption</td>
</tr>
}
}
</table></div>
I have a pretty simple scenario, Model for my view is a List.
Loop through List like
#foreach(CustomObject obj in Model)
{
Html.Partial("_TrackingCustomObject",obj)
}
So i was expecting to have number of partial views according to my list.
Partial View has been developed accordingly.
There is no error on page. It just does not show any data that is supposed to display by partial views.
What is the reason of not showing any data?
You are missing an #:
#foreach(CustomObject obj in Model)
{
#Html.Partial("_TrackingCustomObject", obj)
}
But why writing foreach loops when you can use editor/display templates? Like this:
#model IEnumerable<CustomObject>
#Html.EditorForModel()
and then simply define the corresponding editor template (~/Views/Shared/EditorTemplates/CustomObject.cshtml) that will automatically be rendered for each element of your model:
#model CustomObject
<div>
#Html.EditorFor(x => x.Foo)
</div>
Simple and conventional :-)
You're missing the Razor symbol #:
#foreach(CustomObject obj in Model)
{
#Html.Partial("_TrackingCustomObject",obj)
}
Also make sure your partial view is using the object type CustomObject as the Model.
#model MyProject.Models.CustomObject
<h1>Yeah we're in a partial! #Model.SomeProperty </h1>
To try and drill down to where the error is, try placing some static text inside the PartialView.
<p>Some text</p>
If your collection has 10 items, then you should see 10 of these paragraphs. Next once this works, focus on displaying some property in each item.
#model MyProject.Models.CustomObject
<p>Some text</p>
<p>#Model.SomeProperty</p>
When you are creating html form using #Html.BeginForm() you have to wrap the remaining stuf inside a <div> or other container else the html elements won't get rendered.
Ex.
this won't work
#using(Html.BeginForm())
{
Html.EditorFor(m => m.Name)
}
this will work
#using(Html.BeginForm())
{
<div>
#Html.EditorFor(m => m.Name)
</div>
}
Bit late in the day, but this worked for me in MVC 4:
#foreach (var p in #Model.RelatedCards)
{
Html.RenderPartial("_ThumbPartial", p);
}
Try this:
#Html.RenderPartial("_TrackingCustomObject",obj)
This is too old but someone can use it.
#foreach(CustomObject obj in Model)
{
<text>
Html.Partial("_TrackingCustomObject",obj)
</text>
}
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)
}
}
I have seen many versions of this question but the answers always turn into "you don't need to do that" and never an answer.
I have a list of attributes about a product that I want to show in an unordered list with checkboxes to select particular attributes.
In My Model:
public List<ProductAttribute> ProductAttributes {get;set;}
in my Create.cshtml:
<div Class="ProductAttributes">
#Html.EditorFor(m => m.ProductAttributes, "ProductAttributeSelectorList")
</div>
In my ProductAttributeSelectorList.cshtml:
#model List<Models.DisplayLocationAttribute>
<div class="AttributeSelector">
<ul>
#foreach (var item in Model)
{
<li>
#Html.EditorFor(_ => item, "EditLocationAttributeList")
</li>
}
</ul>
</div>
And finally, in my EditLocationAttributeList.cshtml
#model Models.DisplayLocationAttribute
#Html.HiddenFor(m => m.Id)
#Html.CheckBoxFor(m => m.IsSelected)
<a href="#" alt="#Model.Description" >#Model.Name</a>
This all displays on the page perfectly I can style it like I want with CSS, but when the submit returns, my model.ProductAttributes collection is null.
I know I can bind directly to the EditLocationAttributeList and it will display and return a populated model.ProductAttributes if I use this:
#Html.EditorFor(m => m.ProductAttributes, "EditLocationAttributeList")
but now I do not have the unordered list that I would like to have. I could treat the template like an Item Template and have the line item tags embeded in that template but that seems smelly to have a template that is tightly coupled to another template.
Any Ideas?
Thanks in advance,
Tal
model.ProductAttributes is null, because the DefaultModelBinder is not able to reference each DisplayLocationAttribute back to the ProductAttribute property of your model. The simplest solution is to name your list elements as an array, so that for example each IsSelected element is named in the style ProductAttributes[n].IsSelected.
Add the following to ProductAttributeSelectorList.cshtml:
#model List<Models.DisplayLocationAttribute>
#{
var i = 0;
}
<div class="AttributeSelector">
<ul>
#foreach (var item in Model)
{
this.ViewData.TemplateInfo.HtmlFieldPrefix = "ProductAttributes[" +
i.ToString() + "]";
i++;
<li>
#Html.EditorFor(_ => item, "EditLocationAttributeList")
</li>
}
</ul>
</div>
#{
this.ViewData.TemplateInfo.HtmlFieldPrefix = "";
}
This will give you an indexed array, which the DefaultModelBinder will be able to associate to ProductAttributes. However, it builds a hard dependency to the name ProductAttributes. You can get around the hard dependency by several methods, such as passing the property name in the ViewBag.
I would like to sort the item I'm getting in my view and put them in a different divs according to the category they belong
In my table I have items the belong to different categories (cosmetics_perfumes, cosmetics_makeup …)
Something like after I'm getting
#foreach (var item in Model)
{
To sort it here in same way
(I hope I'm clear )
If you want to do this in your view, then you can use a foreach statement for each of the categories.
<div id="perfumes">
#foreach (var perfume in Model.Where(i => i.Category == "cosmetics_perfumes"))
{
<p>#perfume</p> #*Display each item *#
}
</div>
<div id="makeup">
#foreach (var makeup in Model.Where(i => i.Category == "cosmetics_makeup"))
{
<p>#makeup</p>
}
</div>