Extend HiddenFor Templates in ASP.NET MVC - c#

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.

Related

How do you put a ViewBag item into a Text Box?

I have the following code and I get an error saying:
has no applicable method named 'TextBoxFor' but appears to have an extension method by that name.
My Code:
#Html.TextBoxFor(ViewBag.taglist)
Why don't you use strongly typed model in your view instead of ViewBag. This will make your life easier.
In fact, you must use a model to with TextBoxFor, otherwise it just won't work. See the definition of TextBoxFor - as a second parameter it takes a lambda expression that takes a property form a model.
If you want just a text box, two options:
#Html.TextBox("NameOfTheTextbox", (String)ViewBag.SomeValue)
or just go
<input type="text" value="#ViewBag.SomeValue" />
No complex solutions required.
I agree with other suggestions of using a strongly-typed model, because the compile-time error support is so much better than debugging exceptions. Having said that, in order to do what you want, you can use this:
#Html.TextBox("NameOfTextBox", (string)ViewBag.taglist)
Update: A Simple Example
Now that you've provided some details in your comments, I've taken a guess at what you might be doing, in order to provide a simple example.
I'm assuming you have a list of tags (like SO has per question) that you'd like to display neatly in a textbox, with each tag separated by a space. I'm going to assume your Tag domain model looks something like this:
public class Tag
{
public int Id { get; set; }
public string Description { get; set; }
}
Now, your view will need a list of the tags but will likely need some other information to be displayed as well. However, let's just focus on the tags. Below is a view model to represent all the tags, taking into account that you want to display them as a string inside a textbox:
public class SomeViewModel
{
public string Tags { get; set; }
// Other properties
}
In order to get the data you want you could grab all of the tags like this:
public ActionResult Index()
{
using (YourContext db = new YourContext())
{
var model = new SomeViewModel();
model.Tags = string.Join(" ", db.Tags.Select(t => t.Description).ToList());
return View(model);
}
}
Notice how I'm directly passing model to the view.
The view is now very simple:
#model SomeViewModel
#Html.EditorFor(m => m.Tags)
The model directive is what signifies that a view is strongly-typed. That means this view will expect to receive an instance of SomeViewModel. As you can see from my action code above, we will be providing this view the type that it wants. This now allows us to make use of the strongly-typed HtmlHelper (i.e. Html.XxxFor) methods.
In this particular case, I've used Html.EditorFor, as it will choose an appropriate input element to render the data with. (In this case, because Description is a string, it will render a textbox.)
You cannot use Html.TextBoxFor without explicitly setting a type for your model within the view. If you don't specify a type it defaults to dynamic. If you want to do model binding then you must use an explicit type rather than a dynamic type like ViewBag. To use Html.TextBoxFor you must define a model type that defines the property that you wish to bind. Otherwise you have to use Html.TextBox and set the value manually from ViewBag. As others have said, you will make your life much easier if you use a statically typed model and take advantage of the inbuilt MVC model binding.
You have to use a lambda expression to select the property, plus you will have to cast the ViewBag member to the correct type.
#Html.TextBoxFor(model => (string)ViewBag.taglist)

Should View Models always contain the inputs for forms in that view?

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?

How to add MetaData to a dynamically build MVC3 ViewModel?

One of the key features of a project I'm working on is the ability for the user to configure Forms (as in "Forms" to fill-up) based on a pool of pre-existing field types (well known types, for instance "user name", "date of birth" etc. but also "generic types" like "string", "DateTime" etc.).
We used to have a static ViewModel that worked fine for the "well known" types and looked like this:
public class UserInputModel
{
[StringLength(200)]
public string Name { get; set; }
[Required(ErrorMessageResourceName = "BirthDateEmptyError", ErrorMessageResourceType = typeof(Resources.ErrorMessages))]
public DateTime BirthDate { get; set; }
//Here comes a lot of other properties
}
All the known properties were listed and we were showing or hiding them given the context.
But the last requirement came and changed all that. The user shall now be able to add as many generic type fields as he wants. In order to do this, we decided to make this InputModel entirely dynamic. It now looks like this:
public class UserInputModel
{
// Each ModelProperty has an "Id" and a "Value" property
public ICollection<ModelProperty> Properties { get; set; }
}
This works like a charm. The razor view only has to iterates over the collection, create the corresponding controls for each property of the collection in a more than standard way:
#Html.TextBoxFor(m => m.Properties[index].Value);
... and we nicely get the data back as a filled form.
=> This works fine, but we don't have any client-side validation. For this, we would need some Metadata... which we don't have via annotations anymore since we're dynamically creating the model.
In order to provide those MetaData, I created a CustomModelMetadataProvider that inherits from DataAnnotationsModelMetadataProvider and registered it as the new ModelMetadataProvider in the Global.asax. The CreateMetadata() function gets called upon creation of the ViewModel, and that for each of the properties of my ViewModel... sofar so good.
Where the problem starts: in order to add some metadata to the current property, I first need to identify which property I am currently looking at ("Name" has a maxlength of 200, "date of birth" hasn't so I cannot assign a maxlength to every property per default). And somewhow I didn't manage to do that yet since all the properties have the same name Value and the same container type ModelProperty.
I tried accessing the container of the property via reflection, but since the ModelAccessor's target is the ViewModel itself (because of the lambda expression m => m.Properties), the following construct gives me the ViewModel as a whole, not just the ModelProperty:
var container = modelAccessor.Target.GetType().GetField("container");
var containerObject = (UserInputModel)container.GetValue(modelAccessor.Target);
I've been flipping this over and over but cannot find a way to identify which ModelProperty I have in hand. Is there a way to do this?
Update: after flipping this in every possible direction for a while, we finally went another way. We are basically using unobstrusive javascript to use MVC's validation capabilities without touching attributes nor metadata. In short, we add HTML attributes like value-data="true" (and all other required attributes) to the #Html.TextBoxFor() statements. This works wonderfully for all the atomic validations (required, stringlength etc.).
Tim, you can leverage what appears to be client-side validation through Ajax with the Remote attribute on your properties.
Basically, you'll need to set up a validation controller and then write some smarts into that controller. But at least you'd be able to write some helper methods and keep it all in one place. You would have a series of validators, based on the meta data that you are presenting to the end users, and each validator method would work for a particular type with good re-use.
The one pitfall to this approach would be that you would need to write a validation method for each type and condition that you want to support. Sounds like you're having to go down that road anyways, though.
Hope this helps.
See if this article help you: Technique for carrying metadata to View Models with AutoMapper.
Also use this one for ideas (custom model metadata provider): changing viewmodel's MetadataType attribute at runtime
Fluent validation is probably the best option for you in my mind, but its obviously up to you to select the best match among those above.
Update
Try use ModelMetadata and override ModelMetadataProvider: Dive Deep Into MVC: ModelMetadata and ModelMetadataProvider. This way you completely customize your model metadata (this replaces data annotations) and you have complete control on what is happening, rather than relying on ASP.NET MVC.
Another good place to look at it is Creating your own ModelMetadataProvider to handle custom attributes.
Hope this all is of help to you.

HtmlHelper that Binds Model Propertys. Fluent Interface

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.

Why not use Html.EditorForModel()

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.

Categories