DropDownListFor issue with selected values - asp.net-mvc-4

I'm fairly new (okay well very new) to MVC. I'm trying to create a cascading dropdown list, where I can also preselect a value.
I've spent most of the day on this, but not really got very far. I've looked at a lot of resources, though the most useful were:
http://www.deliveron.com/blog/post/Creating-a-Cascading-Dropdown-in-ASPnet-MVC-3-and-jQuery.aspx - I used this heavily and got my own version of it working, I couldn't get there example working.
http://odetocode.com/blogs/scott/archive/2013/03/11/dropdownlistfor-with-asp-net-mvc.aspx - general oversite.
MVC DropDownList SelectedValue not displaying correctly - There is what appears to be a really good example here, following the view-model method, but for some reason it didn't seem to work for me.
What I've got so far is:
Model:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace BLPrototype.Models
{
public class VehicleModels
{
public List<vehicle_make> getVehicleMakeList()
{
using (var db = new BLEntities())
{
List<vehicle_make> vmk = db.vehicle_make.OrderBy(r => r.vmk_name).ToList();
return vmk;
}
}
public List<vehicle_group> getVehicleModelList(string vmk_id)
{
using (var db = new BLEntities())
{
List<vehicle_group> vmo = db.vehicle_group.Where(v => v.vg_vmk_id == vmk_id).OrderBy(r => r.vg_name).ToList();
return vmo;
}
}
}
public class vmCatalogue1
{
public string Title { get; set; }
[Display(Name = "Select a make")]
public IEnumerable<SelectListItem> selectVehicleMake { get; set; }
[Display(Name = "Select a model")]
public IEnumerable<SelectListItem> selectVehicleModel { get; set; }
}
}
Controller:
using BLPrototype.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace BLPrototype.Controllers
{
public class TestController : Controller
{
//
// GET: /Test/
public ActionResult Index()
{
VehicleModels vm = new VehicleModels();
var model = new vmCatalogue1();
//Which is better and why?
model.selectVehicleMake = vm.getVehicleMakeList().Select(x => new SelectListItem { Value = x.vmk_id, Text = x.vmk_name });
//model.selectVehicleMake = new SelectList(vm.getVehicleMakeList(), "vmk_id", "vmk_name");
model.selectVehicleModel = new SelectList(vm.getVehicleModelList(""), "vg_id", "vg_name");
model.Title = "Title goes here";
return View(model);
}
VehicleModels vm = new VehicleModels();
[HttpPost]
public ActionResult Makes()
{
var makes = vm.getVehicleMakeList();
return Json(new SelectList(makes, "vmk_id", "vmk_name"));
}
[HttpPost]
public ActionResult Models(string vmk_id)
{
var models = vm.getVehicleModelList(vmk_id);
return Json(new SelectList(models, "vg_id", "vg_Name"));
}
}
}
View:
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>#Model.Title </title>
<script src="~/Scripts/jquery-1.7.1.min.js"></script>
<script type="text/javascript">
function getModels(vmk_id) {
$.ajax({
url: "#Url.Action("Models")",
data: { vmk_id: vmk_id },
dataType: "json",
type: "POST",
error: function () {
alert("An error occurred.");
errorHandler("error1");
},
success: function (data) {
var items = "";
$.each(data, function (i, item) {
items += "<option value=\"" + item.Value + "\">" + item.Text + "</option>";
});
$("#selectVehicleModel").html(items);
}
});
}
$(document).ready(function () {
$("#selectVehicleMake").change(function () {
getModels($("#selectVehicleMake").val());
});
});
</script>
</head>
<body>
<div>
#model BLPrototype.Models.vmCatalogue1
#using (Html.BeginForm())
{
<div class="editor-label">
#Html.LabelFor(m => m.selectVehicleMake)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.selectVehicleMake, Model.selectVehicleMake, "Please select")
</div>
<div class="editor-label">
#Html.LabelFor(m => m.selectVehicleModel)
</div>
<div class="editor-field">
#Html.DropDownListFor(m => m.selectVehicleModel, Model.selectVehicleModel, new { style = "width: 150px" })
</div>
}
</body>
</html>
The above example shows the cascading dropdowns. I've seen lots of different ways of populating a dropdownlistfor, I would like to do it via a View-Model. I'm really not sure which is the best way. Some examples seem to show just a list, some a list of selectlistitems and others just a selectlist. Could someone please tell me the difference and what is the 'better' way to do it... I know it often depends upon the application.
I would also like to be able to preselect a Make if I wish, I've seen some examples where you include the ID in various ways, and others by tagging it on to the end of the select list. None of them seem to work.
Also the Model shouldn't really show unless the make has been selected. Would I do this via CSS or another method?
Finally I've put the "Please Select" at the end of the make, so it shows. This doesn't seem like a great way of doing this. Could you please point me in the right direction?
Thank you for your time and I'm sorry for so many stupid noobish questions!
Thanks
Jay

