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

Jason Ebersey
  • 629
  • 9
  • 17
  • 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](https://stackoverflow.com/questions/37161202/can-the-viewbag-name-be-the-same-as-the-model-property-name-in-a-dropdownlist). And to correctly implement cascading dropdownlists, refer [this DotNetFiddle](https://dotnetfiddle.net/1bPZym) –  Feb 28 '18 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? – Jason Ebersey Mar 02 '18 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 ` –  Mar 02 '18 at 19:27
  • 1
    Your view model will contain `int Make` to bind to, `IEnumerable 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) –  Mar 02 '18 at 19:36

1 Answers1

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");
}
Jordi Jordi
  • 461
  • 3
  • 10