Access displayName attribute from the model in MVC View - asp.net

If my model have
[DisplayName("First Name")]
public string firstName { get; set; }
Then I can print it in the View with LabelFor
#Html.LabelFor(model => model.acc_first)
It will then render as
<label for="firstName">First Name</label>
But how can I "raw" read the property (FirstName) attributes in the View?
If for example, I want to send the value to a function on the View page

#Html.DisplayNameFor(x => x.acc_first)

To access the attributes you'll need a custom Html helper. Since the attributes aren't really part of the property or model you need to go in a round about way to access them.
public static IHtmlString DisplayName<TModel, TValue>(
this HtmlHelper<TModel> html,
Expression<Func<TModel, TValue>> expression) {
var metadata = ModelMetadata.FromLambdaExpression<TModel, TValue>(expression, html.ViewData);
return new HtmlString(metadata.DisplayName);
}
For additional attributes that aren't part of the DataAnnotations you can do the following:
Create a custom attribute
public class TooltipAttribute : Attribute, IMetadataAware {
public TooltipAttribute(string tooltip) {
this.Tooltip = tooltip;
}
public string Tooltip { get; set; }
public void OnMetadataCreated(ModelMetadata metadata) {
metadata.AdditionalValues["Tooltip"] = this.Tooltip;
}
}
The magic happens in the OnMetadataCreated implementation. Here we can populate the AdditionalValues with anything we need for our particular Attribute. In this case we’re adding a Tooltip key. Your name should be unique as other attributes or providers may add their own keys with the same name It is important to remember that attributes are not always read in the same order. So your tooltip attribute may be called first, last or somewhere in the middle. This is an important distinction as it may cause undesired effects.
Then create a custom Attribute helper
public static IHtmlString TooltipFor<TModel, TValue>(
this HtmlHelper<TModel> html,
Expression<Func<TModel, TValue>> expression) {
var metadata = ModelMetadata.FromLambdaExpression<TModel, TValue>(expression, html.ViewData);
if (metadata.AdditionalValues.ContainsKey("Tooltip"))
return new HtmlString((string)metadata.AdditionalValues["Tooltip"]);
return new HtmlString("");
}

You can access the value of the attribute Display(Name="...") in 4 steps:
var type = typeof(YourNamespace.Models.YourModelName);
var memInfo = type.GetMember("firstName"); // your member
var attributes = memInfo[0].GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.DisplayAttribute), false);
var displayname = ((System.ComponentModel.DataAnnotations.DisplayAttribute)attributes[0]).Name;
Then displayname will have the value First Name in your context.

Building on Darren Oster's answer:
#Html.DisplayNameFor(x => x.acc_first)
Here's a link to the documentation for this: https://msdn.microsoft.com/en-us/library/system.web.mvc.html.displaynameextensions.displaynamefor(v=vs.118).aspx
You could use this in your view code like this:
#{
var foo = Html.DisplayNameFor(x => x.acc_first);
// call function
DoStuff(foo);
}

you should simply use
[Display(Name = "First Name")]
public string name{ get; set; }

<label asp-for="firstName">First Name</label>
you must change for to asp-for

[Display(Name ="First Name")]

#Html.DisplayFor(model => model.acc_first)
should work for you. If not, try just
#Model.acc_first
Either one should work fine.

#(This.Model.acc_first)
Should work
so in javascript you can use it like
function callme(val)
{
//some ajax call with
#(This.Model.acc_first)
}

Related

Entering Value in Partial View and Posting it back to Main Controller in ASP.NET MVC 5 [duplicate]