A list of SelectListItems is more explicit and gives you a Selected property but it's more geared at multi-select lists and you no longer have access to the original list if your view requires it elsewhere.
Take a look at SelectList. It might work better for you in some cases. You can obtain one like this:
myList.ToSelectList("ID", "Description")
or
new SelectList(myList, "ID", "Description)
Add:
data-placeholder="Please Select"
in the same object that contains your "style = ..." if you want a placeholder until an option is selected.

I finally found the solution that I was after. I'll post it here in case anyone find this post in the future with similar errors.
Model:
public class VehicleModels
{
public List<vehicle_make> getVehicleMakeList()
{
using (var db = new BLEntities())
{
List<vehicle_make> vmk = db.vehicle_make.OrderBy(r => r.vmk_name).ToList();
return vmk;
}
}
public List<vehicle_group> getVehicleModelList(string vmk_id)
{
using (var db = new BLEntities())
{
List<vehicle_group> vmo = db.vehicle_group.Where(v => v.vg_vmk_id == vmk_id).OrderBy(r => r.vg_name).ToList();
return vmo;
}
}
}
public class vmCatalogue3
{
public string Title { get; set; }
public string selectedMakeId { get; set; }
public string SelectTopLine { get; set; }
[Display(Name = "Select a make")]
public SelectList selectVehicleMake { get; set; }
[Display(Name = "Select a model")]
public SelectList selectVehicleModel { get; set; }
}
Controller:
public ActionResult Index()
{
VehicleModels vm = new VehicleModels();
var model = new vmCatalogue3();
model.selectedMakeId = "870";
model.SelectTopLine = "Please Select";
model.selectVehicleMake = new SelectList(vm.getVehicleMakeList(), "vmk_id", "vmk_name");
model.selectVehicleModel = new SelectList(vm.getVehicleModelList(""), "vg_id", "vg_name");
return View(model);
}
View:
#Html.DropDownListFor(m => m.selectedMakeId, Model.selectVehicleMake, Model.SelectTopLine )
Hope that helps someone!

Related

How can I link a DropDown and Textbox to my Model's data?

Here is the code for my Model. ListBuilder.DropDown is part of a common class of functions, which simply returns a List when provided the string name of a stored procedure that will be called on the database.
There is some more shared common class code (stored procedure related) with in the try statement, but that implementation is irrelevant to the problem I'm having. The data is successfully retrieved and stored into the model.
public class PositionViewModel
{
[Display(Name = "Series")]
public string series { get; set; }
public int seriesID { get; set; }
public List<SelectListItem> list_Series { get; set; }
}
public PositionViewModel(string id)
{
Get(id);
this.list_Series = ListBuilder.DropDown(AppConstants.StoredProc_GetSeries);
}
public Position Get(string id)
{
ExecStoredProcedure sp = null;
DataTable dt = new DataTable();
try
{
sp = new ExecStoredProcedure(AppConstants.SP_POSITION_GET, new ConnectionManager().GetConnection(), AppConstants.SP_POSITION_GET);
sp.SPAddParm("#PD_ID", SqlDbType.Char, id, ParameterDirection.Input);
dt = sp.SPselect();
if (dt.Rows.Count > 0)
{
this.pd_id = dt.Rows[0]["PD_ID"].ToString();
this.official_title = dt.Rows[0]["label"].ToString();
this.series = dt.Rows[0]["Series"].ToString();
this.grade = dt.Rows[0]["Grade"].ToString();
this.PDType = dt.Rows[0]["PDType"].ToString();
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
sp.dbConnection.Close();
}
return this;
}
Here is the code for my Controller
[HttpGet]
public ActionResult PositionEdit(string id)
{
PositionViewModel model = new PositionViewModel(id);
return View("PositionEdit", model);
}
[HttpPost]
public ActionResult PositionEdit(PositionViewModel model)
{
if (ModelState.IsValid)
{
int rc = model.Update();
return RedirectToAction("PositionView");
}
else
{
return View("PositionEdit", model);
}
}
Here is the code for my view. What I'd like to have is a dropdownlist that contains the model.seriesID (a sequence number) but as the user selects an item, it will update the textbox with model.series (the name of the series)
#model Project.Models.PositionViewModel
#{
ViewBag.Title = "Edit Position Description";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div class="editor-label">
#Html.LabelFor(model => model.series)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => Model.seriesID, Model.list_Series, new { style = "width:550px" })
#Html.ValidationMessageFor(model => Model.seriesID)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.series, new { style = "width:250px;" })
#Html.ValidationMessageFor(model => model.series)
</div>
<div class="toppad20">
<input type="submit" value="Save" />
</div>
}
I am having trouble linking the dropdownlist with the textbox. Do I need some kind of onChange event? Thanks in advance for your help.
Your solution involves passing a string into your view model's constructor. However, on post, the model binder will be incapable of instantiating your view model with anything but the parameterless constructor. That's part of the reason, but not the only reason, that view models should not handle things like datastore access. That is the job of the controller.
On your view model, leave your list property as a auto-implemented property and then in your controller call ListBuilder.DropDown, which you can use data from your model to call, at that point.

Nested objects in view model confuses MVC 4 routing?

So I have this view and it's generated by CMS. The brains of the view are in another view rendered by RenderAction.
Up until now it has looked like this:
Main.cshtml
<div>CMS content</div>
#{
var myContent = new MainContentModel() {
SomethingUsedFurtherDown = "Easier for CMS people to edit"
}
Html.RenderAction("_Main", "Foo", new { arg1 = "this", arg2 = "that", content = myContent });
}
MainContentModel.cs
namespace MyApp.Services.ViewModels.Foo
{
public class MainContentModel
{
public string SomethingUsedFurtherDown { get; set; }
}
}
MainViewModel.cs
namespace MyApp.Services.ViewModels.Foo
{
public class MainViewModel
{
public string Arg1 { set; set; }
public string Arg2 { set; set; }
public string Name { get; set; }
public string Age { get; set; }
public string Address { get; set; }
public MainContentModel Content { get; set; }
}
}
_Main.cshtml
#model MyApp.Services.ViewModels.Foo.MainViewModel
#Html.EditorFor(m => m.Name)
#Html.EditorFor(m => m.Age)
#Html.EditorFor(m => m.Address)
<!-- whatever - doesn't matter -->
FooController.cs
namespace My.App.Controllers
{
using System;
using System.Web.Mvc;
public class FooController
{
public ActionResult Main(string arg1, string arg2, MainContentModel cm)
{
var vm = new MainViewModel() { Arg1 = arg1, Arg2 = arg2, Content = cm };
return this.View("_Main", vm);
}
}
}
All works fine. So why am I bothering you? Well, this isn't the real code, obviously. There are many more arguments to the controller method and it's all getting rather messy. So I figured I would pass in the view model from the outer view.
Main.cshtml
<div>CMS content</div>
#{
var myVM = new MainViewModel() {
Arg1 = "this"
, Arg2 = "that"
, Content = new MainContentModel() {
SomethingUsedFurtherDown = "Easier for CMS people to edit"
}
};
Html.RenderAction("_Main", "Foo", new { vm = myVM });
}
...and just have one argument to the controller method
FooController.cs
namespace My.App.Controllers
{
using System;
using System.Web.Mvc;
public class FooController
{
public ActionResult Main(MainViewModel vm)
{
return this.View("_Main", vm);
}
}
}
And that's where the trouble starts. I get this error:
The view '_Main' or its master was not found or no view engine supports the searched locations. The following locations were searched:
~/Views/Foo/_Main.aspx
~/Views/Foo/_Main.ascx
~/Views/Shared/_Main.aspx
~/Views/Shared/_Main.ascx
~/Views/Foo/_Main.cshtml
~/Views/Foo/_Main.vbhtml
~/Views/Shared/_Main.cshtml
~/Views/Shared/_Main.vbhtml
~/Views/CMS/_Main.cshtml
~/Views/CMS/Foo/_Main.cshtml
But if I remove the initialisation of the content model, it all works again. Well, okay, it doesn't work work. But the error goes away, leaving me to come up with the obvious solution of adding the content model to the argument list and assigning it to the view model in the controller method.
Main.cshtml
<div>CMS content</div>
#{
var myVM = new MainViewModel() {
Arg1 = "this"
, Arg2 = "that"
};
var myCm = new MainContentModel() {
SomethingUsedFurtherDown = "Easier for CMS people to edit"
};
Html.RenderAction("_Main", "Foo", new { vm = myVM, cm = myCm });
}
FooController.cs
namespace My.App.Controllers
{
using System;
using System.Web.Mvc;
public class FooController
{
public ActionResult Main(MainViewModel vm, MainContentModel cm)
{
vm.Content = cm;
return this.View("_Main", vm);
}
}
}
Which is fine except that there are quite a few child objects of the real view model and I don't want to separate those out into arguments - it defeats the purpose of what is supposed to be a tidying up exercise.
Question
Is MVC4 supposed to be binding the child objects seamlessly and this is a bug? Or does it just not work this way and my only choice is to separate them out into extra parameters as above?
Cheers,
.pd.
Just in case anybody else has fallen into this trap. The problem was not the model binding. MVC 4 binds nested objects just fine. I was missing the subtlety of the error. The standard here is to prefix the "real" view name with an underscore and it was this that was not being found. The failure was occurring here:
return this.View("_Main");
What I didn't include in my post (cuz if I'd remembered, that would've fixed it!) was the fact that in the real situation the RenderAction looks like this:
RenderAction("Main", "Foo", new { area = "SomeArea", ... });
And I had missed off the area in my refactoring. Pretty embarrassing but if it stops somebody else beating their head against the wall then the shame will have been worth it :-)

