MVC4 Razor syntax one-to-many nested lists - linq

I'd be very grateful if someone could point out what I'm doing wrong. I'm using MVC 4, database first with an edmx file, two simple tables trying to list a set of child results under a parent result.
the controller code, simplified:
var query = db.Childtablemodel.Include(c => c.ParentTablemodel);
return View(query.ToList());
The view, which is IEunumerable:
enter code here#{
foreach (var itm in Model)
{
<div>
#itm.ParentTable.DisplayName
<ul>
<li>
<span> #itm.City </span> <span> #itm.State</span>
</li>
</ul>
</div>
}
}
the results repeat the parent name for each child
Company 1
Houston TX
Company 1
Austin TX
Where what I want is
Company 1
Houston TX
Austin TX
I am following the Contoso University tutorial and so chose the child model for my controller (after a discouraging attempt to create these groupings in a parent-model- based controller). The edmx model does show the proper navigation properties and they are present in the generated class files. But when I look at a trace of my query, I see that it is using an inner join, which explains my results -- but utterly baffles me!

You are going to want to use linq's groupby to do that.
#{
foreach (var parent in Model.GroupBy(m => m.ParentTable.DisplayName))
{
<div>
#parent.Key
#foreach( var itm in parent )
{
<ul>
<li>
<span> #itm.City </span> <span> #itm.State</span>
</li>
</ul>
}
</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.

How to dynamically create partial views

I have a table in my database called Programs. I want to display a tab for each program .I am trying to create a partial view to do this and then I want to include the partial view to every view that will need to have those tabs. My partial view looks like below.
<div id="tabs">
<ul>
<li id="HomeTab">#Html.ActionLink("Dashboard", "Index", "Home")</li>
<li id="Program1Tab">#Html.ActionLink("Program1", "Index", "Program")</li>
<li id="Program2Tab">#Html.ActionLink("Program2", "Index", "Program")</li>
</ul>
</div>
I am hoping to dynamically create the tabs using something like
#foreach (var ptype in Model)
{
<li id=\"#ptype.Name\"Tab>#Html.ActionLink(ptype.Name, "Index", "Project")</li>
}
however I am wondering how I can load the tabs without using a controller. Can I use a helper class/method to directly access the model bypassing the controller?
update:
I also tried by creating a helper method
namespace MyProject.Helpers
{
public class ProgramTypes
{
public static List<ProgramType> ProgramTypesList()
{
MyDbContext db = new myDbContext();
return db.ProgramTypes.ToList<Programtype>();
}
}
}
and then accessing them using
#foreach (var ptype in MyProject.Helpers.ProgramTypes.ProgramTypesList())
however i am not sure if this is the correct thing to do.
The best solution is - passing collection of programs to your view
#model IQueyrable<Program>
<div id="tabs">
<ul>
#foreach (var ptype in Model)
{
<li>#Html.RenderPartial("ProgramTab", ptype)</li>
}
</ul>
</div>
so you have to create another partial view where you will display program details.
If you want organize this like tabs you should use something like jquery tabs
you don't have to use actionlink just RenderPartial or RenderAction

To call a page on click of an ActionLink in the same View

i am developing an application in MVC3..
I have several links on the leftside and on click of the link the page should be displayed on the right side
I have made a table.
in the First Column the links are displayed like this:
<ul>
#foreach (var item in Model)
{
<li>#Html.ActionLink(item.HobbyName, "Hobbies")</li>
}
and in the second column i need to display information about the links when it is clicked but i am not understanding hw shud i do that..
Like What shud i write in the Controller to fetch those views in this column.
Please Help me
Use Ajax.ActionLink instead of Html.Actionlink and set update target id in Ajax Options to the div you want to update. Example
<ul>
#foreach(var item in Model)
{
<li>
#Ajax.ActionLink(item.HobbyName, "Hobbies",
new AjaxOptions { UpdateTargetId = "divHobyList" })
</li>
}
</ul>
<div id="divHobyList">
here will load hobbies
</div>

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.

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