I have a ViewModel that has a complex object as one of its members. The complex object has 4 properties (all strings). I'm trying to create a re-usable partial view where I can pass in the complex object and have it generate the html with html helpers for its properties. That's all working great. However, when I submit the form, the model binder isn't mapping the values back to the ViewModel's member so I don't get anything back on the server side. How can I read the values a user types into the html helpers for the complex object.
ViewModel
public class MyViewModel
{
public string SomeProperty { get; set; }
public MyComplexModel ComplexModel { get; set; }
}
MyComplexModel
public class MyComplexModel
{
public int id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
....
}
Controller
public class MyController : Controller
{
public ActionResult Index()
{
MyViewModel model = new MyViewModel();
model.ComplexModel = new MyComplexModel();
model.ComplexModel.id = 15;
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
// model here never has my nested model populated in the partial view
return View(model);
}
}
View
#using(Html.BeginForm("Index", "MyController", FormMethod.Post))
{
....
#Html.Partial("MyPartialView", Model.ComplexModel)
}
Partial View
#model my.path.to.namespace.MyComplexModel
#Html.TextBoxFor(m => m.Name)
...
how can I bind this data on form submission so that the parent model contains the data entered on the web form from the partial view?
thanks
EDIT: I've figured out that I need to prepend "ComplexModel." to all of my control's names in the partial view (textboxes) so that it maps to the nested object, but I can't pass the ViewModel type to the partial view to get that extra layer because it needs to be generic to accept several ViewModel types. I could just rewrite the name attribute with javascript, but that seems overly ghetto to me. How else can I do this?
EDIT 2: I can statically set the name attribute with new { Name="ComplexModel.Name" } so I think I'm in business unless someone has a better method?
You can pass the prefix to the partial using
#Html.Partial("MyPartialView", Model.ComplexModel,
new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "ComplexModel" }})
which will perpend the prefix to you controls name attribute so that <input name="Name" ../> will become <input name="ComplexModel.Name" ../> and correctly bind to typeof MyViewModel on post back
Edit
To make it a little easier, you can encapsulate this in a html helper
public static MvcHtmlString PartialFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, string partialViewName)
{
string name = ExpressionHelper.GetExpressionText(expression);
object model = ModelMetadata.FromLambdaExpression(expression, helper.ViewData).Model;
var viewData = new ViewDataDictionary(helper.ViewData)
{
TemplateInfo = new System.Web.Mvc.TemplateInfo
{
HtmlFieldPrefix = string.IsNullOrEmpty(helper.ViewData.TemplateInfo.HtmlFieldPrefix) ?
name : $"{helper.ViewData.TemplateInfo.HtmlFieldPrefix}.{name}"
}
};
return helper.Partial(partialViewName, model, viewData);
}
and use it as
#Html.PartialFor(m => m.ComplexModel, "MyPartialView")
If you use tag helpers, the partial tag helper accepts a for attribute, which does what you expect.
<partial name="MyPartialView" for="ComplexModel" />
Using the for attribute, rather than the typical model attribute, will cause all of the form fields within the partial to be named with the ComplexModel. prefix.
You can try passing the ViewModel to the partial.
#model my.path.to.namespace.MyViewModel
#Html.TextBoxFor(m => m.ComplexModel.Name)
Edit
You can create a base model and push the complex model in there and pass the based model to the partial.
public class MyViewModel :BaseModel
{
public string SomeProperty { get; set; }
}
public class MyViewModel2 :BaseModel
{
public string SomeProperty2 { get; set; }
}
public class BaseModel
{
public MyComplexModel ComplexModel { get; set; }
}
public class MyComplexModel
{
public int id { get; set; }
public string Name { get; set; }
...
}
Then your partial will be like below :
#model my.path.to.namespace.BaseModel
#Html.TextBoxFor(m => m.ComplexModel.Name)
If this is not an acceptable solution, you may have to think in terms of overriding the model binder. You can read about that here.
I came across the same situation and with the help of such informative posts changed my partial code to have prefix on generated in input elements generated by partial view
I have used Html.partial helper giving partialview name and object of ModelType and an instance of ViewDataDictionary object with Html Field Prefix to constructor of Html.partial.
This results in GET request of "xyz url" of "Main view" and rendering partial view inside it with input elements generated with prefix e.g. earlier Name="Title" now becomes Name="MySubType.Title" in respective HTML element and same for rest of the form input elements.
The problem occurred when POST request is made to "xyz url", expecting the Form which is filled in gets saved in to my database. But the MVC Modelbinder didn't bind my POSTed model data with form values filled in and also ModelState is also lost. The model in viewdata was also coming to null.
Finally I tried to update model data in Posted form using TryUppdateModel method which takes model instance and html prefix which was passed earlier to partial view,and can see now model is bound with values and model state is also present.
Please let me know if this approach is fine or bit diversified!

Add ValidationAttribute on server side validation at runtime depending on another container property

