In my view, I use a partial view with a list of objects from my model as follows:
#foreach (var item in Model.MyObjectList)
{
#Html.Partial("DisplayObject", item)
}
This forms a grid pattern down my main view, 1 row for each object.
I'd like to stripe them using normal method of odd rows one colour, even rows another. (like this for example.
Usually you'd do this by doing some kind of mod calculation.
The question is I can't figure out how to do this without passing in a row number in the item into the partial view.
Is there an easier way? Html.Partial does not have any html attributes I can hook into.
I could of course put a table around the partial and stripe it that way, but would prefer not to.
If you can limit support to CSS3 you can use the :nth-child(odd) and :nth-child(even) rules - as shown on this page from W3C - http://www.w3.org/Style/Examples/007/evenodd.en.html
Those examples show how to use it on tr tags - but it applies to anything that you can apply a selector to:
<style type="text/css">
div p:nth-child(odd) { color:red; }
div p:nth-child(even) { color:green; }
</style>
<div>
<p>first</p>
<p>second</p>
<p>third</p>
</div>
I think you should pass the information in the model that is passed to the partial view OR wrap the partial view in an element and apply the correct css to it...like:
#foreach (var item in Model.MyObjectList) {
<li class="odd|even">
#Html.Partial("DisplayObject", item)
</li>
}
Add the row number as a property to the definition of your class that defines each 'item'.
Use that property in the partial view to determine if its an odd or even row.
Pass the Mode value through ViewBag from the main view to partial.
#foreach (var item in Model.MyObjectList)
{
if(ViewBag.Mode == "odd")
{
ViewBag.Mode = "even";
}
else
{
ViewBag.Mode = "odd";
}
#Html.Partial("DisplayObject", item)
}
Related
When I create multiple view instances of the Marionette view which is linked with a template html with ids, these would get duplicated for multiple instances of these views.
While it works correctly, I feel that there ought to be more architecturally correct way of doing this.
The example code is like below.
Template:
<script id="myTemplate" type="text/template">
<div id="myDiv">
<input type="text" id="myText"/>
<input type="button" id="myBtn" value="Click me!"/>
</div>
</script>
View:
MyView = Backbone.Marionette.ItemView.extend({
template: '#myTemplate',
events: {
'click #myBtn' : 'myFunc' //Correctly identifies its own 'myBtn'
},
myFunc : function() {
alert($('myText').val()); //Again, picks own 'myText'
}
});
var v1= new MyView();
v1.render();
var v2= new MyView();
v2.render(); //Duplicate IDs now present in DOM
I need some unique identification of these DOM elements and hence the ids.
Even when tying the model to this view, we need some way to identify these DOM elements.
What is the correct way of doing this without duplicating the ids.
Just pass the id to the view when you create it:
Template:
<script id="myTemplate" type="text/template">
<input type="text" class="js-myText"/>
<input type="button" class="js-myBtn" value="Click me!"/>
</script>
View def:
MyView = Backbone.Marionette.ItemView.extend({
template: '#myTemplate',
events: {
'click #myBtn' : 'myFunc' //Correctly identifies its own 'myBtn'
},
myFunc : function() {
alert($('myText').val()); //Again, picks own 'myText'
}
});
Instanciation:
var v1= new MyView({ id: "view" + number});
v1.render();
Then you can provide dynamic id values for your views (e.g. by using a model id).
That said, when using Marionette you shouldn't need to call render: you should instead show a view within a region. Take a look at the free sample to my Marionette book to get you up to speed.
If you must go for unique IDs to make sure no one accidentally duplicates a class name inside a view, you can use:
Underscore's uniqueId method to generate a unique ID for each DOM element inside the view, like: <input type="text" id= <%= _.uniqueId("myText_") %> /> This will just make sure that IDs are not duplicated. But they're not very helpful if you need to identify the elements by these IDs.
Marionette's TemplateHelpers which allow you to use helper functions from inside the templates:
//Define this inside your view:
templateHelpers: function() {
var that = this;
return {
getIdSuffix : function() { return that.idSuffix; }
/*Where idSuffix is passed to the view during instantiation
and assigned to this.idSuffix */
};
}
//In the template:
<input type="text" id= <%= "myText_" + getIdSuffix() %> />
You now know before runtime what DOM IDs you will have, provided care is taken not to give the same idSuffix to more than one view instance.
Simply put, don't use an id if it's not unique. Use a class or some other way of identifying the element.
You can use any jQuery selector to locate the element you want, ranging from the insane and brittle:
this.$('div > input:first'); // don't actually do this!
to the slower but semantically better:
this.$('[data-element-type="some-text-box-descriptive-name"]');
Although in reality, using a class is best, because that's what a class is for - for identifying a type of element. I can see that a maintainer might not know not to change your class in the template, so a data-attribute might be acceptable, or maybe even (in this case):
this.$('input[type=text]');
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've got a view that displays a list of items. Rather than display items in a grid, I'd like to display 4 items per page, each with an image and multiple properties, displayed in a unique layout.
So, I'd like a foreach to iterate through the items, and each item to get displayed in a div. I could put all the code in the loop, but I'd like to have a custom html helper extension to do this.
I came up with this,
public static MvcHtmlString DisplayViewerFor(this HtmlHelper helper, TestModel model, bool rightAligned = true) {
if (model == null) {
model = new TestModel();
}
var outterDiv = new TagBuilder("div");
outterDiv.AddCssClass(rightAligned ? "item-display-right" : "item-display");
var image = new TagBuilder("image");
image.Attributes.Add("src", "Item/GetImage/" + model.ItemName);
image.Attributes.Add("height", "150");
var editorLabel = new TagBuilder("div");
editorLabel.AddCssClass("editor-label");
//LOOKING TO ADD CODE LIKE THIS HERE
var labelContent= html.LabelFor({my model property here})
editorLabel.InnerHtml += labelContent;
//END OF ADD
return new MvcHtmlString(outterDiv.ToString(TagRenderMode.EndTag));
}
In my method above, I need to display a few more values, and I would like to use the Html.LabelFor and Html.DisplayFor helpers, but the methods aren't available and I'm not sure what to pass to them if they were.
I'm not sure if this is possible or not, but I thought I would ask.
EDIT
I'm trying to use the html.LabelFor. See my code where I have updated it above, adding to it these two lines.
var labelContent= html.LabelFor({my model property here})
editorLabel.InnerHtml += labelContent;
You can see the code above.
EDIT 2
Here is the planned use for this Helper with dummied down view.
#model TestItemDisplayList
#{
ViewBag.Title = "Items";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#foreach(var item in #model.Items){
#Html.DisplayViewerFor(item)
}
You can use the Html.DisplayFor method to render a DisplayTemplate. One of the over loads for the method is to specify a template to use. You can modify your page code to read:
Page:
#model TestItemDisplayList
#{ ViewBag.Title = "Items"; Layout = "~/Views/Shared/_Layout.cshtml"; }
#Html.DisplayFor(model => Model,"TestItemDisplayList")
Display Template for TestItemDisplayList
#model TestItemDisplayList
#* You could limit this loop to the first 4 items *#
#foreach(var item in model.Items){ #Html.DisplayFor(item => item) }
Display Template for TestModel
#model TestModel
<div class="item-display">
<img src="#Url.Action("GetImage", "Image", new { id = Model.ItemName})" height="150"/>
<div class="editor-label">#Html.LabelFor(model => model.PropertyHere)</div>
</div>
I assume that your URL for the image used the default route of {controller}/{action}/{id} so I used the Url.Action and specified your ID.
You could also get away witout using a DisplayTemplate for "TestItemDisplayList" and moving that code in to your page but I wasn't clear if you wanted to add logic in that to limit the number of pages.
I would like to implement a 2-level parent/child menu in my MVC 3 site such as
Company
- Background
- Contact
I have implemented a single, parent level menu as a PartialView like so ...
<div id="menu" class="block">
<ul id="menuItems">
foreach (var item in Model)
{
<li id="#item.Id">#Html.ActionLink(item.Name, item.Action,item.Controller)</li>
}
</ul>
</div>
and then included it on my MasterPage ...
#{Html.RenderAction("MainMenu", "Menu");}
The problem is that I would like to render a second child-menu based on the menu item selected at the parent level. This involves passing the Id of the parent into the controller action that returns the menu model. I'm not sure how I can pass this parent Id into the controller action. Can anyone provide any insights into this? I'm using MVC3 & Razor.
You may want to check out MvcSiteMapProvider which handles multilevel menu and sitemaps.
Looks like you're using Razor and while I'm not super familiar with that I'll take a shot at it. Basically you're going to pass a new object that has a single property of "id" to the MainMenu view. That will create another menu. Your MainMenu action should take an optional parameter of id.
public ActionResult MainMenu(int? id = null) {
...
}
Here is what your new list item would look like.
<li id="#item.Id">#Html.ActionLink(item.Name, item.Action,item.Controller)
#{Html.RenderAction("MainMenu", "Menu", new { id = item.Id });}
</li>
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>