Implementation of Kendo Listview control in MVC

I was trying to implement Listview control of Kendo UI for MVC. I am trying to bind the list view with my model but I am getting this error :
"CS1977: Cannot use a lambda expression as an argument to a dynamically dispatched operation without first casting it to a delegate or expression tree type"
I have checked some other questions on stackoverflow with the same error but I am unable to know the cause for this error as this is kendo Syntax and there is nothing wrong with my code as far as I know.
The error is in this line::.DataSource(ds => ds
View Page:
#{
ViewBag.Title = "Courses";
}
#using Kendo.Mvc.UI
<h2>Courses</h2>
Back
<div class="bodywrap">
<div class="CommonClass">
#( Html.Kendo().ListView<K_SampleProject.Models.CourseModel>(Model)
.Name("listView")
.TagName("div")
.ClientTemplateId("template")
.DataSource(ds => ds
.Model(model =>
{
//The unique identifier (primary key) of the model is the ProductID property
model.Id(p => p.ProductID);
// Declare a model field and optionally specify its default value (used when a new model instance is created)
model.Field(p => p.ProductName).DefaultValue("N/A");
// Declare a model field and make it readonly
model.Field(p => p.UnitPrice).Editable(false);
})
)
.Pageable()
)
</div>
</div>
<script type="text/x-kendo-tmpl" id="template">
<div class="product">
<img src="#Url.Content("~/content/web/foods/")${ProductID}.jpg" alt="${ProductName} image" />
<h3>${ProductName}</h3>
<dl>
<dt>Price:</dt>
<dd>${kendo.toString(UnitPrice, "c")}</dd>
</dl>
</div>
</script>
Model
namespace K_SampleProject.Models
{
public class CourseModel
{
public List<tbl_Courses> CourseList { get; set; }
public string ProductID { get; set; }
public string ProductName { get; set; }
public string UnitPrice { get; set; }
}
}
Controller
public ActionResult Courses()
{
CourseModel Model = new CourseModel();
RegistrationService ObjService = new RegistrationService();
Model.CourseList = ObjService.GetCourses();
return View(Model);
}
The main error in your code is that you passing single CourseModel class to the list, when it expects the List of CourseModel.
So, your Controller should looks like:
public ActionResult Courses()
{
List<CourseModel> result;
CourseModel Model = new CourseModel();
RegistrationService ObjService = new RegistrationService();
Model.CourseList = ObjService.GetCourses();
result.Add(Model);
return View(result);
}
I also advise:
Add #model List<CourseModel> in top of the View
If it is a PartialView (not main view like index) change return for: return PartialView(result);