There is a class (stripped down version):
public class Parameter
{
public Guid Id { get; set; }
public decimal Value { get; set; }
}
In my view I have a dictionary of Parameters, they get rendered like this:
<input ... name="Parameters[3].Key" type="hidden" value="UniqueParamName" />
<input ... name="Parameters[3].Value.Id" type="hidden" value="395816ad-dfde-11e1-8c36-848f69f05f09" />
<input ... name="Parameters[3].Value.Value" type="text" value="75" />
Validation rules are going to be set by user though some GUI and stored in a database.
I want to get ValidationAttributes from a database by Parameter.Id for current Parameter.Value, but I can't see any way of doing it.
I also tried to add attributes in ModelMetadataProvider.CreateMetadata, where container can be accessed through some [nasty] reflection, but the "Id" has not yet been assigned when metadata is being created for the "Value" (anyway, it's not reliable).
Maybe I'm overcomplicating the whole thing, I just want to stick to the built-in validation.
public class ExtendedValidationProvider : DataAnnotationsModelValidatorProvider
{
protected override IEnumerable<ModelValidator> GetValidators(
ModelMetadata metadata,
ControllerContext context,
IEnumerable<Attribute> attributes)
{
if (metadata.ContainerType == typeof(Parameter)
&& metadata.PropertyName == "Value")
{
Guid parameterId = <some magic code>;
IEnumerable<Attribute> validationAttributes
= db.GetValidationAttributesByParameterId(parameterId);
return base.GetValidators(metadata, context, validationAttributes);
}
return Enumerable.Empty<ModelValidator>();
}
}
In my opinion, you should be using Model Validation. Model Validation will verify against your model and the rules applied with the validation attributes defined on the model.
public class MyModel
{
// This model uses model validation
[Required(ErrorMessage = "FirstName is a required field.")]
[StringLength(50, ErrorMessage = "Your FirstName can not exceed 50 characters.")]
public string FirstName { get; set; }
[Required(ErrorMessage = "LastName is a required field.")]
public string LastName { get; set; }
}
This sets up your model for validation. Then in your controller/action you will need to verify your ModelState using ModelState.IsValid. You can do some custom "verifying" in your controller as well and add custom errors by using AddModelError of the ModelState.
If you wanted to take it a step further and create your own validation attribute, then all you need to do is create a new class that is inherited from the ValidationAttribute class. This would allow you to verify against your own values that might be in the DAL layer or against another value that was bound to your model.
Using model validation also sets you up nicely (and easily) if you needed to use globalization as there are attributes available that will allow you to select a resource file with your resource strings for various languages that you might have defined.
I'm doing this from memory, but this should lead you in the right direction to use the built-in features of MVC3 and take advantage of model binding and model states.
HTH
I figured I can set container in a custom ModelMetadataProvider like this:
public override ModelMetadata GetMetadataForProperty(Func<object> modelAccessor,
Type containerType, string propertyName)
{
var metadata = _InnerProvider.GetMetadataForProperty(modelAccessor, containerType, propertyName);
if (containerType == typeof(Parameter)
&& !metadata.AdditionalValues.ContainsKey("container"))
{
metadata.AdditionalValues["container"] = GetContainer(modelAccessor);
}
return metadata;
}
GetContainer is a method using that [nasty] reflection (compiled and cached expression, actually).
Finally, there is no magic code on the validation provider's side:
public class ExtendedValidationProvider : DataAnnotationsModelValidatorProvider
{
protected override IEnumerable<ModelValidator> GetValidators(
ModelMetadata metadata,
ControllerContext context,
IEnumerable<Attribute> attributes)
{
if (metadata.ContainerType == typeof(Parameter)
&& metadata.PropertyName == "Value")
{
Guid parameterId = ((Parameter)metadata.AdditionalValues["container"]).Id;
IEnumerable<Attribute> validationAttributes
= db.GetValidationAttributesByParameterId(parameterId);
return base.GetValidators(metadata, context, validationAttributes);
}
return Enumerable.Empty<ModelValidator>();
}
}

MVC model binding naming convention for child objects?

I'm having trouble with default model binding naming convention when there is a child property. For example:
I have a ViewModel which looks something like this:
public class UserViewModel
{
public User BusinessObject { get; set; }
}
My User class has a property called "NetworkLogin"
My View has something like this:
<%: Html.LabelFor(model => model.BusinessObject.NetworkLogin)%>
<%: Html.TextBoxFor(model => model.BusinessObject.NetworkLogin)%>
Auto-Fill
And my controller, what I'd like to do, is
[HttpGet]
public ActionResult UserIndex(string networkLogin) { }
The problem:
The input parameter "networkLogin" is always null. This makes sense, because the actual parameter on the html element is name="BusinessObject.NetworkLogin" and id="BusinessObject_NetworkLogin". However, I don't know what parameter name I should use in my action method. I've tried "businessObject_NetworkLogin" and it doesn't work either.
However, I have this workaround that does work, but I don't like it. I add this to my ViewModel:
public string NetworkLogin
{
get
{
if (BusinessObject == null)
BusinessObject = new User();
return BusinessObject.NetworkLogin;
}
set
{
if (BusinessObject == null)
BusinessObject = new User();
BusinessObject.NetworkLogin = value;
}
}
And my View page now says this instead.
<%: Html.TextBoxFor(model => model.NetworkLogin)%>
Can someone tell me what the proper naming convention is for default model binding so that I don't have to employ the above workaround?
Thank you!
Indicate the prefix so that the model binder knows that the BusinessObject.NetworkLogin query string parameter actually refers to networkLogin which is what you use as action argument
public ActionResult UserIndex(
[Bind(Prefix = "BusinessObject")] string networkLogin
)
{
...
}
or reuse your view model:
public ActionResult UserIndex(UserViewModel model)
{
// TODO: use model.BusinessObject.NetworkLogin
// which is gonna be correctly bound here
...
}
As far as your workaround is concerned, once you put one of my two suggestions into action your view model property should really look like this:
public string NetworkLogin { get; set; }

asp.net mvc custom attributes

I am trying to create a custom attribute in mvc to use it's parameters in a view as breadCrumb.
well, this is the code of the attribute
[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class BreadCrumbAttribute : Attribute {
public BreadCrumbAttribute(string title, string parent, string url) {
this._title = title;
this._parent = parent;
this._url = url;
}
#region named parameters properties
private string _title;
public string Title {
get { return _title; }
}
private string _url;
public string Url {
get { return _url; }
}
private string _parent;
public string Parent {
get { return _parent; }
}
#endregion
#region positional parameters properties
public string Comments { get; set; }
#endregion
}
this is the call of the attribute
[BreadCrumbAttribute("tile", "parent name", "url")]
public ActionResult Index() {
//code goes here
}
this is a way of how I'd like to get the values. (this is a partial view)
System.Reflection.MemberInfo inf = typeof(ProductsController);
object[] attributes;
attributes = inf.GetCustomAttributes(typeof(BreadCrumbAttribute), false);
foreach (Object attribute in attributes) {
var bca = (BreadCrumbAttribute)attribute;
Response.Write(string.Format("{0}><a href={1}>{2}</a>", bca.Parent, bca.Url, bca.Title));
}
Unfortunately, the attribute didn't get call with the way I implement it. Although, If I add the attribute in Class instead of an Action method it worked.
How could I make it work?
Thanks
The problem is that you are using reflection to get the attributes for the class, so naturally it does not include attributes defined on the action method.
To get those, you should define an ActionFilterAttribute, and in the OnActionExecuting or OnActionExecuted method, you can use filterContext.ActionDescriptor.GetCustomAttributes() method (MSDN description here).
Note that with this solution, you will likely have two different types of attributes: The first one is the one you wrote, to define the breadcrumbs. The second is the one that looks at the attributes on the executing action and builds up the breadcrumb (and presumably adds it to the ViewModel or sticks it in HttpContext.Items or something).

Bind CheckBoxFor to bool?

How to bind nullable bool to checkbox in MVC 2. I try with this code in view:
<%: Html.CheckBoxFor(model => model.Communication.Before)%>
But show me compilation error.
Thanks in advance.
I know about this issue. You can try to use this workaround:
Create new property called Before in yours ViewModel:
public class YoursViewModel
{
public Communication Communication { get; set; }
public bool Before
{
get
{
bool result;
if (this.Communication.Before.HasValue)
{
result = (bool)this.Communication.Before.Value;
}
else
{
result = false;
}
return result;
}
set
{
this.Communication.Before = value;
}
}
}
Also you have to be careful for Communication property this have to be instanced before use. For example when you initialize ViewModel in controller you also have to initialize this property.
ControllerAction()
{
YoursViewModel model = ViewModelFactory.CreateViewModel<YoursViewModel >("");
model.Communication = new Communication ();
return View(model);
}
Thanks
Ivan Baev
A checkbox can have two states: ckecked/uncheked, true/false, 1/0. So trying to bind a checkbox to a property that could potentially have three states doesn't really fit the picture. I would recommend you adapting your view model so that it uses a non nullable boolean property. If in your domain model you have a nullable boolean which you cannot change you could do this in the mapping layer between your domain model and view model.
One way to bind Checkbox in MVC View
With EF database first, boolean (bit) field in the database produces a nullable bool? Property in the generated class. For demo I have a table named Dude with the fields
Id uniqueidentifier
Name varchar(50)
IsAwesome bit
The following class is generated by EF:
namespace NullableEfDemo
{
using System;
public partial class Dude
{
public System.Guid Id { get; set; }
public string Name { get; set; }
public Nullable<bool> IsAwesome { get; set; }
}
}
To be able to bind IsAwesome to checkbox I simply extend the class Dude. This is to avoid editing the generated class, if I need to refresh it. So I added a code file DudePartial.cs to my project (the name is irrelevant). Don’t forget to declare or using the project namespace:
namespace NullableEfDemo
{
partial class Dude
{
public bool Awesome
{
get { return IsAwesome ?? false; }
set { IsAwesome = value; }
}
}
}
This declares a new property Awesome of type bool that can be bound to the checkbox in the Edit view
#Html.CheckBoxFor(model => model.Awesome, new { #class = "control-label" })
In the HttpPost I’m binding the models Awesome property instead of IsAwesome.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "Id,Name,Awesome")] Dude dude)
{…

Resources