ASP.NET MVC 3 Partial View Template - asp.net-mvc-3

To be honest, I have no idea what to call this or how to start to search it.
I have a display page with a standard layout.
<div>
<label for="field">Field Name:</label>
#Model.Field
</div>
While trying to make this more change friendly, I want to make a template instead of typing out each field with the above code.
I created a partial view with the following:
#model System.Object
<div>
#Html.LabelFor(m => m)
#Html.DisplayFor(m => m)
</div>
On my view, I added the following:
#Html.Partial("_BillField", Model.Field)
The model then has a description with like:
public ModelName {
[Display(Name="Field Description")]
public decimal Field { get; set; }
}
This works when on the main view, but the label is missing when using the template. What am I missing?
Update: Per #K. Bob I make the change to the partial view:
<div>
#Html.LabelFor(m => m)
#Html.DisplayFor(m => m)
</div>
Update 2: For clarity of what I want.
In the end, I want to be able to do:
#Html.Partial("_BillField", Model.Field1)
#Html.Partial("_BillField", Model.Field2)
#Html.Partial("_BillField", Model.Field3)
And have the equivalent of:
<div>
<label for="field1">Field 1 Name:</label>
#Model.Field1
</div>
<div>
<label for="field2">Field 2 Name:</label>
#Model.Field2
</div>
<div>
<label for="field3">Field 3 Name:</label>
#Model.Field3
</div>
Sorry for not making that clearer.

The partial doesn't need told what the #model is, it'll use the parent #model, if you take out the #model in the partial does that help?
If I have this as the view....
#model MyApp.Models.ModelName
#{
ViewBag.Title = "Test";
}
<h2>Test</h2>
<div>
<div class="editor-label">
#Html.LabelFor(m => m.Field)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.Field)
#Html.ValidationMessageFor(m => m.Field)
</div>
</div>
#Html.Partial("_partial", Model) #*note I pass the whole model*#
And this as the partial....
#model MyApp.Models.ModelName
<div>
<div class="editor-label">
#Html.LabelFor(m => m.Field)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.Field)
#Html.ValidationMessageFor(m => m.Field)
</div>
</div>
Then it does what I think you want to do (obv. it does it twice but you could remove the code from the main view).
I'm not sure it gives you a huge benefit though. Maybe I've misunderstood something.

I think that potentially you should use Display/Editor templates for types, your model includes 3 fields of that type.
#Html.Partial("_BillField", Model.Field1)
#Html.Partial("_BillField", Model.Field2)
#Html.Partial("_BillField", Model.Field3)
Defining a Template for the type rather than a Partial view is possibly more effective. See ASP.NET MVC 3 - Partial vs Display Template vs Editor Template for a detailed comparison.
In this case your View would look more like:
#Html.DisplayFor(model => model.Field1)
#Html.DisplayFor(model => model.Field2)
#Html.DisplayFor(model => model.Field3)
Your model would be:
public class model
{
[DisplayName("Field1")]
public ComplexType Field1 {get;set;}
[DisplayName("Field2")]
public ComplexType Field2 {get;set;}
[DisplayName("Field3")]
public ComplexType Field3 {get;set;}
}
Or whatever the data annotation is for the display name.

Sounds like you want a DisplayFor(m => m):
http://buildstarted.com/2010/09/10/overriding-displayfor-and-editorfor-to-create-custom-outputs-for-mvc/
Function reference: http://buildstarted.com/2010/09/29/htmlhelper-guide-for-mvc-3-part-2/

You don't need to ToString() your property.
#Html.LabelFor(m => m)
UPDATED
Based on what you want to do, re-use views, take a look at this longer than normal post about reuse of validation and partial views I wrote up to the answer for ASP.NET MVC 3 - Model Validation. It is extremely detailed.

Added a new answer as the other one didn't answer the question once clarified.
This post LabelFor extension should be able to be adjusted to suit your needs I think, but rather than override better to create your own extension.
You'll need to call it slightly differently from how you've proposed because you need to use the m=>m.Field syntax. But I'm sure this should do what you need without having to use a partial view.
If you use Model.Field syntax you will only ever send in the actual value of the Field property, like 1.23 for your decimal, you need to use the m=>m.Field to get more than just the evaluation of the property so that you can change the text in the label.

Related

Asp.Net MVC 3 not binding collections to models on posting

