I am trying to access the DataTypeName from the ModelMetadata in a custom DisplayTemplate, but it is always null. My goal is to make a decimal.cshtml template that looks for the DataTypeName and if it's equal to Currency format it as currency. Otherwise display the decimal like normal.
Here's an example of what I have now:
The Model:
public class MyModel
{
[DataType(DataType.Currency)]
public decimal foo { get; set; }
}
In a view that is strongly typed to MyModel I have a call like this: #Html.DisplayFor(m => m.foo)
The Display template (~/Views/Shared/DisplayTemplates/decimal.cshtml)
#model decimal?
#{
var type = ViewData.ModelMetadata.DataTypeName; // This is always null
}
I know that it's using my DisplayTemplate like it's supposed to, but I would expect the DataTypeName to not be null in this case since I have the DataType attribute on the model. Is there any way to access the DataType in a display template like this?
Strange, it should work. I am unable to reproduce. Here's what I tried in a new application:
Model:
public class MyModel
{
[DataType(DataType.Currency)]
public decimal foo { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new MyModel
{
foo = 12.5m
});
}
}
View (~/Views/Home/Index.cshtml):
#model MyModel
#Html.DisplayFor(x => x.foo)
Display template (~/Views/Home/DisplayTemplates/decimal.cshtml):
#model decimal?
#ViewData.ModelMetadata.DataTypeName
When I run the application it prints Currency as expected.
Related
I have this error on read my view on #foreach.
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object. Model is null.
this is my userprofile model.
public class UsersContext : DbContext
{
public UsersContext()
: base("DefaultConnection")
{
}
public DbSet<UserProfile> UserProfiles { get; set; }
public DbSet<Secretarias> Secretarias { get; set; }
public DbSet<Secretarias_Direcciones> Secretarias_Direcciones { get; set; }
}
[Table("UserProfile")]
public class UserProfile
{
[Key]
public int UserId { get; set; }
[Required]
public string Nombre { get; set; }
[Required]
public int SecretariaId { get; set; }
[Required]
public int DireccionId { get; set; }
[Required]
public string UserName { get; set; }
}
this is my control
public ActionResult ListaUsuarios()
{
UsersContext db = new UsersContext();
var model = from usrs in db.UserProfiles
select new { usrs.UserId, usrs.Nombre, usrs.UserName, usrs.SecretariaId, usrs.DireccionId };
ViewBag.Model = model.ToList();
return View();
}
this is my view
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.UserId)
</td>
<td>
#Html.DisplayFor(modelItem => item.Nombre)
</td>
<td>
#Html.DisplayFor(modelItem => item.UserName)
</td>
<td>
#Html.DisplayFor(modelItem => item.SecretariaId)
</td>
<td>
#Html.DisplayFor(modelItem => item.DireccionId)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.UserId }) |
#Html.ActionLink("Details", "Details", new { id=item.UserId}) |
#Html.ActionLink("Delete", "Delete", new { id=item.UserId })
</td>
</tr>
}
Here are the Exception Details:
System.NullReferenceException: Object reference not set to an instance
of an object. Model is null.
You're getting the NullReferenceException because you haven't passed a strongly-typed model to your view. So in your foreach loop, Model is null. The normal way of doing that is like so:
return View(model);
The real problem is that you're mixing different concepts here. You're trying to access the properties of a model in a strongly-typed way, and yet you're defining a ViewBag.Model property, which is dynamic and has no relation whatsoever to the Model property within a view.
If you want to access your model in a strongly-typed way, and that's certainly the way I'd suggest you do it, you need to make several changes. I'd suggest you first define a view model to represent the data for a UserProfile:
public class UserProfileViewModel
{
public int UserId { get; set; }
public string Nombre { get; set; }
public int SecretariaId { get; set; }
public int DireccionId { get; set; }
public string UserName { get; set; }
}
Now, change your action to create a list of these:
public ActionResult ListaUsuarios()
{
using (UsersContext db = new UsersContext())
{
var model = from usrs in db.UserProfiles
select new UserProfileViewModel
{
UserId = usrs.UserId,
Nombre = usrs.Nombre,
UserName = usrs.UserName,
SecretariaId = usrs.SecretariaId,
DireccionId = usrs.DireccionId
};
return View(model.ToList());
}
}
Notice that I've made several changes here. Firstly, I've added the using statement to ensure UsersContext is disposed properly. Next, I've changed the query to instantiate UserProfileViewModels, instead of anonymous types. Lastly, I'm directly passing model as a parameter to View(), making sure to call ToList() on it, in order to evaluate the query before UsersContext gets disposed.
Finally, you just need to make one change to your view:
#model List<UserProfileViewModel>
#foreach (var item in Model)
{
// rest of code goes here
}
The #model directive specifies the exact model type that the view should expect to receive, thus making it strongly-typed.
Obviously - check the data types of each of the Ids that you are using to perform your joins in your database. Also ensure the database data types were mapped to the correct .Net data types.
If the data types of your ID fields appear to be ok, I would suggest narrowing down which join is causing the error. Modify you LINQ expression to have a single join and ensure there is no error. Add the other joins in one at a time until the error appears.
For your model, please check that it is being populated.
Depending on whatever is happening, it could be that your model is just null
and possibly its type might be something that it is not expected.
-- in the controller a foreach loop on the model context reference will let you see the values
I have a model:
public class TestModel {
[Display(Name = "Date")]
public DateTime Date { get; set; }
}
with Html.LabelFor helper method in Test.cshtml page
#Html.LabelFor(m => m.Date )
and use this page with 2 MVC action methods: Create and Update.
Example:
public virtual ViewResult Create() {
return View("Test");
}
public virtual ViewResult Update() {
return View("Test");
}
and I want to display #Html.LabelFor(m => m.Date ) with Create page: "Date" and Update page: "Update Date" . I know if the normal way of MVC3 can't do this. I hope your ideal can edit Html.LabelFor hepler method or anything way to bind data to Html.LabelFor in action methods on the controller
Adding a hiddenFor field will bind the data to your Model.
#Html.HiddenFor(m=>m.Date);
For override, please just look this answer
https://stackoverflow.com/a/5196392/5557777
you can override editorfor like this How can I override the #Html.LabelFor template? but I think you can do it more easily with ViewBag:
public virtual ViewResult Create() {
ViewBag.Title = "Create";
return View("Test");
}
public virtual ViewResult Update() {
ViewBag.Title = "Update";
return View("Test");
}
in view:
#string.format("{0} Date" , ViewBag.Title )
I try to initialize the DataView.Model in a partial view. The Page works fine but when I return to the controller the model is empty.
some help(solution or an explanation why it is not right).
thanks!!
code:
In my Partial View:
ViewData.Model = new DiamondPrint();
ViewData.Model.Diamond = m_db.DiamondInfoes.Where(di => di.Id == id).SingleOrDefault();
In my Controller:
public ActionResult Preview(DiamondPrint d)//the properties in d = null
{
return View(d);
}
Here is a great article on Model Binding. Model Binding Make sure you are setting the name property in your html input fields.
Looking at the code you have included it seems that you are initialising the ViewData.Model in the partial view but in the controller action you are expecting the default model binder to recreate your model. For the model binder to recreate your model you will need to have created a strongly typed view.
For example:
Controller:
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(IndexModel model)
{
return View();
}
Model:
public class IndexModel
{
public string MyValue { get; set; }
}
View:
Note the #model definition at the top (ignore namespace)
#model MvcApplication14.Models.IndexModel
#using (Html.BeginForm())
{
#Html.Partial("_IndexPartial", Model)
<input type="submit" value="click"/>
}
Partial View:
#model MvcApplication14.Models.IndexModel
#Html.EditorFor(m => m.MyValue)
I'm currently experiencing a weird issue with ASP.NET MVC 3 ListBox validation, as stated in the title. Basically, I have a List in my viewmodel, which I bind to a ListBox with multiple selection enabled.
The List is given an attribute [Required]. When I submit the form with single value selected, it passes validation with no hiccups. However, with more than one, validation would fail.
Any thoughts?
Weird, I am unable to reproduce your issue.
Model:
public class MyViewModel
{
[Required(ErrorMessage = "Please select at least one item")]
public string[] SelectedItems { get; set; }
public IEnumerable<SelectListItem> Items
{
get
{
return Enumerable.Range(1, 5).Select(x => new SelectListItem
{
Value = x.ToString(),
Text = "item " + x
});
}
}
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new MyViewModel());
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
}
View:
#model MyViewModel
#using (Html.BeginForm())
{
#Html.ListBoxFor(x => x.SelectedItems, Model.Items)
#Html.ValidationMessageFor(x => x.SelectedItems)
<button type="submit">OK</button>
}
If you don't select any item in the list the validation error message is shown as expected. If you select one or more items the validation passes and no error message is displayed.
Hopefully this will have a simple answer.
Using MVC3, I am passing a simple list of POCO objects as a model to my view:
public partial class PeopleAddress
{
public int Id { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
public int PersonId { get; set; }
public virtual Person Person { get; set; }
}
I use the PeopleId as the FK property to the Person entity, and the Person navigational property to navigate to the object. Here is my controller for the view:
public ViewResult Index()
{
var peopleaddresses = db.PeopleAddresses.Include("Person");
return View(peopleaddresses.ToList());
}
Pretty trivial. I add the columns to the grid and normal edit modes, etc in the view, but for the PersonId property.
QUESTION ABOUT COLUMNS: How can I get the select (normal) mode to display the model.Person.Name, but keep the edit mode on editing model.PersonId? For model binding purposes, I need the HTTP post to send PersonId.
Help!
Simple
If all you need is the Person.Id when you hit edit (you're editing outside the grid or something), then it IS that simple. Your column would be:
columns.Bound(e => e.Person).Title("Person").ClientTemplate("<#= Person ? Person.Name : '' #>");
And you can get the Id for the person:
onEdit(e) {
var personId = e.dataItem['Person'].Id;
}
Full
However, if you're trying to edit in the grid using a combobox, your column should look like:
columns.Bound(e => e.Person).Title("Person").ClientTemplate("<#= Person ? Person.Name : '' #>").EditorTemplateName("PersonSelector");
your editor template:
#(Html.Telerik().ComboBox()
.Name("YourNameGoesHere")
.DataBinding(binding => binding.Ajax().Select("SelectPeopleForComboBox","Shared")))
Your client script:
onEdit(e){
$comboBox = $(e.cell).find('#Person');
if($comboBox.length > 0) {
var comboBox = $ddl.data('tComboBox');
comboBox.fill(function(){
if (e.dataItem['Person'] != null){
ddl.value(e.dataItem['Person'].Id)
}
});
}
}