How can I Sort items in a view in a razor view - asp.net-mvc-3

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>

Related

MVC LINQ query to populate model with a one-to-many and many-to-one tables

I am stuck trying to get my model populated given the domain table structure.
I have a main table that stores Vendors and each Vendor can select one-to-many categories from a master category lookup table. The vendor selections are stored in another table that only stores the VendorID and CategoryID to link between them.
I can write the query (below) to include the category table but then I can only display the categoryID and not the category names.
public VendorProfile GetVendor(String id)
{
Guid pid = Guid.Parse(id);
var view = _db.VendorProfiles.Include("VendorCategories").Single(v => v.ProfileID == pid);
return view;
}
I attempted to include the lookup table in the query (below) but this is generating a runtime error.
public VendorProfile GetVendor(String id)
{
Guid pid = Guid.Parse(id);
var view = _db.VendorProfiles.Include("VendorCategories").Include("ProductServiceCategory").Single(v => v.ProfileID == pid);
return view;
}
A specified Include path is not valid. The EntityType 'VendorProfilesIntranet.VendorProfile' does not declare a navigation property with the name 'ProductServiceCategory'.
The Category table does have the navigation property. I don't know how to add this same navigation property to the main table since it does not have any FK to the lookup table.
UPDATE:
#Gert This notation does work!
_db.VendorProfiles.Include("VendorCategories.ProductServiceCategory").Single(v => v.ProfileID == pid);
However, what I get displayed now is only the category items that were selected. I wish to get the entire list of catgories and have the ones checked that were selected. I am using a scrolling CheckboxList.
<div class="scroll_checkboxes">
<ul>
#foreach (var c in Model.VendorCategories)
{
<li>
<input type="checkbox" name="categories" value="#c.ID" /> #c.ProductServiceCategory.CategoryName
</li>
}
</ul>
#Html.ValidationMessage("Please check at least one Product/Service category")
</div>
UPDATE2:
There might be a better solution but for anyone stuck with similar situation, this worked
<div class="scroll_checkboxes">
<ul>
#foreach (var c in ViewBag.Categories)
{
<li>
#foreach(var vc in Model.VendorCategories)
{
if(c.Id == vc.CategoryID)
{
<input type="checkbox" name="categories" checked="checked" value="#c.Id" /> #vc.ProductServiceCategory.CategoryName
<br />
}
else
{
<input type="checkbox" name="categories" value="#c.Id" /> #vc.ProductServiceCategory.CategoryName
<br />
}
}
</li>
}
</ul>
#Html.ValidationMessage("Please check at least one Product/Service category")
</div>
You can do
_db.VendorProfiles.Include("VendorCategories.ProductServiceCategory")
to include both the VendorCategories and their ProductServiceCategorys in the result set.
By the way, if you use DbExtensions.Include you have intellisense to help you find the right include paths.

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)
}
}

Nested edit templates in mvc razor

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.

Working with ASP.NET Razor and HTML

I have a list of categories and sub categories which is passing from controller to the view. Now, I want them to be represented in the HTML like following. But, I dont know how can i achieve this by using foreach or table or whatever.
EDIT : Code
public ActionResult Electronics()
{
var topCategories = pe.Categories.Where(category => category.ParentCategory.CategoryName == "Electronics").ToList();
//var catsAndSubs = pe.Categories.Include("ParentCategory").Where(c => c.ParentCategory.CategoryName == "Electronics");
return View(topCategories);
}
With this view code, I am just able to pull a vertical list.
#foreach (var cats in Model)
{
<li>#cats.CategoryName</li>
foreach (var subcats in cats.SubCategories)
{
<li>#subcats.CategoryName</li>
}
}
When designing HTML mark-up it is very important to consider semantics. What meaning are you trying to convey? That doesn't look like tabular data to me so please don't put it in tables :P
Based on your wireframe above, the way I would probably structure this is like this:
<h1>Category Directory</h1>
<h2>Multimedia Projectors</h2>
<h2>Home Audio</h2>
<p>
Amplifiers, Speakers
</p>
Adjust the hX tags to reflect their position within the document's hierachy. Remember to only ever have ONE h1 per page (or per <acticle>, or <section> if using HTML5).
If instead you wind up turning this into something like a Superfish menu then this is the markup that you would use:
<nav id="category_menu">
<ul>
<li>
Multimedia Projectors
</li>
<li>
Home Audio
<ul>
<li>
Amplifiers
</li>
<li>
Speakers
</li>
</ul>
</li>
</ul>
</nav>
Edit
Your model is not suitable for creating your desired view, the relationship is bottom-up, but to conveniently construct the view you will want the relationships defined top-down. You need to start by converting the data model into a view model, such as:
class CategoryViewModel
{
string CategoryName { get;set; }
IList<CategoryModel> SubCategories { get;set; }
}
and to make this:
IList<CategoryViewModel> Map(IList<CategoryDataModel> dataModel)
{
var model = new List<CategoryViewModel>();
//Select the categories with no parent (these are the root categories)
var rootDataCategories = dataModel.Where(x => x.ParentCategory == null);
foreach(var dataCat in rootDataCategories )
{
//Select the sub-categories for this root category
var children = dataModel
.Where(x => x.ParentCategory != null && x.ParentCategory.Name = cat.Name)
.Select(y => new CategoryViewModel() { CategoryName = y.CategoryName })
.ToList();
var viewCat = new CategoryViewModel()
{
CategoryName = dataCat.CategoryName,
SubCategories = children
};
model.Add(viewCat);
}
return model;
}
Then your view:
<h1>Category Directory</h1>
#foreach(var category in Model)
{
#Html.Partial("Category", category)
}
Category partial:
<h2>#Html.ActionLink(Model.CategoryName, "Detail", new { Model.CategoryName })</h2>
#if(Model.SubCategories.Count> 0)
{
<p>
#for (var i = 0; i < Model.SubCategories.Count; i++)
{
var subCat = Model.SubCategories[i];
#Html.ActionLink(subCat.CategoryName, "Detail", new { subCat.CategoryName })
#if(i < Model.SubCategories.Count - 1)
{
<text>,</text>
}
}
</p>
}
Note that my current solution only supports 2 levels of categories (as per your wireframe). It could however be easily extended to be recursive.

Index of current item in mvc display template

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>

Resources