i have a object with many properties of which one is an array of some other complex object
something like this
class obj1
{
public string prop1 {get; set;}
public string prop2 {get; set;}
public obj2[] array {get; set;}
}
class obj2
{
SomeEnum Type{get; set;}
string Content{get; set;}
}
I have created an editor template for the array of obj2 lets name it obj2ArrayTemplate
which is like this
#for (int i = 0; i < Model.Length; i++)
{
#Html.EditorFor(model=>model[i],"obj2Template")
}
and an editor template for obj2 lets name It obj2Template
which is like this
<div class="editor-label">
#Html.DisplayFor(model=>model.Type,"SomeEnum",ViewData)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Content)
#Html.ValidationMessageFor(model => model.Content)
</div>
now because property Type if of type SomeEnum which is an enum and hence cannot be rendered directly by the Asp.Net MVC
I have created a Template for this too
which is something like this
<input type="text" value="#Model.ToString()" id="#ViewData.TemplateInfo.HtmlFieldPrefix" name="#ViewData.TemplateInfo.HtmlFieldPrefix"/>
the view is being rendered correctly and the HTML of the combined rendered view is
<div class="editor-field">
<input class="text-box single-line" id="array__0__Content" name="array.[0].Content" type="text" value="FBID" />
</div>
<div class="editor-label">
<input type="text" value="Blog" id="array.[1].Type" name="array.[1].Type"/>
</div>
<div class="editor-field">
<input class="text-box single-line" id="array__1__Content" name="array.[1].Content" type="text" value="SOme random blog" />
</div>
<div class="editor-label">
<input type="text" value="Twitter" id="array.[2].Type" name="array.[2].Type"/>
</div>
<div class="editor-field">
<input class="text-box single-line" id="array__2__Content" name="array.[2].Content" type="text" value="Twitter Profile" />
</div>
when iam posting back the form containing this html
chrome is showing me this posted data
prop1:val1
prop2:val2
array.[0].Type:Facebook
array.[0].Content:FBID
array.[1].Type:Blog
array.[1].Content:SOme random blog
array.[2].Type:Twitter
array.[2].Content:Twitter Profile
but still array field which is an array of obj2 in the model of type obj1 is null
what am I doing wrong?
Could crack that one :)
inspecting the request that was being posted from the server I found out that there is one .(dot) extra in the request so the array is not being populated
so instead of
array.[0].Type:Facebook
array.[0].Content:FBID
array.[1].Type:Blog
array.[1].Content:SOme random blog
array.[2].Type:Twitter
array.[2].Content:Twitter Profile
this should be posted back
array[0].Type:Facebook
array[0].Content:FBID
array[1].Type:Blog
array[1].Content:SOme random blog
array[2].Type:Twitter
array[2].Content:Twitter
and I found no apparent reason of why Asp.Net MVC framework is putting in that extra dot. when im kind of doing everything the MVC way
so I changed my code to include a little hack.
I added this like to the Editor template of obj 2
#{
ViewData.TemplateInfo.HtmlFieldPrefix = ViewData.TemplateInfo.HtmlFieldPrefix.Replace(".", "");
}
so now everything is running fine and smooth.

MVC3 RenderPage never renders content

So I have this master/detail setup with a WebGrid. All is well until I try and use RenderPage to display the detail when a record is clicked:
<div class="innerbox">
#{
if(gdEligibility.HasSelection){
#RenderPage("~/Views/Eligibility/EligibilityPolicyDetailView.cshtml",
new { Customer = gdEligibility.SelectedRow })
}
}
</div>
Everything works, if I put a break point I can step through the detail view's cshtml file no problem, no errors. But nothing is ever rendered between the outer div's. Ever. Why doesn't RenderPage return anything? I even tried adding .ToHtmlString() on the end of the line but still nothing.
The detail cshtml:
#{ foreach(TravelInsurance.Models.Policy p in Page.Customer.Policies){
<fieldset>
<legend>Policy</legend>
<div class="display-label">Policy Number</div>
<div class="display-field">
#Html.DisplayFor(model => p.PolicyNumber)
</div>
<div class="display-label">Premium</div>
<div class="display-field">
#Html.DisplayFor(model => p.Premium)
</div>
</fieldset>
}}
This question suggests that RenderPage will use the parent model...
Maybe try using
#{Html.RenderPartial("EligibilityPolicyDetailView", new { Customer = gdEligibility.SelectedRow });}
or
#Html.Partial("EligibilityPolicyDetailView" ,new { Customer = gdEligibility.SelectedRow })
Pl. try again by removing the attribute "ajaxUpdateContainerId" in the grid definition and it should work.

The model item passed into the dictionary is of type... ut this dictionary requires a model item of type

