I would like to create an HtmlHelper extension method that allows you to set the model instance and then bind properties of this model to render some html.
This would be following a similiar pattern close to the Telerik Grid.
In the following example, we see that we set the Model as the Grid's DataContext.
Then declare some columns. Foreach column, they bind a property in respect the the Model which is IEnumerable. So when the grid renders rows, the respective property is bound from the model's instance for that row.
#(Html.Telerik().Grid(Model)
.Name("Grid")
.Columns(columns =>
{
columns.Bound(o => o.OrderID).Width(100);
columns.Bound(o => o.ContactName).Width(200);
columns.Bound(o => o.ShipAddress);
columns.Bound(o => o.OrderDate).Format("{0:MM/dd/yyyy}").Width(120);
})
.Scrollable(scrolling => scrolling.Enabled((bool)ViewData["scrolling"]))
.Sortable(sorting => sorting.Enabled((bool)ViewData["sorting"]))
)
Basically, I would like ideas on how to implement the Bound method. For my scenario, the Model will also be of IEnumerable. So lets say I have a Model that is List<MyObj>
For argument sake, MyObj looks something like this:
public class MyObj
{
public bool IsRegistered { get; set;}
public string Name { get; set; }
}
I would like to end up in the view calling an htmlhelper that would render my list and look something like this:
#(Html.MyHelpers().MyList(Model)
.ControlOne.Bound(o => o.IsRegistered)
.ControlTwo.Bound(o => o.Name)
)
I haven't tried a whole lot yet and to be honest I'm not sure how to implement this. So I will be posting updates as this will be a work in progress. But, if there are any ideas or suggestions, I would appreciate it. I know I could use the grid, but this helper will be lighter weight for I don't need very much functionality and don't want to drag around the telerik grid for this purpose.
Thanks!
http://publicvoidlife.blogspot.com/2011/02/htmlhelper-extensions.html this post might help you to write strongly typed HtmlHelper extensions.
Relevant Question:
Is it possible to create a custom ASP.NET MVC strongly typed HTML Helper?
And you might want to check out some opensource projects like mvccontrib and djme
Have you had a look at the source code for Html.DisplayForModel() (since ASP.NET MVC is open source)? Maybe it will give you some clues.
Source code for ASP.NET MVC 3 RTM is here: http://aspnet.codeplex.com/releases/view/58781
The bits you need to look at are DisplayExtensions.cs and TemplateHelpers.cs in mvc3-rtm-sources\mvc3\src\SystemWebMvc\Mvc\Html
The point is that you can already use Html.DisplayForModel() to render a control for each property of your model and you can see how it's done by looking at the source code, so you could easily tweak it.
There's also EditorExtensions.cs which contains Html.EditorForModel() which gives you the edit version.
The important thing about ASP.NET MVC is that you can swap out any part of it and write your own, so you can achieve pretty much anything.
Related
I have a collection of items, name it type "A", that I want to view and edit some of its attributes in a View. I would like it to save simultaneously, however, this does not seem to work as it does not seem like it is passing anything back to the Post method.
My Controller:
[HttpPost]
public ActionResult inline(IEnumerable<A> listA)
{
for (int i = 0; i <= listA.Count(); i++ )
{
A theObj = listA.ElementAt(i);
db.SaveChanges();
} //Somehow this is returning to be Null
}
My View:
#model IEnumerable<A>
#using (Html.BeginForm())
{
#Html.EditorForModel("Multiple")
}
So far this prints out all the Id of the entries (with no formatting/line breaks, of course) for some reason.
And I am not sure how to create an editor template "Multiple", this is what I have so far:
#model Models.A
#Html.EditorFor(x => x.Id)
I'm not sure where to put it / create it, so I just made it as another view in the same folder.
Any pointers how I can make this to work, so that I can edit multiple entries of the same object in the same view and pass it back to the controller and save it? I'm a newbie to MVC, so if this seems like a really simple question, I apologize in advance. Thanks!
Like most things in programming, there's multiple ways to achieve this. If you want to go the editor template route, though, it's pretty straight-forward.
As #JamieD77 pointed out, editor templates go into Views\Shared\EditorTemplates. The most important part of that path is the EditorTemplates directory convention, though. Just as with any other view in MVC, you can override/fallback depending on where you place your view. For example, Areas\Foo\Views\Shared\EditorTemplates, will work as well, but then it's only available to the Foo area. Or, you can override it for a particular controller by placing it in Views\Foo\EditorTemplates.
Then, the name of the view should correspond with the type it's intended to be used with. In your case, the view should be named A.cshtml. You can technically specify the view name manually by either passing it to EditorFor or using something like UIHint, but it's easier and more foolproof to just rely on convention here.
Inside this view, you should create the look and feel you want to have a for a single instance of A, with all editable properties represented. Then, in your main view, you simply call Html.EditorFor on the collection property, which in your case here, is the whole model:
#Html.EditorFor(model => model)
Razor will realize it has a collection and render the editor template for each item in the collection. Importantly, because it has this context, it will also be able to generate the appropriate indexes on the field names.
If you did something like the following instead:
#foreach (var item in Model)
{
#Html.EditorFor(m => item)
}
Your field names would not be indexed, and the modelbinder would not know what to do with the posted data. If you wanted to use a loop, you would have to use indexing inside the loop:
#for (var i = 0; i < Model.Count(); i++)
{
#Html.EditorFor(m => Model[i])
}
That then gives Razor the proper context to generate appropriate field names. However, importantly, that approach requires utilizing a List<T> structure, rather than something like IEnumerable<T> or ICollection<T>.
I thought Html.HiddenFor could use Templates like Html.DisplayFor or Html.EditorFor. Unfortunately the method doesn't accept a TemplateName like the others.
I know, the workaround would be to use a DisplayFor/EditorFor Template which has HiddenFors. But I would like to find out how to extend the Html.HiddenFor method. Anyone?
Regards
Seems like you are mislead by wrong analogy. HiddenFor corresponds exactly to the <input type="hidden"/> tag. Just like TextBoxFor, CheckBoxFor etc. These methods are not designed to use templates. DisplayFor/EditorFor on the other side are specially created to be used with templates defined in the project. Thus what you are asking for is not possible out-of-the-box.
However you can always define your own overload for HiddenFor with whatever set of parameters and whatever logic you might require.
There is an overload which accept additional parameter - htmlAttributes. And you can use it for add some attributes to the result tag.
Also the second way is to create razor partial view in one of the folders
~/Areas/AreaName/Views/ControllerName/DisplayTemplates/TemplateName.cshtml
~/Areas/AreaName/Views/Shared/DisplayTemplates/TemplateName.cshtml
~/Views/ControllerName/DisplayTemplates/TemplateName.cshtml
~/Views/Shared/DisplayTemplates/TemplateName.cshtml
with name HiddenInput.cshtml
Here's what you do, you create it as an editor template, because as Andre pointed out, HiddenFor is equivalent to the helper methods like TextBoxFor and CheckboxFor.
It's likely that you'll want to have an actual editor too, so place your real editor under ~/Shared/EditorTemplates. We're going to put our "hidden editor" under the controller you wish to use it on.
~/Views/ControllerName/EditorTemplates/ModelName.cshtml
Lets say we have a Person model.
public class Person
{
public string First { get; set; }
public string Last { get; set; }
}
We'll create a partial view.
#Model Person
#Html.HiddenFor(p => p.First);
#Html.HiddenFor(p => p.Last);
And then we'll pretend we have a model that contains a Person as a property. From our main view, we call our "hidden editor" like so.
#Model Foo
#Html.EditorFor(f => f.Person)
Easy peasy lemon squeezy. A bit hacky, but it works like a charm.
This question is related to this one, but I think in my example I have detail which may alter answers.
Say I have a User action on a Controller that renders a View for displaying data about a particular User, it might have a UserViewModel like so:
public class UserViewModel {
public string FirstName;
public string LastName;
etc...
}
However, in this View, as well as showing this user data, I want to have a search textbox for the user so they can look up another user on this page. This form would post into an action of FindUser, which accepts the following model:
public class FindUserInputViewModel {
[Required]
public string SearchQuery;
}
If this action finds the model to be invalid, it redirects back to the User action, maintaining the ModelState.
Now, currently to show the validation error, I cannot use a strongly-typed helper as that search query property isn't in UserViewModel, I'd have to do this:
#Html.TextBox("SearchQuery")
#Html.ValidationMessageFor("SearchQuery")
This works, and the error will be displayed, as well as the old value that was POSTed being shown (as it is persisted in the ModelState). However, I'd prefer to use a strongly-typed helper wherever possible.
From all the examples I have seen, the pattern here seems to be that the UserViewModel should contain the FindUserInputViewModel inside it, perhaps as an FindUserInput property. I could then do:
#Html.TextBoxFor(m => m.FindUserInput.SearchQuery)
This also works, as long as I make sure my FindUser action binds to the correct prefix, or I specify a name in the TextboxFor method call.
However, I don't really see why my UserViewModel should contain this other ViewModel simply for the case of binding the validation using this helper. Does it bring other benefits that I am not seeing? I understand the use of it if your View's model is needing to render out the same data you are posting, such as on a typical Edit action, but that isn't the case here.
It looks like to me that what would be handy here is a another generic helper that can reference a different type, something like this:
#Html.TextBoxForType<FindUserInput>(m => m.SearchQuery)
This doesn't exist, but I think I should be able to write that, and this is a good case for one. Does that sound like an appropriate solution, or am I missing something here?
Another option entirely is perhaps that the small-form for posting in that FindUserInputViewModel should have its own GET action as well as POST, and then the User View can just call into it using #Html.Action. It could then render a partial-view that is only strongly typed to FindUserInputViewModel.
Why not create a partial view for your search and simply pass this a new FindUserInputViewModel from your user view?
#Html.Partial("FindUser", new FindUserInputViewModel())
You can type your partial view to FindUserInputViewModel and use strongly-typed helpers in there. I'd say this is the simplest and neatest approach, unless there's something I'm missing?
I am currently trying to figure out how you can bind multiple fields in an MVC 3 view to 1
property on a view model and what is recommended when trying to achieve this.
Using an example for the number of minutes (only) that it takes to prepare something.
The form might look something like this:
Preparation time - Hours: [ 1 ] Minutes: [ 30 ]
and the submitted form values need to be converted into minutes and bound to the PreparationTimeInMinutes property of the ViewModel.
ViewModel property:
public short PreparationTimeInMinutes { get; set; }
and the View is strongly typed to the ViewModel and currently has the below:
<div class="editor-label">
#Html.LabelFor(model => model.PreparationTimeInMinutes)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.PreparationTimeInMinutes)
#Html.ValidationMessageFor(model => model.PreparationTimeInMinutes)
</div>
Here are a few things that I began thinking about in order to achieve this.
Change the PreparationTimeInMinutes property to a ViewModel that has an hour & minute
property and create Editor and display templates for it so that
MVC3 can sort out the binding and validation?
Use javascript and a hidden field for the preparation time.
Some other approach that lets you use the MVC model binding to do this sort of thing?
Does anyone have any suggestions on best practice for this sort of thing? or if I am way off.
Please let me know. Thanks
The Javascript isn't bad but not really MVCish. I would think the better approach would be a view model with those two fields or making a new class with the editor/display templates.
I agree with SLaks; you can create a custom model binder to handle this but it is probably overkill. I created a custom binder for a type with one date and two times based on this article, though mine is much simpler. But if you just want the practice or to learn more about model binding, it is doable.
EDIT: Sorry, just in case you decide to try the ModelBinder route, that example is a little old (maybe MVC 1?!). I forgot about having to make additional modifications. These links should help with updating that example. Good luck.
You should use the first option.
This is exactly what editor templates are made for.
If you want to, you can create a custom ModelBinder to bind two POSTed values to a single number (along with a similar editor template) and apply an attribute to the property to select that ModelBinder.
However, that would probably be overkill.
Thanks SLaks.
Using the first option I did the below in my view model
public TimeInMinutesViewModel PreparationTimeInMinutes { get; set; }
Made this view model
public class TimeInMinutesViewModel {
public string Label { get; set; }
public short Hours { get; set; }
public short Minutes { get; set; }
}
and this editor template
#model TimeInMinutesViewModel
<div>#Html.LabelFor(x => x.Label)</div>
<div class="editor-label">#Html.LabelFor(x => x.Hours)</div>
<div class="editor-field">#Html.EditorFor(x => x.Hours)</div>
<div class="editor-label">#Html.LabelFor(x => x.Minutes)</div>
<div class="editor-field">#Html.EditorFor(x => x.Minutes)</div>
and in the view
#Html.EditorFor(model => model.PreparationTimeInMinutes)
#Html.ValidationMessageFor(model => model.PreparationTimeInMinutes)
I haven't tested this yet and I might need to do some changes for the labels but this looks like it might work for 2 different properties that I want to use this approach for.
The problem I have got now is being able to map these to my entity property which is a short int.
I am using AutoMapper so I am hoping this will work with a custom type converter, at least in 1 direction but I'm not sure about both directions :(
Ok I just discovered about the EditorForModel in MVC and I want to know when I should use this instead of an EditorFor on each of my property? And why does when I add a strongly typed view it does not use this and build an EditorFor on every property?
I'm late on this... but thanks for the info!
Since the accepted answer is a link-only answer (and was removed), I thought I'd actually answer the question derived from Brad Wilson's Blog: ASP.NET MVC 2 Templates, Part 1: Introduction.
The model expressions are simple helpers which operate on the current model. The line DisplayForModel() is equivalent to DisplayFor(model => model).
TL;DR the same idea can be assumed for EditorFor(model => model) and EditorForModel(); these helper methods achieve the same thing. EditorForModel() assumes the model expression is the #model that was passed to the view.
Take the following models and view for example:
public class Person
{
public string Name {get; set;}
public Address MailingAddress {get; set;}
}
public class Address
{
public String Street {get; set;}
public String City {get; set;}
public String State {get; set;}
}
Create.cshtml:
#model MyNamespace.Models.Person
/* So, you need an Editor for the Person model? */
#Html.EditorForModel()
/*the above is equivalent to #Html.EditorFor(model => model) */
/* you need to specify the Address property that the editor accepts? */
#Html.EditorFor(model => model.MailingAddress)
You should use it when possible, but sometimes you will need the customizability of individual Html.EditorFor uses.
As for why the built-in templates don't use it, that's mainly because they are silly in general, but also because, if I recall, they need to wrap elements (like table rows etc.) around each Html.EditorFor.
#Html.EditorForModel() ?? And give up the fun of writing your own view? smile
Besides the fun, doing so as a habit is rather dicey. Consider the following common scenario - you have a bool variable say IsMale in your database in your customer table. Well obviously you don't want the default version (IsMale with a check-box) - you probably want something a bit more friendly, say a {select, Options .... , /select} tags, right? that's where the view really starts kicking in. That's the customization. Every view is a little different. You have the RAZOR engine, exploit it to the max! In your view you can override anything, or even manually type an entire chunk of HTML code of your own.