0

I am trying to Create a page that has 3 cascading dropdowns for vehicle Make, Model and Variant.

Clicking on Search button must post the page to another controller and view using the ViewModel which will show the details of the vehicle selected on the index page.

Models

public class Vehicle
{
    public IEnumerable<SelectListItem> Make{ get; set; }
    public IEnumerable<SelectListItem> Model { get; set; }
    public IEnumerable<SelectListItem> Variant { get; set; }
}

public class Details
{
    public string PowerOutput{ get; set; }
    public string NumberOfDoors{ get; set; }
    public string VariantName { get; set; }
}

HomeController

VehicleEntities db = new VehicleEntities();

public ActionResult Index()
{
    var model = new Vehicle
    {
        BrandName = GetMakes()
    };

    return View(model);
}


private IEnumerable<SelectListItem> GetMakes()
{
    IEnumerable<SelectListItem> brandname = from s in db.prcGetMakes(null)
                                            select new SelectListItem
                                            {
                                                Selected = s.Make.ToString() == "Active",
                                                Text = s.Make,
                                                Value = s.Make
                                            };
    return new SelectList(brandname, "Value", "Text");
}

public JsonResult GetModels(string make)
{
    var list = db.prcGetModels(make);

    return Json(new SelectList(list, "Model", "Model"), JsonRequestBehavior.AllowGet);
}

public JsonResult GetVariants(string model)
{
    var list = db.prcGetVariants(model);

    return Json(new SelectList(list, "Variant", "Variant"), JsonRequestBehavior.AllowGet);
}

View

@model WebApplication7.ViewModels.VehicleDetailsViewModel**
@using (Html.BeginForm("Index", "Vehicle", FormMethod.Post))
{
    @Html.AntiForgeryToken()
    @Html.ValidationSummary()

<div class="form-group">
    <div class="col-md-3">
        @Html.Label("Make", new { @class = "col-md-2 control-label" })
        @Html.DropDownList("Make", Model.Make as SelectList, htmlAttributes: new { @class = "form-control minimal", @onchange = "refreshModelFromBrandName()" })
    </div>
    <div class="col-md-3">
        @Html.LabelFor(model => model.Model, htmlAttributes: new { @class = "control-label col-md-2" })
        @Html.DropDownList("Model", Model.Model as SelectList, htmlAttributes: new { @class = "form-control minimal", @onchange = "refreshVariantFromModel()" })*@
    </div>
    <div class="col-md-3">
        @Html.LabelFor(model => model.Brand, htmlAttributes: new { @class = "control-label col-md-2" })
        @Html.DropDownList("Variant", Model.Variant as SelectList, htmlAttributes: new { @class = "form-control minimal", @onchange = "" })*@
    </div>
    <div class="col-md-3">
        <input id="search" type="submit" value="Search" />
    </div>
</div>
}

<script type="text/javascript">

    function refreshBrandName() {
        // get references to the source and target drop downs html controls
        // These are jquery searches to find the drop down controls

        // find a control with id=BrandName
        src = $("#Make");

        // find a control with id=Model (you need to add this to your view)
        tgt = $("#BrandName");

        // clear drop down
        tgt.empty();

        // Get new model dataset via ajax
        // based on BrandName
        // The url parameter points at your web method
        $.ajax({
            type: 'GET',
            //url: 'GetMakes',
            url: 'GetMakes',
            dataType: 'json',
            data: { brandName: src.val() },
            // success is called when dataset returns
            success: function (p) {
                // Populate with each returned member
                $.each(p, function (i, pr) {
                    tgt.append(
                        '<option value="' + pr.Value + '">' +
                        pr.Text + '</option>'
                    );
                })
            }
        });
    }

    function refreshModelFromBrandName() {
        // get references to the source and target drop downs html controls
        // These are jquery searches to find the drop down controls

        // find a control with id=BrandName
        src = $("#Make");

        // find a control with id=Model (you need to add this to your view)
        tgt = $("#Model");

        // clear drop down
        tgt.empty();

        // Get new model dataset via ajax
        // based on BrandName
        // The url parameter points at your web method
        $.ajax({
            type: 'GET',
            url: 'GetModels',
            dataType: 'json',
            data: { brandName: src.val() },
            // success is called when dataset returns
            success: function (p) {
                // Populate with each returned member
                $.each(p, function (i, pr) {
                    tgt.append(
                        '<option value="' + pr.Value + '">' +
                        pr.Text + '</option>'
                    );
                })
            }
        });
    }

    function refreshVariantFromModel() {
        // get references to the source and target drop downs html controls
        // These are jquery searches to find the drop down controls

        // find a control with id=BrandName
        src = $("#Model");

        // find a control with id=Model (you need to add this to your view)
        tgt = $("#Variant");

        // clear drop down
        tgt.empty();

        // Get new model dataset via ajax
        // based on BrandName
        // The url parameter points at your web method
        $.ajax({
            type: 'GET',
            url: 'GetVariants',
            dataType: 'json',
            data: { modelName: src.val() },
            // success is called when dataset returns
            success: function (p) {
                // Populate with each returned member
                $.each(p, function (i, pr) {
                    tgt.append(
                        '<option value="' + pr.Value + '">' +
                        pr.Text + '</option>'
                    );
                })
            }
        });
    }
