I'm new towards MVC3 Razor. Currently, I'm facing this error "Object reference not set to an instance of an object.", which I have no idea what is this about.
In Model
public List<SelectListItem> CatList { get; set; }
[Display(Name = "Category")]
public string CatID { get; set; }
In Controller
public ActionResult DisplayCategory()
{
var model = new CatModel();
model.CatList = GetCat();
return View(model);
}
private List<SelectListItem> GetCat()
{
List<SelectListItem> itemList = new List<SelectListItem>();
itemList.Add(new SelectListItem { Text = "1", Value = "1" });
itemList.Add(new SelectListItem { Text = "2", Value = "2" });
return itemList;
}
In CSHTML
#using (Html.BeginForm())
{
<table>
<tr>
<td>#Html.LabelFor(c => c.CatID)</td>
<td>#Html.DropDownListFor(c => c.CatID, Model.CatList)</td>
</tr>
</table>
}
Thanks for any help.
I suspect that you have a POST action in which you forgot to reassign the CatList property of your view model so you are getting the NRE when you submit the form, not when the form is initially rendered:
public ActionResult DisplayCategory()
{
var model = new CatModel();
model.CatList = GetCat();
return View(model);
}
[HttpPost]
public ActionResult Index(CatModel model)
{
// some processing ...
// since we return the same view we need to populate the CatList property
// the same way we did in the GET action
model.CatList = GetCat();
return View(model);
}
private List<SelectListItem> GetCat()
{
List<SelectListItem> itemList = new List<SelectListItem>();
itemList.Add(new SelectListItem { Text = "1", Value = "1" });
itemList.Add(new SelectListItem { Text = "2", Value = "2" });
return itemList;
}
I have no idea what am i doing wrong.
Well i have this form, it's part of complex view.
#{
var filtersAjaxOptions = new AjaxOptions
{
HttpMethod = "POST",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "clientList-body",
OnBegin = "clientList.filterRequestStart()",
OnComplete = "clientList.filterRequestComplete()",
OnSuccess = "clientList.filterRequestSuccess()"
};
}
<span class="clientFilters-filterValue inlineBlock">
#using (Ajax.BeginForm(
"Index",
"ClientList",
new {
ProductId = Model.ClientListViewModel.Filters.ProductId,
ClientFilter = Model.ClientListViewModel.Filters.ClientFilter,
BillFilter = Model.ClientListViewModel.Filters.BillFilter,
DateSortType = Model.ClientListViewModel.Filters.DateSortType,
SortDirection = Model.ClientListViewModel.Filters.SortDirection
},
filtersAjaxOptions,
new
{
id = "clientListDateFilter-form"
}
))
{
#Html.TextBoxFor(
m => m.ClientListViewModel.Filters.BeginDateRange,
new
{
#class = "dp-input textInput inlineBlock",
id = "dp-billDateFilterStart",
}
)
#Html.TextBoxFor(
m => m.ClientListViewModel.Filters.EndDateRange,
new
{
#class = "dp-input textInput inlineBlock",
id = "dp-billDateFilterEnd",
}
)
}
</span>
Here's the filters model
public class FilterModel
{
public FilterModel()
{
ClientFilter = ClientsEnum.All;
BillFilter = ClientBillsEnum.All;
}
public string ProductId { get; set; }
public ClientsEnum ClientFilter { get; set; }
public ClientBillsEnum BillFilter { get; set; }
public DateTime? BeginDateRange { get; set; }
public DateTime? EndDateRange { get; set; }
public DateSortType? DateSortType { get; set; }
public SortDirection? SortDirection { get; set; }
}
This part is ClientListController method Index:
public ActionResult Index(FilterModel filters)
{
var clientListViewModel = GetClientListViewModel(filters, 1, 1, PageSize);
if (ControllerContext.HttpContext.Request.IsAjaxRequest())
return PartialView("Partial/ClientListBody", clientListViewModel);
return View(clientListViewModel);
}
Whenever i submit the form above, it turns to me that fields "BeginDateRange" and "EndDateRange" are null and other fields are set properly. Although, when i insert Request.Form in Watch, i can see the whole data.
UPDATE 1
So i set the <globalisation> in Web.config as this:
<globalisation responseHeaderEncoding="utf-8" culture="en-US">
and yet it doesn't work. Very same result as before.
UPDATE 2
Also when i tried to put all the routevalues data into #Html.HiddenFor, controller saw only nulls. And again, Request.Form is filled prprly.
So the question is: how can i bind form data to incoming model?
TY
The default model binder uses the current culture datetime format when binding datetimes. This means that you have to enter the date into the proper format in your textboxes. On the other hand if you need a fixed format you could use a fixed culture in your web.config (<globalization> element) or write a custom model binder: https://stackoverflow.com/a/7836093/29407
UPDATE:
You need to specify the correct binding prefix because your input fields are named like ClientListViewModel.Filters.BeginDateRange but your controller action takes a FilterModel as parameter instead of the root view model:
public ActionResult Index([Bind(Prefix = "ClientListViewModel.Filters")] FilterModel filters)
{
...
}
But now this will break the other values, so you need to adjust your view as well:
#using (Ajax.BeginForm(
"Index",
"ClientList",
null,
filtersAjaxOptions,
new
{
id = "clientListDateFilter-form"
}
))
{
#Html.HiddenFor(x => x.ClientListViewModel.Filters.ProductId)
#Html.HiddenFor(x => x.ClientListViewModel.Filters.ClientFilter)
#Html.HiddenFor(x => x.ClientListViewModel.Filters.BillFilter)
#Html.HiddenFor(x => x.ClientListViewModel.Filters.DateSortType)
#Html.HiddenFor(x => x.ClientListViewModel.Filters.SortDirection)
#Html.TextBoxFor(
m => m.ClientListViewModel.Filters.BeginDateRange,
new
{
#class = "dp-input textInput inlineBlock",
id = "dp-billDateFilterStart",
}
)
#Html.TextBoxFor(
m => m.ClientListViewModel.Filters.EndDateRange,
new
{
#class = "dp-input textInput inlineBlock",
id = "dp-billDateFilterEnd",
}
)
}
or if you want to send them as part of the form url instead if using hidden fields:
#using (Ajax.BeginForm(
"Index",
"ClientList",
new RouteValueDictionary
{
{ "ClientListViewModel.Filters.ProductId", Model.ClientListViewModel.Filters.ProductId },
{ "ClientListViewModel.Filters.ClientFilter", Model.ClientListViewModel.Filters.ClientFilter },
{ "ClientListViewModel.Filters.BillFilter", Model.ClientListViewModel.Filters.BillFilter },
{ "ClientListViewModel.Filters.DateSortType", Model.ClientListViewModel.Filters.DateSortType },
{ "ClientListViewModel.Filters.SortDirection", Model.ClientListViewModel.Filters.SortDirection },
},
filtersAjaxOptions,
new RouteValueDictionary
{
{ "id", "clientListDateFilter-form" }
}
))
{
#Html.TextBoxFor(
m => m.ClientListViewModel.Filters.BeginDateRange,
new
{
#class = "dp-input textInput inlineBlock",
id = "dp-billDateFilterStart",
}
)
#Html.TextBoxFor(
m => m.ClientListViewModel.Filters.EndDateRange,
new
{
#class = "dp-input textInput inlineBlock",
id = "dp-billDateFilterEnd",
}
)
}
Try this:
public ActionResult Index(FilterModel filters, FormCollection collection)
{
UpdateModel(filters, "ClientListViewModel");
var clientListViewModel = GetClientListViewModel(filters, 1, 1, PageSize);
if (ControllerContext.HttpContext.Request.IsAjaxRequest())
return PartialView("Partial/ClientListBody", clientListViewModel);
return View(clientListViewModel);
}
And in view:
#Html.TextBoxFor(
m => m.ClientListViewModel.FilterModel.EndDateRange,
new
{
#class = "dp-input textInput inlineBlock",
id = "dp-billDateFilterEnd",
}
)
You have strange naming. Also it would be better to use hidden fields then passing values through routevalues.
I looked at similar posts but nothing working for my case.
I have a form which loads fine and I see the categories dropdown with all categories in it.
The problem is when I try to post the form.
I get this error:
The ViewData item that has the key 'Category' is of type 'System.String' but must be of type 'IEnumerable'.
#Html.DropDownList("Category", Model.Categories) <-- red color
Here is my view:
#using (Html.BeginForm("Save", "Album", FormMethod.Post, new { id = "frmNewAlbum" }))
{
#Html.DropDownList("Category", Model.Categories)
}
Here is my model:
public class AlbumModel
{
public string Title { get; set; }
public string Category { get; set; }
public List<SelectListItem> Categories { get; set; } <-- holds categories
}
This is the controller actions to view the page:
[HttpGet]
public ActionResult Save()
{
var model = new AlbumModel();
var categories = new List<SelectListItem>() { new SelectListItem() { Text = "-- pick --" } };
categories.AddRange(svc.GetAll().Select(x => new SelectListItem() { Text = x.Name, Value = x.Name }));
model.Categories = categories;
return View(model);
}
Action that receives the post:
[HttpPost]
public ActionResult Save(AlbumModel model)
{
var album = new AlbumDoc()
{
Category = model.Category,
Title = model.Title,
};
svc.SaveAlbum(album);
return View(model);
}
In your POST action you seem to be redisplaying the same view but you are not populating the Categories property on your view model which will contain the dropdown list values. And by the way I would recommend you using strongly typed helper. So:
public class AlbumController: Controller
{
[HttpGet]
public ActionResult Save()
{
var model = new AlbumModel();
model.Categories = GetCategories();
return View(model);
}
[HttpPost]
public ActionResult Save(AlbumModel model)
{
var album = new AlbumDoc()
{
Category = model.Category,
Title = model.Title,
};
svc.SaveAlbum(album);
model.Categories = GetCategories();
return View(model);
}
private IList<SelectListItem> GetCategories()
{
return svc
.GetAll()
.ToList()
.Select(x => new SelectListItem
{
Text = x.Name,
Value = x.Name
});
}
}
and in your view:
#model AlbumModel
...
#using (Html.BeginForm("Save", "Album", FormMethod.Post, new { id = "frmNewAlbum" }))
{
#Html.DropDownListFor(
x => x.Category,
Model.Categories,
-- pick --
)
}
I have a grid using AJAX DATABINDING.
When I issue a POPUP EDITING command with TEMPLATENAME SPECIFIED my NESTED EDITOR TEMPLATES are not populating.
My Models
namespace eGate.BackOffice.WebClient.Model
{
public class TemplateTesterModel
{
public int TemplateModelId { get; set; }
public string TemplateModelName { get; set; }
public List<UserRole> UserRoles { get; set; }
}
}
{
public class TemplateTesterModels : List<TemplateTesterModel>
{
}
}
My View
#model eGate.BackOffice.WebClient.Model.TemplateTesterModels
#Html.EditorFor(m=>m)
#( Html.Telerik().Grid<eGate.BackOffice.WebClient.Model.TemplateTesterModel>()
.Name("Grid")
.DataKeys(keys => { keys.Add(m=>m.TemplateModelId); })
.Columns(columns =>
{
columns.Bound(o => o.TemplateModelId);
columns.Bound(o => o.TemplateModelName).Width(200);
columns.Bound(o => o.UserRoles).ClientTemplate(
"<# for (var i = 0; i < UserRoles.length; i++) {" +
"#> <#= UserRoles[i].RoleName #> <#" +
"} #>")
;
columns.Command(commands =>
{
commands.Edit().ButtonType(GridButtonType.Text);
}).Width(180).Title("Commands");
})
.DataBinding(dataBinding => dataBinding.Ajax()
.Select("_SelectAjaxEditing", "TemplateTester")
.Insert("_InsertAjaxEditing", "Grid")
.Update("_SaveAjaxEditing", "TemplateTester")
.Delete("_DeleteAjaxEditing", "TemplateTester")
)
.Editable(editable => editable.Mode(GridEditMode.PopUp).TemplateName("TemplateTesterModel"))
)
My Controller
namespace eGate.BackOffice.WebClient.Controllers
{
public class TemplateTesterController : Controller
{
public ActionResult Index()
{
return View(GetTemplateTesters());
//return View(new GridModel(GetTemplateTesters()));
}
private TemplateTesterModels GetTemplateTesters() {
TemplateTesterModels returnme = new TemplateTesterModels();
returnme.Add(new TemplateTesterModel());
returnme[0].TemplateModelId = 0;
returnme[0].TemplateModelName = "Template Tester 0";
returnme[0].UserRoles = new List<UserRole>();
returnme[0].UserRoles.Add(new UserRole() { RoleName = "Role1", IsChecked = true, Description = "Role for 1" });
returnme[0].UserRoles.Add(new UserRole() { RoleName = "Role2", IsChecked = false, Description = "Role for 2" });
returnme[0].UserRoles.Add(new UserRole() { RoleName = "Role3", IsChecked = false, Description = "Role for 3" });
return returnme;
}
[GridAction]
public ActionResult _SelectAjaxEditing()
{
return View(new GridModel(GetTemplateTesters()));
}
[AcceptVerbs(HttpVerbs.Post)]
[GridAction]
public ActionResult _SaveAjaxEditing(int id)
{
return View(new GridModel(GetTemplateTesters()));
}
[GridAction]
public ActionResult _InsertAjaxEditing(){
return View(new GridModel(GetTemplateTesters()));
}
[AcceptVerbs(HttpVerbs.Post)]
[GridAction]
public ActionResult _DeleteAjaxEditing(int id)
{
return View(new GridModel(GetTemplateTesters()));
}
}
}
My EditorTemplates
Shared/EditorTemplates/TemplateTesterModel.cshtml
#model eGate.BackOffice.WebClient.Model.TemplateTesterModel
<div>TemplateTesterModel Editor</div>
<div>#Html.EditorFor(m=>m.TemplateModelId)</div>
<div>#Html.EditorFor(m=>m.TemplateModelName)</div>
<div>Roles</div>
<div>#Html.EditorFor(m=>m.UserRoles)</div>
Shared/EditorTemplates/UserRole.cshtml
#model eGate.BackOffice.WebClient.Model.UserRole
<div>
I can has user role?
#Html.CheckBoxFor(m=>m.IsChecked)
</div>
This renders out as such:
As you can see the #Html.EditFor statements that precede the grid filter down through to the userrole EditorTemplate as expected. Additionally we can see that role data is in the grid because it is showing up in the role column.
But click the edit window and this is the result:
As you can see the UserRoles template is not populating with the roles on the UserRoles property of the TemplateTesterModel we're editing.
Am I missing something? Why is the .UserRoles property not populating in the telerik grid pop-up window?
This could be a "by design" decision of ASP.NET MVC. It does not automatically render display and editor templates for nested complex objects. I even have a blog post discussing this.
Long story short you need to create a custom editor template for the parent model.
I have the following error when I click save:
the ViewData item that has the key 'SelectedCategoryId' is of type 'System.Int32' but must be of type 'IEnumerable'?
my controller:
public ActionResult IndexTwee()
{
var listCategories = new List<SelectListItem>();
listCategories.Add(new SelectListItem() {Text="foo",Value="1" });
listCategories.Add(new SelectListItem() { Text = "bar", Value = "2" });
MyViewModelTwee model = new MyViewModelTwee() { };
model.Categories = listCategories;
model.SelectedCategoryId = 2;
return View(model);
}
[HttpPost]
public ActionResult IndexTwee(MyViewModelTwee Model)
{
return View(Model);
}
my model:
public class MyViewModelTwee
{
public int SelectedCategoryId { get; set; }
public IEnumerable<SelectListItem> Categories { get; set; }
}
my view:
#model mvc3DropDown.Models.MyViewModelTwee
#using (Html.BeginForm())
{
#Html.DropDownListFor(
x => x.SelectedCategoryId,
Model.Categories
)
<button>Save</button>
}
Don't forget to rebind the list in your POST action:
[HttpPost]
public ActionResult Index(MyViewModelTwee Model)
{
var listCategories = new List<SelectListItem>();
listCategories.Add(new SelectListItem() { Text = "foo", Value = "1" });
listCategories.Add(new SelectListItem() { Text = "bar", Value = "2" });
Model.Categories = listCategories;
return View(Model);
}
Remember that only the selected value is sent when you submit the html <form>. The other values are lost so you need to refetch them from wherever you fetched them in the GET action. Of course you will externalize this code into a repository layer so that your code now looks like this:
public ActionResult IndexTwee()
{
var model = new MyViewModelTwee
{
SelectedCategoryId = 2,
Categories = _repository.GetCategories()
};
return View(model);
}
[HttpPost]
public ActionResult IndexTwee(MyViewModelTwee Model)
{
Model.Categories = _repository.GetCategories();
return View(Model);
}