MVC DropDownList OnChange to update other form fields
Asked Answered
C

3

18

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?

Condensable answered 2/12, 2015 at 12:53 Comment(7)
make a Controller action that return Json data and make ajax call onchange of dropdown to that action. On ajax response (json) u will get values then set those values to fields form json response.Stroman
You can POST to an action method to achieve the same results, but here's an example of how to do with javascriptLaruelarum
Depends do you want to do it on the view itself, or should you actually query something from the back-end?San
Ah...OK... so I have to explicit execute an ajax request to the controller (with the selected value) to then pull back the default L/H/W valuesCondensable
Yes. need to make ajax call.Stroman
That is certainly one way. In fact we did it so often we created a jQuery plugin to do it for our related dropdowns, but it is just Ajax calls and returning JSON data.Can
@ParthTrivedi: please add your initial comment as an answer.Grained
A
27

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" });
}
Ailsa answered 2/12, 2015 at 16:58 Comment(3)
Brilliantly explained. Thank you!Heigl
It works until we hit the res.Success... which comes through as undefined. I think this is because the json is in an unexpected format. I have not been able to overcome this.Seamark
I got it working for c# MVC 5. I am making a post below.Seamark
S
4
  1. Make a Controller "Action" that return "Json" data.
  2. Make Ajax call "onchange" of dropdown to that "Action".
  3. On ajax "response" (json) u will get values then set those values to fields from json response.

This is the way to update field values.

Stroman answered 3/12, 2015 at 5:42 Comment(1)
Thanks Parth... Kudos to you as you did provide the answer first (in comments) but Shyju posted up a full and complete example, hence that one was marked as the answer.Condensable
S
0

Shyju made a brilliant post in 2015 but I had to update it to make it work for MVC 5. I worked with one of my progammers (I'm an IT manager) to create this. You need to create a class to represent the dropdown and the Height and Width.

public class AjaxText
{

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

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

    public int? SelectedWidget { set; get; }

}

In my HomeController.cs, the GET action will create an object of this, initialize the Widgets property and send to the view.

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

}

And your create view 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)

@model AjaxText

@{
    ViewData["Title"] = "AjaxText";
}
<h1>@ViewData["Title"]</h1>
@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>

Now we will have some 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. I added some alerts to help you debug.

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

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

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

            if (t !== "") 
            {               
                $.ajax({
                    type: 'POST',
                    datatype: 'json',
                    url: '/Home/GetDefault?val=' + t,
                    success: function (bbb) {
                        alert(t);
                        alert(bbb.success);
                        alert(bbb.info.height);
                        $("#Width").prop('disabled', false).val(res.Data.Width);
                        $("#Height").prop('disabled', false).val(res.Data.Height);

                    },
                    error: function (msg) {
                        alert("error");
                    }
                });
            } else {
                //Let's clear the values and disable :)
                $("input.editableItems").val('').prop('disabled', true);
            }

        });
 });

</script>

And in my home controller, the Post is done almost the same as how Shyju did it, but success doesn't have quotes around true and false. And you don't have to use the word data... info or whatever will work too. But keep it lowercase to maintain your sanity.

[HttpPost]
public JsonResult 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, info = new { width = 234, height = 345 } });
    }
    return Json(new { Success = false });
}

I'm sure there are better ways to do this. This is what worked for us. Cheers and enjoy your coding experience! :)

Seamark answered 30/9, 2022 at 19:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.