</script>

This works nicely and the cascading dropdowns do what they are expected to and the form posts to the correct Controller

VehicleController

public ActionResult Index()
{
    return View();
}

[HttpPost]
public ActionResult Index(Details model)
{

    return View(model);
}

But this is where I get stuck. How do I get the Vehicle Controller to pass the VehicleDetailsViewModel to the View attached to the VehicleController?

I know it has something to do with the fact that I am only using the Vehicle class in the HomeController but I have no idea how to create the implement the VehicleDetailsViewController so that ViewModel works on the HomeController view and on the VehicleController view.

I think the ViewModel must be something like this

public class VehicleDetailsViewModel
{
    public IEnumerable<SelectListItem> Brands { get; set; }
    public IEnumerable<SelectListItem> Models { get; set; }
    public IEnumerable<SelectListItem> Variants { get; set; }

    public string PowerOutput { get; set; }
    public string NumberOfDoors { get; set; }
    public string VariantName { get; set; }

    public VehicleDetailsViewModel()
    {
        this.Brands = new List<SelectListItem>();
        this.Models = new List<SelectListItem>();
        this.Variants = new List<SelectListItem>();
    }
}

Any help will be much appreciated :)

4
  • What is your VehicleDetailsViewModel? That is the model is the view, so that is what the model in the POST method needs to be. And there are other issues with your code - for example, read this answer. And to correctly implement cascading dropdownlists, refer this DotNetFiddle Commented Feb 28, 2018 at 22:22
  • Thanks for telling me to put the VehicleDetailsViewModel into the POST But I don't know what the ViewModel should look like or how to implement it. Your first link is how to use ViewBag, but I don't want to use ViewBag I prefer to use Model. How do I rework your second link to use my Entity instead of hard-coding values in each dropdown? Commented Mar 2, 2018 at 16:55
  • I am not telling you to use VIewBag (I never use it and would never suggest it) - I am pointing out that you have the same name for the property your binding to - ("Make") and the SelectList - Model.Make which can never bind correctly as explained in the link. The whole point of using a view model is to be able to bind to it, yet your creating `<select> that have no relationship to anything and then your POST method has an entirely different model anyway Commented Mar 2, 2018 at 19:27
  • 1
    Your view model will contain int Make to bind to, IEnumerable<SelectListItem> MakeList for the options (ditto for Model and Variant) and properties string PowerOutput etc (and the POST method will contain a parameter for the view model (that is what you sent to the view so that is what you send back) Commented Mar 2, 2018 at 19:36

1 Answer 1

2

Usually i make the next:

public ActionResult Index()
{
   var vm = new VehicleDetailsViewController();
   return View(vm);
}

[HttpPost]
public ActionResult Index(VehicleDetailsViewController vm)
{
   //Validation something wrong
    if (!ModelState.IsValid) return View(vm);

   //Make what you want with all OK
    return View("AllOk");
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.