I am creating LOB business application using ASP.NET MVC. In my views I find this pattern repeated a lot:
<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>
It must be possible to write a helper to reduce it down to something like this:
#EditorForField(model => model.Name)
This will make the views simpler, and make it easier to change the form layout to table based layout (if required)
Any ideas how to make such a helper method?
Thanks!
I was able to make this work by this code:
public static class HelperExtensions
{
public static MvcHtmlString EditorForField<TModel, TValue>(
this HtmlHelper<TModel> html,
Expression<Func<TModel, TValue>> expression)
{
const string template = #"<div class=""editor-label"">{0}</div><div class=""editor-field"">{1}{2}</div>";
string markup = string.Format(template,
html.LabelFor(expression),
html.EditorFor(expression),
html.ValidationMessageFor(expression));
return new MvcHtmlString(markup);
}
}
In your View:
#Html.EditorForField(model => model.Name)
Related
I have a partial view with a form in it that I place on to my page using an #ajax.ActionLinK
#Ajax.ActionLink("Update File", "CreateFileVersion", new AjaxOptions() { UpdateTargetId = "CreateFileVersion", InsertionMode = InsertionMode.Replace })
and here is my partial view
#model CasWeb.Models.DataContext.FileVersion
#{
Layout = null;
}
<h4>Create File Version</h4>
#using (Html.BeginForm())
{
#Html.ValidationSummary()
<fieldset>
<legend>File Version</legend>
<div class="editor-label">
#Html.LabelFor(model => model.VersionNumber)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.VersionNumber)
#Html.ValidationMessageFor(model => model.VersionNumber)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ActivationTime)
</div>
<div class="editor-field">
#Html.EditorFor(m => m.ActivationTime)
#Html.ValidationMessageFor(model => model.ActivationTime)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
This all works great, the user clicks on the link and the form appears with the "CreateFileVersion" div.
The problem comes with validation. When the user submits the form it goes to my controller which checks the data and returns the partial view back if it invalid. This partial view is no longer within the "CreateFileVersion" div, it now takes over the entire page.
How can I keep the partial view returned from my contoller within the "CreateFileVersion" div after validation.
Thanks
You should use an Ajax.BeginForm inside your partial instead of Html.BeginForm. This would allow you to submit the form using an AJAX call and specify the same update target id:
#using (Ajax.BeginForm(new AjaxOptions { UpdateTargetId = "CreateFileVersion" }))
{
...
}
I'm making a survey application. Survey has Questions and Questions have QuestionOption. haveThere is a example here.
I am trying to use this technique in a large form with a list(List) but when I post back, the Viewmodel.Order that should’ve contained list of items and activities return with the lists empty.
My QuestionModel.cs like this.
public int Id { get; set; }
public string QuestionText { get; set; }
public System.Nullable<bool> OptionType1 { get; set; }
public System.Nullable<bool> OptionType2 { get; set; }
public List<QuestionOptionModel> OptionList = new List<QuestionOptionModel>();
When I post back "IEnumerable questions" List OptionList comes null. How can I do this?
public ActionResult CallSurvey()
{
IEnumerable<QuestionModel> questionModelList = (IEnumerable<QuestionModel>)SessionHelper.GetSessionObject(SessionKeys.SurveyKey);
questionModelList = questionSrv.GetQuestionModel();
return View(questionModelList);
}
questionModelList include all my survey question and question options. When I post it, post back is coming with only null optionList.
[HttpPost]
public ActionResult CallSurvey(IEnumerable<QuestionModel> questions)
{ ..... }
CallSurvey.cshtml
<body>
#using ((Html.BeginForm()))
{
#ViewBag.Test
<section class="slides layout-regular template-kendo">
#foreach (var item in Model)
{<article>
#Html.Partial("QuestionEditor", item)
</article>
}
<div class="slide-area" id="prev-slide-area"></div>
<div class="slide-area" id="next-slide-area"></div>
</section>
}
</body>
QuestionEditor.cshtml
#model LSMM.Business.Model.Survey.QuestionModel
#using LSMM.Web.Helpers
<div>
#using (Html.BeginCollectionItem("Questions"))
{
<table id="table1">
<tr>
<td>
<div id="#Model.Id" class="hint">
#Html.HiddenFor(m => m.Id)
#Html.HiddenFor(m => m.QuestionText)
#Html.HiddenFor(m => m.OptionType1)
#Html.HiddenFor(m => m.OptionType2)
#for (int i = 0; i < Model.OptionList.Count; ++i)
{
#Html.LabelFor(m => m.OptionList[i].Id)
#Html.LabelFor(m => m.OptionList[i].QuestionId)
#Html.LabelFor(m => m.OptionList[i].Checked)
#Html.LabelFor(m => m.OptionList[i].Description)
#Html.LabelFor(m => m.OptionList[i])
}
<span id="sorular">#Model.Id. #Model.QuestionText</span>
<br />
<br />
</div>
</td>
</tr>
<tr>
<td>
<div class="hint2">
#Html.Partial("QuestionOptionEditor", Model)
</div>
</td>
<td>
<div id="#Model.Id-Img">
<h2 style="top: 200px; right: 0;">
<img src="../../Content/css/img/#Model.Id-Img.png"></h2>
</div>
</td>
</tr>
</table>
and QuestionOptionEditor.cshtml
#model LSMM.Business.Model.Survey.QuestionModel
#using LSMM.Web.Helpers
#foreach (var option in #Model.OptionList)
{
<p>
#if (#Model.OptionType1 == false)
{
#Html.Partial("QuestionOptionModel", option)
}
else
{
#Html.Partial("../Shared/DisplayTemplates/QuestionOptionModel", option)
}
</p>
}
Here QuestionOptionModel views like this;
#model LSMM.Business.Model.Survey.QuestionOptionModel
#(Html.RadioButtonFor(m => m.Id, true, new { Id = #Model.Id, Name = #Model.QuestionId })) #Html.Label("Evet")
<br />
<br />
#(Html.RadioButtonFor(m => m.Id, false ,new { Id=#Model.Id, Name = #Model.QuestionId})) #Html.Label("Hayır")
The name attribute on your radio buttons is not "correct" according to the naming rules used by the default ModelBinder. That's why you aren't seeing the values you expect, the ModelBinder couldn't find what it was looking for.
This is easy to fix and you should end up with less code.
Take advantage of the framework and let it do work for you:
MVC can loop IEnumerables for you. Let it. There's little reason to write foreach loops in views these days.
Get rid of those partials. Use MVC's Templates feature instead.
Then your view can be as simple as:
<body>
#using ((Html.BeginForm()))
{
#ViewBag.Test
<section class="slides layout-regular template-kendo">
<table id="table1">
#* Table header goes here *#
#Html.EditorFor(model => model.OptionList)
</table>
<div class="slide-area" id="prev-slide-area"></div>
<div class="slide-area" id="next-slide-area"></div>
</section>
}
</body>
When you use an EditorTemplate or a DisplayTemplate, MVC will automatically fix the name attribute on your form fields. It won't do that with a partial.
Another suggestion, also if you don't mind. Get rid of those nulla-bools on your model and write different QuestionOptionModel types. DisplayTemplate/EditorTemplates are wise to the type system and MVC will pick the template that matches the type it is presented.
So you could have another class that derives from QuestionOptionModel:
public class ExtendedQuestionOptionModel : QuestionOptionModel
{
//Stuff goes here.
}
and you could have a List<QuestionOptionModel> hanging off your model that contains:
QuestionOptionModel
ExtendedQuestionOptionModel
QuestionOptionModel
and when you do this:
#Html.EditorFor(model => model.QuestionOptions)
MVC will go through the option list and render the Editor Template for QuestionOptionModel, then the Editor Template for ExtendedQuestionOptionModel, then the Editor Template for QuestionOptionModel again.
The game you want to play is to try to minimize the C# code in the views, because the views cannot be unit tested. Code that isn't unit tested will eventually come back and get you.
Your question is abit ambiguous. Frankly i hope this is what you are loooking for : IEnumerable can be transformed to list using
List<foo>listFoo =iEnumerableEntityFoo.toList()
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.
Basically, I want an Html helper (something like #Html.MyEditor(m => m.Property)) to produce this:
<div class="editor-label">
#html.LabelFor(m => m.Property)
</div>
<div class="editor-field">
#html.EditorFor(m => m.Property)
#html.ValidationMessageFor(m => m.Property)
</div>
Only problem is that I can't seem to access Html.EditorFor() or any of the other extension methods in side my own helper. Example attempt:
#helper Edit(this System.Web.Mvc.HtmlHelper<Spartacus.ViewModels.NewTaskItemModel> html)
{
<div class="editor-label">
#html.LabelFor(m => m.Property)
</div>
<div class="editor-field">
#html.EditorFor(m => m.Property)
#html.ValidationMessageFor(m => m.Property)
</div>
}
I also tried the extension method syntax:
public static string DatePickerFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression)
{
var sb = new StringBuilder();
sb.AppendLine("<div class=\"editor-label\">");
sb.AppendLine(html.LabelFor(expression));
sb.AppendLine("</div>");
sb.AppendLine("<div class=\"editor-field\">");
sb.AppendLine(html.EditorFor(expression));
sb.AppendLine(html.ValidationMessageFor(expression));
sb.AppendLine("</div>");
return sb.ToString();
}
in both attempts above, the LabelFor, EditorFor, and ValidationMessageFor throw compile errors ("could not be found").
Anyone know of a way to do this? Thanks in advance!
It should work if you have a namespace using for the System.Web.Mvc.Html namespace. The extension methods are defined in this namespace on various static extension classes (e.g. EditorExtensions).
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).