Using Ajax.BeginForm with MVC 4 - adding to my model collection asynchronously isn't working

I am trying to make a small football site where the user can create a new team and then asynchronously in another div it shows all the teams the user has created. So basically a team is created then added to the list of teams. All of this is in the model.
Now, I would like to do this asynchronously because its a nice to have but it's not working in my code. I am either missing something or it's not possible with what I am doing.
Controller
public ActionResult TeamManagement()
{
modelTeamSelect modelTeamSelect = new modelTeamSelect();
return View(modelTeamSelect);
}
[HttpPost]
public ActionResult TeamManagement(string btnSubmit, modelTeamSelect modelTeamSelect)
{
switch (btnSubmit)
{
case "Add Team":
// For now - add to collection but not use DAL
modelTeamSelect.teams.Add(modelTeamSelect.team);
//modelTeamSelect.team.TeamName = string.Empty;
break;
}
return View(modelTeamSelect);
}
View
#model Website.Models.modelTeamSelect
#{
ViewBag.Title = "Football App";
}
#section featured {
}
#using (Ajax.BeginForm(new AjaxOptions
{
HttpMethod = "POST",
Url = "Home/TeamManagement",
OnComplete = "teamAdded()"
}))
{
<div id="divTeams" style="float:left">
<h3>Create a new team:</h3>
#Html.LabelFor(m => m.team.TeamName)
#Html.TextBoxFor(m => m.team.TeamName)
<input type="submit" value="Add Team" name="btnSubmit" />
</div>
<div id="divCreatedTeams" style="float:left">
<h3>Your created teams:</h3>
#if (Model.teams.Count > 0)
{
for (int i = 0; i < Model.teams.Count; i++)
{
#Html.TextBoxFor(m => m.teams[i].TeamName)
}
}
</div>
<div id="divLeagues">
</div>
}
Model
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Website.Models
{
public class modelTeamSelect
{
public modelTeamSelect()
{
teams = new List<modelTeam>();
team = new modelTeam();
}
public List<modelTeam> teams { get; set; }
public modelTeam team { get; set; }
}
}
I have the right javascript references being used in the project as I recently fixed this.
Why isn't my UI changing to reflect new contents of list?
I dont get the idea of passing the submit button string to the Action. But in order to pass a ViewModel to the Action I think you have to write your own model binder. If you want you can try getting the models seperately in the action and combining them in the Action
public ActionResult TeamManagement(List<modelTeam> teams, modelTeam team)
and combine them in the action in the viewModel.
Just a sugestion If you want to retrieve them async with ajax what I do is return partial view (i think better in your case) or json