I'm new to exploring MVC3 and came up with a problem.
I'm editing my Master view and want the login boxes to be visible on every page when a user isn't logged in. I've done this by using this code:
#if (Request.IsAuthenticated)
{
<text>Welcome <strong>#User.Identity.Name</strong>!
[ #Html.ActionLink("Log Off", "LogOff", "Account") ]</text>
}
else
{
Html.RenderPartial("../Account/LogOn");
}
This works when going to my normal Index method of the HomeController.
However, when going to the Index method of my NewsController I get the following error:
The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[LeagueSite.Models.News]', but this dictionary requires a model item of type 'LeagueSite.Models.LogOnModel'.
I understand what the problem is but don't really know a sollution for it.
The view LogOn looks like this (standard MVC3 logon view):
#model LeagueSite.Models.LogOnModel
#{
ViewBag.Title = "Log On";
}
<h2>Login</h2>
<p>
Please enter your user name and password. #Html.ActionLink("Register", "Register") if you don't have an account.
</p>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#Html.ValidationSummary(true, "Login was unsuccessful. Please correct the errors and try again.")
#using (Html.BeginForm()) {
<div>
<fieldset>
<legend>Account Information</legend>
<div class="editor-label">
#Html.LabelFor(m => m.UserName)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.UserName)
#Html.ValidationMessageFor(m => m.UserName)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.Password)
</div>
<div class="editor-field">
#Html.PasswordFor(m => m.Password)
#Html.ValidationMessageFor(m => m.Password)
</div>
<div class="editor-label">
#Html.CheckBoxFor(m => m.RememberMe)
#Html.LabelFor(m => m.RememberMe)
</div>
<p>
<input type="submit" value="Log On" />
</p>
</fieldset>
</div>
}
`
Any tips/ideas?
Quick and dirty: instead of Html.RenderPartial use Html.RenderAction:
Html.RenderAction("LogOn", "Account");
and in the LogOn action of the Account controller ensure to return a PartialView or you will get a stack overflow and Cassini will crash:
public ActionResult LogOn()
{
return PartialView();
}
I think your "news" view has already a model associated to it.
maybe it starts like this?:
#model LeagueSite.Models.News
well, if so, if you are not passing a model to your partial view, then the framework assumes by default that the model for that partial is "LeagueSite.Models.News", which of course isn't what you want. You must pass the LogOnModel to your LogOn partial view like this:
Html.RenderPartial("../Account/LogOn", Model.ObjLogonModel);
this assumes that you have an instance of LogonModel into your "News" model. then you'll be able to handle the Logon action
regards

MVC Razor - Create / Edit view best practice

I'm working with MVC 3 / Razor for the first time and it seems odd that all the examples and VS scaffolds for create and edit views all have separate HTML views for these concepts.
There is really not much difference between many Create/Edit forms so I was wondering why I can't find examples of people using a single Update form that can be used by both Create and Edit actions.
I have gotten an Update.cshtml view working but was wondering about how it talks to the Edit or Create action method on the controller.
My questions are:
Anyone have a quick answer to talking to the controller, or
Anyone know of a tutorial showing good practice for working this way, or
Is there some good reason for keeping the Create/Edit views separate when the HTML is often the same.
Cheers Dave
This (kind!) of question is asked before: ASP.NET MVC - using the same form to both create and edit
Basically you can create a partial view and include it on your Create and Edit view.
Scott Guthrie has a nice post about Partial Views.
(I've read about this somewhere, but can't find it, I'll update this post when I do find it)
Be mindful that answers to your question should also be driven by business need (and roles). The scaffolding does provide separate functionality, which in some cases is the preferred implementation.
CREATE and EDIT functionality is often pretty much identical from a technical (programming) perspective. This can lead a technical person to think that the functionality should be combined in order to implement a more efficient technical solution. However, any technical implementation must be in response to business need, which might require separation (e.g. by business role) of these concerns.
For example, a business may require that the role which CREATEs business objects is not the same one as EDITs them. In this case, the implemented web pages may not be seen by the same roles (and people) at all.
If you implement CREATE and EDIT using common functionality but the business need is for role separation, you must still implement "role checking" before rendering the required view/partial view/etc. In such cases, separate views can be a preferred implementation.
I do this. I don't know if it's best practice but it can be nice. There are some situations where a completely separate add/edit view could be useful though. Also, if you're using ViewModels then as far as I can tell you're stuck using the same ViewModel for both add and edit. In theory they should both have their own ViewModels.
Here's how this looks for me:
AddVideo.cshtml
#model Multimedia.MediaVideoViewModel
#{
Layout = "~/Views/Shared/LiveSubLayout.cshtml";
}
#section AdditionalHeadContent {
}
<div class="page-header">
<h1>Add a new video</h1>
</div>
<div id="add-video" class="row-fluid">
#Html.Partial("_VideoForm", Model, new ViewDataDictionary { { "ActionKeyword", "Add" } })
</div>
EditVideo.cshtml
#model Multimedia.MediaVideoViewModel
#{
Layout = "~/Views/Shared/LiveSubLayout.cshtml";
}
#section AdditionalHeadContent {
}
#if (ViewBag.Success)
{
<div class="alert alert-success">
<button class="close" data-dismiss="alert">×</button>
<h3><strong>Video saved!</strong></h3><br/>
<div class="btn-group">
Preview this video
#Html.ActionLink("Add Another Video", "AddVideo", "Multimedia", new { Model.Id }, new { #class = "btn" })
#Html.ActionLink("View all media", "Index", "Multimedia", null, new { #class = "btn" })
</div>
<p>or continue editing below...</p>
</div>
}
<div class="page-header">
<h1>Edit video <small>#Model.Title</small></h1>
</div>
<div id="edit-video" class="row-fluid">
#Html.Partial("_VideoForm", Model, new ViewDataDictionary { { "ActionKeyword", "Edit" } })
</div>
_VideoForm.cshtml (partial)
#model Multimedia.MediaVideoViewModel
#{
string actionKeyword = ViewData["ActionKeyword"].ToString();
}
<div class="span6">
#using (Html.BeginForm("editvideo", "multimedia"))
{
<label class="control-label" id="embed-url">Paste video URL here:</label>
<div class="control-group">
#Html.TextBoxFor(model => model.EmbedUrl, new { #class = "span12", id = "video-url", placeholder = "ex: http://www.youtube.com/watch?v=PoAGasPLh30" })
<button class="btn disabled" id="get-video" title="Tooltip">Get Video</button>
</div>
<div class="video-meta">
<h3>Video Information</h3>
<label class="control-label">Title:</label>
<div class="control-group">
#Html.TextBoxFor(model => model.Title, new { #class = "span12", id = "video-title" })
#Html.ValidationMessageFor(model => model.Title, "A title is required", new { #class = "label label-important" })
</div>
<label class="control-label">Description:</label>
<div class="control-group">
#Html.TextAreaFor(model => model.Description, new { #class = "span12", id = "video-description" })
</div>
<h3>Categories</h3>
<div id="tag-search" class="well">
<label class="control-label">Search tags:</label>
<div class="controls"><input type="text" class="typeahead" /></div>
#if (Model != null)
{
foreach (var category in Model.Tags)
{
#Html.Partial("_TagFragment", category)
}
}
</div>
<hr />
#Html.HiddenFor(model => model.Id)
#Html.HiddenFor(model => model.ThumbnailUrl, new { id = "thumb-url" })
<input type="submit" id="video-submit" name="video-submit" class="btn-large btn-primary" value="#actionKeyword video" />
</div>
}
</div>
I edited these down a bit so something might be missing but this should give you the general idea.
here's how i do it, it's not always the best practice (it depends on the case)
1/ combine the controller actions for create and edit
public PartialViewResult Creedit(string id = null)
{
if (id == null)
{
// Create new record (this is the view in Create mode)
return PartialView();
}
else
{
// Edit record (view in Edit mode)
Client x = db.ClientSet.Find(id);
if (x == null) { return PartialView("_error"); }
// ...
return PartialView(x);
}
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Creedit(Client x)
{
if (x.id == null)
{
// insert new record
}
else
{
// update record
}
}
2/ combine the edit and create views into one view i called Creedit
// if you need to display something unique to a create view
// just check if the Model is null
#if(Model==null){
}
so i have 1 view and 2 actions (1 post and 1 get) instead of 2 views and 4 action.
Look into MVC scaffolding in nuget as well, when it generates the view files it does so explicitly creating a creatandedit partial and having the create page and edit page use that partial.

Problem with dropdown in Razor

I have 2 table in db: MixedType(id and name) and Block(id, name, idMixedType).
I want to make strongly-typed view for Block (Create view).
Controller is following:
public ActionResult Create()
{
return View();
}
Block() is a partial class (I use Entity Framework + POCO).
I have no problem with text fields, it works fine:
<div class="editor-label">
#Html.LabelFor(model => model.name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.name)
#Html.ValidationMessageFor(model => model.name)
</div>
But I want to make dropdown for idMixedType field with values from MixedType table.
I tried to do it in following way (according to this answer Create a Dropdown List for MVC3 using Entity Framework (.edmx Model) & Razor Views && Insert A Database Record to Multiple Tables):
<div class="editor-label">
#Html.LabelFor(model => model.idMixedType)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.idMixedType, new SelectList(Model.MixedType, "id", "name"))
#Html.ValidationMessageFor(model => model.idMixedType)
</div>
But I have a error
The best overloaded method match for 'System.Web.Mvc.SelectList.SelectList(System.Collections.IEnumerable, string, string)' has some invalid arguments
What is wrong?
You're passing in Model.MixedType to the SelectList constructor. Presumably Model.MixedType is not IEnumerable.
Is it possible it should be a lowercase "m" (model.MixedType)?
If not, you need to review the static MixedType property and make sure it is a collection that implements IEnumerable (and that the objects it enumerates have "id" and "name" properties, but I presume that's the case).

Resources