I have a strongly-typed Razor 2 editor template. When the view model meets certain conditions, I want the view to delegate to an alternative template of the same type. I use the TemplateName argument to the EditorFor helper to select the alternative template:
#model MyType
#if (Model.IsSpecialCase)
{
#Html.EditorFor(m => m, "SpecialCaseTemplate")
}
else
{
#* Default markup *#
}
Problem is, Razor does not call the alternative template; it simply passes over the EditorFor method. If I change the type of the second template, it shows it correctly. I can work around this using a Partial View, but I would rather not, as I have a scheme going with Editor Templates that I want to stick to.
Anyone know how I can get this to work?
Edit
Looks like this has something to do with the behaviour described here: ASP.net MVC - Using EditorFor with the same model type twice. In short, that MVC does not support using the EditorFor method on the same object twice.
The best way to do this, is by returning a different view from your Controller:
public ActionResult someaction(){
var Model = ...;
if (Model.IsSpecialCase){
return View("SpecialCaseTemplate");
}
else{
return View();
}
}
Alternatively, you could do in the view like this:
#model MyType
#if (Model.IsSpecialCase)
{
Html.RenderPartial("SpecialCaseTemplate", model);
}
else
{
#* Default markup *#
}
Related
I have created a razor helper using the
#helper MyHelper(string param) {
}
syntax. I need to be able to access the model state to determine if I should add error classes to the elements. How would I access this? Intellisense does show ModelState but it is always null.
In a razor page I would use ViewData.ModelState but ViewData doesn't exist in the context.
You need to explicitly pass the view context from the view when you call this helper method.
#helper MyHelper(string param,ViewContext context) {
<div>
#foreach (var modelStateVal in context.ViewData.ModelState.Values)
{
foreach (var error in modelStateVal.Errors)
{
<p>#error.ErrorMessage</p>
}
}
</div>
}
and in the view where you want to call this,
#MyHelperClass.MyHelper("Hello",this.ViewContext)
Another option is to create an Html Hepler method
i was reading a post on EditorTemplates
from this url http://stackoverflow.com/questions/4872192/checkboxlist-in-mvc3-0
after seeing their code i just do not understand area like how it would work
view model:
public class MyViewModel
{
public int Id { get; set; }
public bool IsChecked { get; set; }
}
A controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new[]
{
new MyViewModel { Id = 1, IsChecked = false },
new MyViewModel { Id = 2, IsChecked = true },
new MyViewModel { Id = 3, IsChecked = false },
};
return View(model);
}
[HttpPost]
public ActionResult Index(IEnumerable<MyViewModel> model)
{
// TODO: Handle the user selection here
...
}
}
A View
(
~/Views/Home/Index.cshtml
):
#model IEnumerable<AppName.Models.MyViewModel>
#{
ViewBag.Title = "Home Page";
}
#using (Html.BeginForm())
{
#Html.EditorForModel()
<input type="submit" value="OK" />
}
and the corresponding Editor template
(
~/Views/Home/EditorTemplates/MyViewModel.cshtml
):
#model AppName.Models.MyViewModel
#Html.HiddenFor(x => x.Id)
#Html.CheckBoxFor(x => x.IsChecked)
see this code
#using (Html.BeginForm())
{
#Html.EditorForModel()
<input type="submit" value="OK" />
}
1) what this line will do
#Html.EditorForModel() ?
2) if this line would load a view called
MyViewModel from this location
/Views/Home/EditorTemplates/MyViewModel.cshtml
3) then how mvc engine would understand that it has to load view called
MyViewModel from this location /Views/Home/EditorTemplates/
4) i saw people always create
EditorTemplates
folder in shared view but in this case it is created in home folder.....why ?
5) if there are so many other view in this location then how this line
#Html.EditorForModel()
would load this specific view
MyViewModel.cshtml
from this location
/Views/Home/EditorTemplates.
i am new in mvc and learning. so please help me to understand how the above code will work?
also please answer my 5 questions. thanks
Before answer to your specific question, you have to know that asp .net mvc relay heavily on Convention over Configuration.
1) what this line will do
#Html.EditorForModel() ?
It just tell the view to render the model pass in as a whole thing to the EditorFor.
2) if this line would load a view called
MyViewModel from this location
/Views/Home/EditorTemplates/MyViewModel.cshtml 3) then how mvc engine
would understand that it has to load view called
MyViewModel from this location /Views/Home/EditorTemplates/
Mvc knows it by convention. It will look into Views for a template same name to the viewModel type(in this case MyViewModel )
the pattern mvc look at is:
Views{Controller}\EditorTemplates\MyViewModel.cshtml
Views\Shared\EditorTemplates\MyViewModel.cshtml
And if it find it, it will stop looking. Hence the view in Controller will be used even if there is one in the shared.
4) i saw people always create
EditorTemplates folder in shared view but in this case it is created
in home folder.....why ?
If it is in Shared, that means any other controller with same ViweModel type name MyViewModel can use that same view. If it is in home, that means it is only available to "Home" controller specific.
5) if there are so many other view in this location then how this line
#Html.EditorForModel() would load this specific view
MyViewModel.cshtml from this location
/Views/Home/EditorTemplates. i am new in mvc and learning. so please
help me to understand how the above code will work?
That is the convention, as I answered above, there is certain pattern which mvc looks view in, the first match get applied.
Edit
Thank Stephen Muecke for pointing out, I was typing too fast.
The search Pattern for view is:
Views{Controller}\EditorTemplates\MyViewModel.cshtml
Views\Shared\EditorTemplates\MyViewModel.cshtml
So if it find it, it will stop looking. This means if it found in the current controller (in your example is Home), then it stop looking. It only continue to look when it can not find it in the controller specific folder.
Edit 2 - include some reason to use Editor template
the reason for writting editor/display template is for code reuse.
Consider using jquery datepicker on a datetime property.
If you don't use Editor template, then you are going to duplicate code in the view to use jquery datepicker.
If you use editor template, then if some day you want to change jquery datepicker to some other plugin, you only change in the editor template, and no where else. Hence Don't Repate Yourself (DRY) principal.
This also keep same consistency of ui across multiple page when showing same type of input.
The example I gave above is on one property, but if a template is for whole model, that is where EditorForModel comes in, but same idea.
Consider this article on some usage of template
https://www.simple-talk.com/dotnet/asp.net/extending-editor-templates-for-asp.net-mvc/
And below is a more depth detail on template written for asp .net mvc 2. But same thing apply to mvc 4 and 5
http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-1-introduction.html
I'm using an Editor Template to make an Html.EditorFor(property) in my viewModel's view. There's a different .cshtml file containing "#Html.EditorFor(property)".
Now, depending on the value of a property of my viewModel, I need to display a DisplayFor instead.
I tried doing this by adding some conditional logic in my EditorTemplate but can't seem to access the properties of my viewModel from there (since the editor template is using #model.someOtherModel and not #viewModel). So if I can say something like
// Razor
if(true)
{ EditorFor(property) }
else {DisplayFor(property)}
in my viewModel's view, that would work. I just don't know how to define a "Display Template" for my object, in the same way I defined an Editor Template.
Another solution might be accessing the viewModel data from the Editor template... is this possible?
In Razor, you should be using something like the following syntax:
#if (condition) {
#Html.EditorFor(modelItem => model.property)
} else {
#Html.DisplayFor(modelItem => model.property)
}
Are you getting a specific error you can share?
'Add' and 'Edit' views are typically more or less identical. How can I reuse a View so that Foos/Add and Foos/Edit/[Id] both use it? What would the actions look like?
Thanks
Simply specify the view name when calling the View() method like
public ViewResult Add() {
//...
return View("Foo");
}
public ViewResult Edit(int id) {
//...
var model = repository.get(id);
return View("Foo", model);
}
Your view will have to handle null/empty model values for the Add action or you could populate your model with default values.
You may want to consider using an Editor Template as opposed to reusing the same View. An Editor Template is a partial View that is used for editing and/or inserting data.
This would require separate views but the code would be minimal. The bulk of the code would be in the template which you would reuse for both the Add and Edit actions.
After you create your template, your Add View would look like (Razor):
#model Models.Foo
<h2>Add</h2>
<p>
#Html.EditorFor(model => model) // equivalent to EditorForModel()
</p>
And your Edit View would look like:
#model Models.Foo
<h2>Edit</h2>
<p>
#Html.EditorFor(model => model) // equivalent to EditorForModel()
</p>
I would use jQuery ajax. Clicking on Add or Edit would call serve action which will return the same PartialView (if you want to reuse it).
Then, in the success function of ajax call, you have just to put returned html (from that PartialView) into certain part of your page (or popup).
Nice and clean and no page reload...
I have written an Action method for an ASP.NET MVC controller, which is being used to provide a model to a usercontrol.
public class ProductsController : Controller
{
public PartialViewResult ProductSummary()
{
ViewData.Model = new ProductSummaryModel("42"); // dummy data for now
return new PartialViewResult()
{
ViewData = ViewData
};
}
}
I am using the 'futures' Microsoft.Web.Mvc dll and rendering the control in my main view like this :
<% Html.RenderAction<ProductsController>(x => x.ProductSummary()); %>
What I have here appears to work just fine, but i attempted to google new PartialResult() to see if what I was doing was following the correct patterns.
Currently this search only comes up with 4 results!
So I figured I'm doing somethin wrong here in my controller. Whats the correct way to create an action method that returns a partial view? And what (if anything) is wrong or bad about what I'm doing.
I usually just use:
return PartialView("MyView", myModel);
But this just returns a new PartialViewResult("MyView", myModel) so it is potatoes/potatoes.