MVC4 Dropdown menu for Gender

I am new to MVC 4, i want to create a drop downmenu for listing the gender.I tried a lot but nothing helped me, also i google it but invain.Please help me in it tha thow to create dropdown menu for gender please guide me.
<div class="editor-field">
#Html.DropDownList("Gender", new List<SelectListItem>{
new SelectListItem{ Text="Male", Value="Male"},
new SelectListItem{ Text="Female", Value="Female"}
}, "--- Select ---"
)
</div>
say we use an enum for the gender:
namespace DropdownExample.Models
{
public enum GenderType
{
Male=1,
Female=2
}
}
and we make a model like this:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace DropdownExample.Models
{
public class ActionModel
{
public ActionModel()
{
ActionsList = new List<SelectListItem>();
}
[Display(Name="Gender")]
public int ActionId { get; set; }
public IEnumerable<SelectListItem> ActionsList { get; set; }
}
}
make a controller like so:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using DropdownExample.Models;
namespace DropdownExample.Controllers
{
public class ActionController : Controller
{
public ActionResult Index()
{
ActionModel model = new ActionModel();
IEnumerable<GenderType> GenderType = Enum.GetValues(typeof(GenderType))
.Cast<GenderType>();
model.ActionsList = from action in actionTypes
select new SelectListItem
{
Text = action.ToString(),
Value = ((int)action).ToString()
};
return View(model);
}
}
}
then in your view, you use the DropDownListFor html helper you include the following:
#model DropdownExample.Models.ActionModel
#{
ViewBag.Title = "Index";
}
#Html.LabelFor(model=>model.ActionId)
#Html.DropDownListFor(model=>model.ActionId, Model.ActionsList)
the DropDownListFor html helper uses at leats these two parameters:
the name of the property that will hold the selected value
the List<SelectListItem>() that contains all the options in the dropdownlist.
Create a Class for Gender
public class Gender
{
public int ID { get; set;}
public string Name { get; set; }
}
Create a list for Gender anywhere in the Controller
List<Gender> genderList = new List<Gender> {
new Gender{ Name="Male", ID = 1},
new Gender{ Name="Female", ID = 2}
};
You will have to create a SelectList for genders in the Action that will be passed to the view.
ViewBag.Genders= new SelectList(genderList,"ID","Name");
Finally you'll add a DropDownList to the view
#Html.DropDownList("Genders", null, htmlAttributes: new { #id = "genders", #class = "form-control" })
#Html.ValidationMessageFor(model => model.Gender, "", new { #class = "text-danger" })