I'm trying to get the drop down list to have my item selected when there is an item, but it never does. I've Googled this and tried many different methods, but they all seem to use a ViewModel containing the list instead of using ViewBag, but I would like to stick to the ViewBag if possible.
My controller:
[HttpGet]
public ActionResult Index(int? id)
{
ViewBag.SelectList = new SelectList(rep.GetItemList(), "id", "type");
if (id.HasValue)
{
var model = rep.GetItemByID(id.Value);
if ( model != null )
{
return View(model);
}
}
return View();
}
My View:
<div class="editor-field">
#Html.DropDownListFor(model => model.itemID, (SelectList)ViewBag.SelectList)
#Html.ValidationMessageFor(model => model.itemID)
</div>
This doesn't have my item selected in the DropDownList, and I've also tried having a list in the ViewBag and then constructing the SelectList in the View, which some posts say should solve the problem:
<div class="editor-field">
#Html.DropDownListFor(model => model.itemID, new SelectList(ViewBag.SelectList, "id", "type", Model.itemID))
#Html.ValidationMessageFor(model => model.itemID)
</div>
But none of it seems to work. So I was wondering if there is anyone where that is able to spot what I'm doing wrong?
make sure your itemID property is set in the model you are passing to the view
if (id.HasValue)
{
var model = rep.GetItemByID(id.Value);
model.itemID=id.Value;
return View(model);
}
I would try setting the selected value from the begining since SelectList is immutable.
[HttpGet]
public ActionResult Index(int? id)
{
if (id.HasValue)
{
ViewBag.SelectList = new SelectList(rep.GetItemList(), "id", "type", id );
var model = rep.GetItemByID(id.Value);
if ( model != null )
{
return View(model);
}
}
else
{
ViewBag.SelectList = new SelectList(rep.GetItemList(), "id", "type");
}
return View();
}
In your View use it like this:
#Html.DropDownListFor(model => model.itemID, (SelectList)ViewBag.SelectList, "Please select...")
Related
My question is two-fold.
I have a View that gets data on the change of a drop down selection.
The data retrieved is a List property of a ViewModel class using an Ajax call.
This data is shown as a selection of check boxes for the user to select any number of them.
If I return a Partial View from an AJAX call, this is easy enough, but from what I have experienced, this doesn't work for POST'ing back to the controller. Nothing is bound correctly.
From what I have read the correct way is to use EditorFor, So firstly, I cannot figure out how to populate the EditorFor from the AJAX call.
Secondly, If I test by sending initial data from my GET, the EditorFor displays the correct checkbox options, but when I POST, the count of the items is 0.
View:
#model EngineeringAssistantMVC.Controllers.FirmwareController.FirmwareViewModel
#using (Html.BeginForm("Upload", "Firmware", FormMethod.Post, new { #id = "uploadFirmwareForm", #class = "form-horizontal" }))
{
<!-- Device -->
<div class="form-group">
<div class="col-lg-1">
#Html.LabelFor(x => x.Device, htmlAttributes: new { #class = "control-label" })
</div>
<div class="col-lg-2">
#Html.DropDownListFor(x => x.Device, ViewBag.Devices as IEnumerable<SelectListItem>, new { #class = "form-control", #id = "Devices" })
</div>
<div class="col-lg-9">
#Html.ValidationMessageFor(x => x.Device, "", new { #class = "text-danger" })
</div>
</div>
#Html.EditorFor(x => x.SelectedModels, "SelectedModels", new { #id = "Models" })
#Html.HiddenFor(x => x.SelectedModels)
}
And the AJAX call:
function GetModels() {
$.ajax({
type: "GET",
url: '#Url.Action("GetModels", "Firmware", null)',
data: { SelectedDevice: $('#Devices').val() },
success: function (dataSet) {
//$('#Models').html(dataSet);
//$('#Models').data(dataSet);
//$('#Models').val(dataSet);
// How do I populate the EditorFor from the dataSet returned?
},
error: function (err) {
console.log("ERROR: " + err.responseText);
},
})
}
SelectedModels EditFor Template:
#model IEnumerable<EngineeringAssistantMVC.ViewModels.ModelViewModel>
#foreach (var item in Model)
{
#Html.CheckBoxFor(x => item.IsSelected)
#Html.Label(item.Description)
#Html.HiddenFor(x => item.ModelId)
#Html.HiddenFor(x => item.IsSelected)
#Html.HiddenFor(x => item.Description)
}
Controller:
[HttpPost]
public ActionResult Upload(HttpPostedFileBase uploadFile, FirmwareViewModel firmwareViewModel)
{
// firmwareViewModel.SelectedModels count is 0 here
}
ModelFirmware Class:
public class ModelFirmware
{
public int ModelFirmwareId { get; set; }
public int FirmwareId { get; set; }
public int ModelId { get; set; }
}
FirmwareViewModel:
public class FirmwareViewModel
{
public int FirmwareViewModelId { get; set; }
[Required]
public string Device { get; set; }
public ICollection<ModelViewModel> SelectedModels { get; set; }
}
I just can't get it to work correctly.
EDIT 1: - Add method that returns the models
[HttpGet]
public ActionResult GetModels(string SelectedDevice)
{
var deviceAbbreviation = _dbContext.Radios.Where(x => x.RadioName == SelectedDevice).Select(x => x.ProjectAbbreviation).FirstOrDefault();
var models = _dbContext.AnatomyModels.Where(x => x.SerialPrefix.StartsWith(deviceAbbreviation.Trim()) && x.ParentId == 0).ToList();
List<ModelViewModel> mvms = models.Select(x => new ModelViewModel()
{
ModelId = x.AnatomyModelId,
Description = x.SerialPrefix,
IsSelected = false,
}).ToList();
return Json(mvms);
}
There are numerous issues with your code.
First your not using the EditorTemplate correctly. Change its name to ModelViewModel.cshtml to match the name of the class, and locate it in the /Views/Shared/EditorTemplates (or /Views/YourControllerName/EditorTemplates) folder. The template is then based on a single object (note also the LabelFor() required to create a label associated with the checkbox, and you need to delete the hidden input for IsSelected)
#model ModelViewModel
#Html.CheckBoxFor(m => m.IsSelected)
#Html.LabelFor(m => m.IsSelected, Model.Description)
#Html.HiddenFor(m => m.ModelId)
#Html.HiddenFor(m => m.Description)
Refer also Post an HTML Table to ADO.NET DataTable to understand why your foreach loop would never have created the correct name attributes for model binding.
Then in the main view use
<div id="container">
#Html.EditorFor(m => m.SelectedModels)
</div>
and remove the hidden input for SelectedModels (but before you do, inspect the html for that element to understand why its value would never bind). The EditorFor() method will correctly generate the html for each item in your collection.
Next, change your GetModels() method to return a partial view based on FirmwareViewModel since that is what you will be posting back. Note that you could return a JsonResult, but that would mean generating a whole lot of html in the ajax call back that would not be strongly typed.
[HttpGet]
public PartialViewResult GetModels(string SelectedDevice)
{
var deviceAbbreviation = _dbContext.Radios.Where(x => x.RadioName == SelectedDevice).Select(x => x.ProjectAbbreviation).FirstOrDefault();
var models = _dbContext.AnatomyModels.Where(x => x.SerialPrefix.StartsWith(deviceAbbreviation.Trim()) && x.ParentId == 0).ToList();
List<ModelViewModel> mvms = models.Select(x => new ModelViewModel()
{
ModelId = x.AnatomyModelId,
Description = x.SerialPrefix,
IsSelected = false, // not really necessary since its the default
}).ToList();
FirmwareViewModel model = new FirmwareViewModel
{
SelectedModels = mvms
};
return PartialView(model);
}
and your GetModels.cshtml view will be
#model FirmwareViewModel
#Html.EditorFor(m => m.SelectedModels)
Then, modify your ajax call to add the partial view in the success callback
$.ajax({
type: "GET",
url: '#Url.Action("GetModels", "Firmware")', // do not need to add 3rd parameter
data: { SelectedDevice: $('#Devices').val() },
success: function (response) {
$('#container').html(response);
},
error: function (err) {
console.log("ERROR: " + err.responseText);
},
})
The .html() function will replace any elements already existing in the <div id="container"> element
Finally, since your using a view model, make use of it and do not use ViewBag. Your view model should contain a IEnumerable<SelectListItem> Devices property which you populate in the GET method (and use #Html.DropDownListFor(x => x.Device, Model.Devices, new { #class = "form-control" }) in the view (note also that the method generates id="Device"). It should also contain a HttpPostedFileBase property to avoid the additional parameter in the POST method, and allow you to add validation attributes.
this is my Get actionresult :
public ActionResult Add()
{
ViewData["categoryList"]= _categoryRepository.GetAllCategory().
ToSelectList(c => c.Id, c => c.Name);
return View("Add");
}
this my razor that render the categoryList , and I have no trouble with that !
<div>
#Html.LabelFor(b => b.Category)
#Html.DropDownList("Category", ViewData["categoryList"] as IEnumerable<SelectListItem>)
#Html.ValidationMessageFor(b => b.Category)
</div>
finally after submitting the page , category select send via null value to post this action
[HttpPost]
public ActionResult Add(BlogPost blogPost)
{
if (ModelState.IsValid)
{
blogPost.PublishDate = DateTime.Now;
_blogPostRepository.AddPost(blogPost);
_blogPostRepository.Save();
return RedirectToAction("Add");
}
return new HttpNotFoundResult("An Error Accoured while requesting your order!");
}
could anybody tell me why ??
controller
public ActionResult Add()
{
ViewBag.CategoryList = new SelectList(_categoryRepository.GetAllCategory(), "Id", "Name");
// you dont need the specify View name
// like this: return View("Add")
// you need to pass your model.
return View(new BlogPost());
}
view
#Html.DropDownListFor(model => model.CategoryId, ViewBag.CategoryList as SelectList, "--- Select Category ---", new { #class = "some_class" })
controller post action
[HttpPost]
public ActionResult Add(BlogPost blogPost)
{
if (ModelState.IsValid)
{
blogPost.PublishDate = DateTime.Now;
_blogPostRepository.AddPost(blogPost);
_blogPostRepository.Save();
// if you want to return "Add" page you should
// initialize your viewbag and create model instance again
ViewBag.CategoryList = new SelectList(_categoryRepository.GetAllCategory(), "Id", "Name");
return View(new BlogPost());
}
return new HttpNotFoundResult("An Error Accoured while requesting your order!");
}
I have a generic list method that returns a CategoryID and CategoryName.
I have spent enough time researching and cant seem to put it together. I very new at MVC.
Here is my DropdownList Method in a repository. I get back the data... So far so good.
public List<DropdownList> GetDDl()
{
return catDDL;
}
Here is my CONTROLLER CODE(attempt at it)
IEnumerable<SelectListItem> liCat =
userRepository.Getddl().Select(c => new SelectListItem
{
Value = c.DropDownID.ToString(),
Text = c.DropDownText
}
ViewBag.catItems = new SelecList(liCat,"Value","Text");
Here is my VIEW
#Html.Dropdownlist("catItems","Select Category)
Try to avoid dynamic stuff like ViewBag and ViewData. Use strongly typed views.
ViewModel is just a POCO class which we will use to transfer data between your view and the action method. It will be specific to the view.
ex : if you want to create a view which creates a product. So create a viewmodel like this
public class Product
{
public string Name { set;get;}
public IEnumerable<SelectListItem> Categories{ get; set; }
public string SelectedCategoryId { get; set; }
//Other Properties as needed
}
now in your GET action method, you create an object of this view model and initialize the values and send to the view.
public ActionResult Create()
{
var vm=new Product();
vm.Categories=userRepository.Getddl().
Select(c => new SelectListItem
{
Value = c.DropDownID.ToString(),
Text = c.DropDownText
});
return View(vm);
}
Now make your view strongly typed to our Product class and use the Html.DropDownListFor helper method.
#model PersonsProduct
#using (Html.BeginForm())
{
#Html.DropDownListFor(x => x.SelectedCategoryId,
new SelectList(Model.Categories,"Value","Text"), "Select")
<input type="submit" value="save" />
}
Now in your HttpPost , you can get the form values like this
[HttpPost]
public ActionResult Create(Product model)
{
if(ModelState.IsValid)
{
//check model.SelectedCategoryId
//save and redirect
}
//to do :reload the dropdown again.
return view(model);
}
Should just be:
Controller:
IEnumerable<SelectListItem> liCat = userRepository.Getddl().Select(c => new SelectListItem
{
Value = c.DropDownID.ToString(),
Text = c.DropDownText
}
ViewBag.catItems = liCat
View:
#Html.Dropdownlist("catItems", ViewBag.catItems)
i've a table post(p_id,u_id(FK),date,title,type,viewer)and a Modelclass UserModel.cs with(p_id,u_id,date,title,type). When i to create a new post in a post controller i tried like public ActionResult Create(PostModel p)
{
if (ModelState.IsValid)
{
p.date = DateTime.Today;
post post = new Models.DB.post();
post.u_id = User.Identity.Name;
post.date = p.date;
post.title = p.title;
post.type= p.type;
post.viwer=1;
try{ db.posts.AddObject(post);
db.SaveChanges();
return RedirectToAction("LoggedIndex");
}
catch{ return RedirectToAction("Index"); }
}
return View(p);
}
It is not Working. I think from is not submitting when i submit always view is returned. i i've created stronly typed create view. in create.cshtml i've two hidden field user_id and date of postModel Object. I'm very new at MVC.please help me thanks in advance....
Sending Data from View to the Controler by using the MVC-Model
I understand it like that: you have a mvc3 page and want to send some custom data to your controller:
Controller:
[http-post]
public ActionResult Create(PostModel p)
{
if (ModelState.IsValid)
{
p.date = DateTime.Today;
post post = new Models.DB.post();
post.u_id = User.Identity.Name;
post.date = p.date;
post.title = p.title;
post.type= p.type;
post.viwer=1;
db.posts.AddObject(post);
db.SaveChanges();
return RedirectToAction("LoggedIndex");
}
return View(p);
}
public ActionResult Create()
{
return View(new post());
}
View:
#model post
#using (Html.BeginForm("Create", "Controller-Name", FormMethod.Post, new { id = "FORM" }))
{
#Html.EditorFor(model => model.date)
#Html.EditorFor(model => model.name)
#Html.EditorFor(model => model.title)
#Html.EditorFor(model => model.type)
}
Further informations: http://weblogs.asp.net/scottgu/archive/2010/07/27/introducing-asp-net-mvc-3-preview-1.aspx
I have a property in my model very simple one:
Now this dropDown doesn't work right
#Html.DropDownListFor(m => m.Camp, new SelectList(ViewBag.Camps, "Id", "Name"))
it returns null instead of a chosen Camp, but if I change that into:
#Html.DropDownListFor(m => m.Camp.Id, new SelectList(ViewBag.Camps, "Id", "Name"))
It would return me a Camp object with correct Id, but the Name would be still null.
Why?
UPD:
And now another problem is if I choose the second approach it would screw up with unobtrusive validation. Although I'll be able to get the right camp based on the chosen id.
That's normal. Only the Id is posted to the controller action. That's how dropdown inside forms work. So that's all you can hope to get there. You will then use this Id to get the corresponding Camp object from the database:
[HttpPost]
public ActionResult Foo([Bind(Prefix = "Camp")]int id)
{
Camp camp = Repository.GetCamp(id);
...
}
Also please get rid of this ViewBag and use a real view model:
public class CampViewModel
{
public int Id { get; set; }
public IEnumerable<SelectListItem> Camps { get; set; }
}
and in the controller:
public ActionResult Index()
{
var model = new CampViewModel
{
Camps = Repository.GetCamps().Select(x => new SelectListItem
{
Value = x.Id.ToString(),
Text = x.Name
})
};
return View(model);
}
[HttpPost]
public ActionResult Index(int id)
{
Camp camp = Repository.GetCamp(id);
...
}
and the view:
#model CampViewModel
#using (Html.BeginForm())
{
#Html.DropDownListFor(
x => x.Id,
new SelectList(Model.Camps, "Value", "Text")
)
<input type="submit" value="OK" />
}