MVC DropDownList OnChange to update other form fields

Chris Hammond picture Chris Hammond · Dec 2, 2015 · Viewed 45.5k times · Source

I am new to MVC (I am moving over from the dark side of traditional ASP.Net) and I know that SO is more of a "why doesn't this work" but, being new to MVC, I just wanted to ask how something is achieved - I don't really have any code or markup because I don't know how at the moment.

Right, using an analogous example... I have a form that has a drop-down of a list of "Widgets" (have that working, thanks to SO) ... and then there are other fields (Length/Height/Width) which have "default" values.

When the form displays, the Drop-Down is shown but the form fields of L/H/W are empty/disabled until the user selects one from the DDL.

Now, in clasic ASP.Net world, you would do a PostBack on the "onselectedindexchange" and that would look at the item selected, then update the L/H/W fields with values from the "master widget entry" version.

As MVC does not have post back... how is this achieved?

Answer

Shyju picture Shyju · Dec 2, 2015

In Asp.Net MVC, There is no postback behaviour like you had in the web forms when a control value is changed. You can still post the form and in the action method, you may read the selected value(posted value(s)) and load the values for your text boxes and render the page again. This is complete form posting. But there are better ways to do this using ajax so user won't experience the complete page reload.

What you do is, When user changes the dropdown, get the selected item value and make a call to your server to get the data you want to show in the input fields and set those.

Create a viewmodel for your page.

public class CreateViewModel
{
    public int Width { set; get; }
    public int Height{ set; get; }

    public List<SelectListItem> Widgets{ set; get; }

    public int? SelectedWidget { set; get; }    
}

Now in the GET action, We will create an object of this, Initialize the Widgets property and send to the view

public ActionResult Create()
{
  var vm=new CreateViewModel();
  //Hard coded for demo. You may replace with data form db.
  vm.Widgets = new List<SelectListItem>
            {
                new SelectListItem {Value = "1", Text = "Weather"},
                new SelectListItem {Value = "2", Text = "Messages"}
            };
 return View(vm);
}

And your create view which is strongly typed to CreateViewModel

@model ReplaceWithYourNamespaceHere.CreateViewModel
@using(Html.BeginForm())
{
    @Html.DropDownListFor(s => s.SelectedWidget, Model.Widgets, "Select");

    <div id = "editablePane" >
         @Html.TextBoxFor(s =>s. Width,new { @class ="myEditable", disabled="disabled"})
         @Html.TextBoxFor(s =>s. Height,new { @class ="myEditable", disabled="disabled"})
    </div>
}

The above code will render html markup for the SELECT element and 2 input text fields for Width and Height. ( Do a "view source" on the page and see)

Now we will have some jQuery code which listens to the change event of the SELECT element and reads the selected item value, Makes an ajax call to server to get the Height and Width for the selected widget.

<script type="text/javascript">
 $(function(){

      $("#SelectedWidget").change(function() {

            var t = $(this).val();

            if (t !== "") {               
                $.post("@Url.Action("GetDefault", "Home")?val=" + t, function(res) {
                    if (res.Success === "true") {

                      //enable the text boxes and set the value

                        $("#Width").prop('disabled', false).val(res.Data.Width);
                        $("#Height").prop('disabled', false).val(res.Data.Height);

                    } else {
                        alert("Error getting data!");
                    }
                });
            } else {
                //Let's clear the values and disable :)
                $("input.editableItems").val('').prop('disabled', true);
            }

        });
 });

</script>

We need to make sure that we have an action method called GetDetault inside the HomeController to handle the ajax call.

[HttpPost]
public ActionResult GetDefault(int? val)
{
    if (val != null)
    {
        //Values are hard coded for demo. you may replae with values 
       // coming from your db/service based on the passed in value ( val.Value)

        return Json(new { Success="true",Data = new { Width = 234, Height = 345}});
    }
    return Json(new { Success = "false" });
}