Data binding MVC3 - ajax

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.

Related

Setting the default value of multiple dropdown lists on page load

I have a model,
public class Customer
{
public string Name { get; set;}
public string CountryCode { get; set;}
}
In the controller
var model = new List<Customer>
{
new Customer { Name = "foo", CountryCode = "US"},
new Customer { Name = "bar", CountryCode = "UK",
};
return PartialView("_Edit", model);
An extension method for displaying all countries:-
public class CountryList
{
public static IEnumerable<SelectListItem> CountrySelectList
{
get
{
var list = new List<SelectListItem>()
{
new SelectListItem { Value = "US", Text="US" },
new SelectListItem { Value = "UK", Text="UK" },
};
return list;
}
}
}
In the PartialView
#model List<Customer>
#Html.DropDownListFor(model => model[i].CountryCode, CountryList.CountrySelectList, "Select Country Type")
But the drop down doesn't select each customer's country code? Any thoughts?
PS: It is using model[i] => which is of type Customer, for simplicity i had removed the forloop before rendering the html tags.
#using(Html.BeginForm())
{
for(int i = 0; i < Model.Count(); i++)
{
#Html.TextBoxFor(model => model[i].Name)
#Html.DropDownListFor..........
}
}
Because your CoutryList helper does returns a list of SelectListItems that all have Selected property set to False (which is default).
I would rewrite your helper method as follows:
public static IEnumerable<SelectListItem> CountrySelectList(string selectedCountryCode)
{
get
{
var list = new List<SelectListItem>()
{
new SelectListItem { Value = "US", Text="US" },
new SelectListItem { Value = "UK", Text="UK" },
};
var selectedListItem = list.FirstOrDefault(t=>t.Value== selectedCountryCode);
if(selectedListItem!=null)
selectedListItem.Selected=true;
return list;
}
}
In view:
#Html.DropDownListFor(model => model[i].Customer, CountryList.CountrySelectList(model[i].Customer.CountryCode), "Select Country Type")

MVC 3 Model binding and No parameterless constructor defined for this object

I created a view that was working wonderfully until I added some JQuery to support cascading drop downs. I believe in doing that, I broke the binding between the view and the model. I'm getting the error "No parameterless constructor defined for this object." when the form is submitted. The obvious solution would be to add a parameterless constructor, but I'm assuming that the postmodel will be null? Code Snippets below.
Thanks in Advance for your help.
View:
<script type="text/javascript">
$(document).ready(function () {
$("#ddlCategories").change(function () {
var iCategoryId = $(this).val();
$.getJSON(
"#Url.Content("~/Remote/SubCategoriesByCateogry")",
{ id: iCategoryId },
function (data) {
var select = ResetAndReturnSubCategoryDDL();
$.each(data, function (index, itemData) {
select.append($('<option/>', { value: itemData.Value, text: itemData.Text }));
});
});
});
function ResetAndReturnSubCategoryDDL() {
var select = $('#ddlSubCategory');
select.empty();
select.append($('<option/>', { value: '', text: "--Select SubCategory--" }));
return select;
}
});
...
<div class="editor-field">
#Html.DropDownList("iCategoryID", Model.Categories,"--Select Category--", new Dictionary<string,object>{ {"class","dropdowns"},{"id","ddlCategories"}})
#Html.ValidationMessage("iCategoryID")
</div>
<div class="editor-label">
#Html.LabelFor(model => model.SubCategories, "SubCategory")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.SubCategories, new SelectList(Enumerable.Empty<SelectListItem>(), "iSubCategoryID", "SubCategory",Model.SubCategories), "--Select SubCategory--", new { id = "ddlSubCategory" })
#Html.ValidationMessage("iSubCategoryID")
</div>
Controller:
[HttpPost]
public ActionResult Create(VendorCreateModel postModel)
{
VendorCreateEditPostValidator createValidator = new VendorCreateEditPostValidator(
postModel.iCategoryID,
postModel.iSubCategoryID,
postModel.AppliedPrograms,
m_unitOfWork.ProgramRepository,
new ModelStateValidationWrapper(ModelState));
if (ModelState.IsValid)
{
int categoryId = int.Parse(postModel.iCategoryID);
int subcategoryId = int.Parse(postModel.iSubCategoryID);
var programIds = postModel.AppliedPrograms.Select(ap => int.Parse(ap));
var programs = m_unitOfWork.ProgramRepository.GetPrograms(programIds);
Vendor vendor = postModel.Vendor;
vendor.Category = m_unitOfWork.CategoryRepository.GetCategory(categoryId);
vendor.SubCategory = m_unitOfWork.SubCategoryRepository.GetSubCategory(subcategoryId);
foreach (Program p in programs)
vendor.Programs.Add(p);
m_unitOfWork.VendorRepository.Add(vendor);
m_unitOfWork.SaveChanges();
return RedirectToAction("Index");
}
VendorCreateModel model = new VendorCreateModel(
postModel.Vendor,
postModel.iCategoryID,
postModel.iSubCategoryID,
postModel.AppliedPrograms,
User.Identity.Name,
m_unitOfWork.CategoryRepository,
m_unitOfWork.SubCategoryRepository,
m_unitOfWork.PermissionRepository);
return View(model);
}
RemoteController:
[AcceptVerbs(HttpVerbs.Get)]
public JsonResult SubCategoriesByCateogry(int id)
{
System.Diagnostics.Debug.WriteLine(id);
var SubCategories = db.SubCategories
.Where(v => v.iCategoryID == id)
.OrderBy(v => v.sDesc)
.ToList();
var modelData = SubCategories.Select(v => new SelectListItem()
{
Text = v.sDesc,
Value = v.iSubCategoryID.ToString()
});
return Json(modelData, JsonRequestBehavior.AllowGet);
}
VendorCreateModel:
public class VendorCreateModel
{
public VendorCreateModel()
{
}
public VendorCreateModel(
Vendor vendor,
string categoryId,
string subcategoryId,
IEnumerable<string> appliedPrograms,
string username,
ICategoryRepository categoryRepository,
ISubCategoryRepository subcategoryRepository,
IPermissionRepository permissionRepository)
{
UserHasProgramsValidator programValidator = new UserHasProgramsValidator(username, permissionRepository);
var availablePrograms = programValidator.AvailablePrograms;
HashSet<Category> applicableCategories = new HashSet<Category>();
foreach (var p in availablePrograms)
foreach (var c in categoryRepository.GetCategoriesByProgram(p.iProgramID))
applicableCategories.Add(c);
this.Vendor = vendor;
this.AppliedPrograms = appliedPrograms;
this.Categories = new SelectList(applicableCategories.OrderBy(x => x.sDesc).ToList(), "iCategoryID", "sDesc");
this.SubCategories = new SelectList(subcategoryRepository.GetAllSubCategories().OrderBy(x => x.sDesc).ToList(), "iSubCategoryID", "sDesc");
if (!string.IsNullOrEmpty(categoryId))
{
int temp;
if (!int.TryParse(categoryId, out temp))
throw new ApplicationException("Invalid Category Identifier.");
}
this.iCategoryID = categoryId;
this.iSubCategoryID = subcategoryId;
this.ProgramItems = availablePrograms
.Select(p => new SelectListItem()
{
Text = p.sDesc,
Value = p.iProgramID.ToString(),
Selected = (AppliedPrograms != null ? AppliedPrograms.Contains(p.iProgramID.ToString()) : false)
});
}
public Vendor Vendor { get; set; }
public SelectList Categories { get; set; }
public SelectList SubCategories { get; set; }
public string iCategoryID { get; set; }
public string iSubCategoryID { get; set; }
public IEnumerable<SelectListItem> ProgramItems { get; set; }
[AtLeastOneElementExists(ErrorMessage = "Please select at least one program.")]
public IEnumerable<string> AppliedPrograms { get; set; }
}
I correct the issue and wanted to share in case someone else was banging their head against their desk like Ihave been. Basically I changed the dropdownlistfor to reflect:
#Html.DropDownListFor(model => model.iSubCategoryID, new SelectList(Enumerable.Empty<SelectListItem>(), "iSubCategoryID", "SubCategory",Model.SubCategories), "--Select SubCategory--", new Dictionary<string,object>{ {"class","dropdowns"},{"id","ddlSubCategory"},{"name","iSubCategoryID"}})
Assuming here the problem is in your VendorCreateModel, you either need to add a parameterless constructor or remove it, and create an instance in your action method and populate it by TryUpdateModel. Or parse the form using FormsCollection (not a fan).
You don't have the code for your viewmodel posted here but the basic assumption is that it will map.

SelectList in MVC3

I have a MVC3 page that has to dropdownList contains Sex(male,female) and Role(admin,Operator), in RegisterViewModel I have done like this :
public List<SelectListItem> SelectedItemForSex { get; set; }
public List<SelectListItem> SelectedItemForRole { get; set; }
public string SearchText { get; set; }
public string SelectedValue { get; set; }
//public string SelectedValueForRole { get; set; }
public static RegisterViewModel Get()
{
var model = new RegisterViewModel { SelectedItemForSex = new List<SelectListItem>() };
model.SelectedItemForSex.Add(new SelectListItem() { Text = "Male", Value = "1" });
model.SelectedItemForSex.Add(new SelectListItem() { Text = "Femle", Value = "2" });
model.SelectedItemForRole.Add(new SelectListItem() {Text = "Administrator", Value = "1"});
model.SelectedItemForRole.Add(new SelectListItem() {Text = "Operator", Value = "2"});
return model;
}
and in get action of Register I have this code :
public ActionResult Create()
{
var model = RegisterViewModel.Get();
return View(model);
}
and also in razor :
<div>
#Html.LabelFor(register => register.Sex)
#Html.DropDownListFor(register => register.SelectedValue, new SelectList(Model.SelectedItemForSex, "Value", "Text"))
</div>
<div>
#Html.LabelFor(register => register.Role)
#Html.DropDownListFor(register => register.SelectedValue, new SelectList(Model.SelectedItemForRole, "Value", "Text"))
</div>
<div>
I know I have Not initialize selectedListItem for Administrator and operator , I wanna to send all of this 4 value via Get() method, How can I do this ??

To populate a dropdown in mvc3

I am new to MVC3
I am finding it difficult to create an dropdown.I have gone through all the other related questions but they all seem to be complex
I jus need to create a dropdown and insert the selected value in database
Here is what i have tried:
//Model class:
public int Id { get; set; }
public SelectList hobbiename { get; set; }
public string filelocation { get; set; }
public string hobbydetail { get; set; }
//Inside Controller
public ActionResult Create()
{
var values = new[]
{
new { Value = "1", Text = "Dancing" },
new { Value = "2", Text = "Painting" },
new { Value = "3", Text = "Singing" },
};
var model = new Hobbies
{
hobbiename = new SelectList(values, "Value", "Text")
};
return View();
}
//Inside view
<div class="editor-label">
#Html.LabelFor(model => model.hobbiename)
</div>
<div class="editor-field">
#Html.DropDownListFor( x => x.hobbiename, Model.hobbiename )
#Html.ValidationMessageFor(model => model.hobbiename)
</div>
I get an error:System.MissingMethodException: No parameterless constructor defined for this object
You are not passing any model to the view in your action. Also you should not use the same property as first and second argument of the DropDownListFor helper. The first argument that you pass as lambda expression corresponds to a scalar property on your view model that will hold the selected value and which will allow you to retrieve this value back when the form is submitted. The second argument is the collection.
So you could adapt a little bit your code:
Model:
public class Hobbies
{
[Required]
public string SelectedHobbyId { get; set; }
public IEnumerable<SelectListItem> AvailableHobbies { get; set; }
... some other properties that are irrelevant to the question
}
Controller:
public class HomeController: Controller
{
public ActionResult Create()
{
// obviously those values might come from a database or something
var values = new[]
{
new { Value = "1", Text = "Dancing" },
new { Value = "2", Text = "Painting" },
new { Value = "3", Text = "Singing" },
};
var model = new Hobbies
{
AvailableHobbies = values.Select(x => new SelectListItem
{
Value = x.Value,
Text = x.Text
});
};
return View(model);
}
[HttpPost]
public ActionResult Create(Hobbies hobbies)
{
// hobbies.SelectedHobbyId will contain the id of the element
// that was selected in the dropdown
...
}
}
View:
#model Hobbies
#using (Html.BeginForm())
{
#Html.LabelFor(x => x.SelectedHobbyId)
#Html.DropDownListFor(x => x.SelectedHobbyId, Model.AvailableHobbies)
#Html.ValidationMessageFor(x => x.SelectedHobbyId)
<button type="submit">Create</button>
}
I would create them as
Model:
public class ViewModel
{
public int Id { get; set; }
public string HobbyName { get; set; }
public IEnumerable<SelectListItem> Hobbies {get;set; }
public string FileLocation { get; set; }
public string HobbyDetail { get; set; }
}
Action
public ActionResult Create()
{
var someDbObjects= new[]
{
new { Id = "1", Text = "Dancing" },
new { Id = "2", Text = "Painting" },
new { Id = "3", Text = "Singing" },
};
var model = new ViewModel
{
Hobbies = someDbObjects.Select(k => new SelectListItem{ Text = k, Value = k.Id })
};
return View(model);
}
View
<div class="editor-label">
#Html.LabelFor(model => model.HobbyName)
</div>
<div class="editor-field">
#Html.DropDownListFor(x => x.HobbyName, Model.Hobbies )
#Html.ValidationMessageFor(model => model.HobbyName)
</div>

Error when trying to post MVC form with Dropdown list in it

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 --
)
}

Resources