0

I am a ASP.NET developer that is trying to get to grips with MVC.

I'm creating a register page and I have 3 dropdowns.

Now the first dropdown must load first go to load the countries. Based on that I load the provinces and then based on that I load the cities in that province.

The trouble I'm having is that on the first change it keeps my selected value but on the second onchange it seems as everything is lost.

I load the reigster view by just loading countries so long. then the javascript posts it to an action.

cshtml

<label class="col-md-3 col-xs-5 control-label">Country:</label>
<div class="col-md-9 col-xs-7">
    @Html.DropDownListFor(x => x.CountryId, (IEnumerable<SelectListItem>)ViewBag.CountryItems, "Please Select", new { @class = "form-control select", @onchange = "CallChangefunc(this.value, null)" })
</div>
</div>
<div class="form-group">
<label class="col-md-3 col-xs-5 control-label">Province:</label>
<div class="col-md-9 col-xs-7">
        @Html.DropDownListFor(x => x.ProvinceId, (IEnumerable<SelectListItem>)ViewBag.ProvinceItems, "Please Select", new { @class = "form-control select", @onchange = "CallChangefunc(null, this.value)" })
</div>
</div>
<div class="form-group">
<label class="col-md-3 col-xs-5 control-label">City:</label>
<div class="col-md-9 col-xs-7">
    @Html.DropDownListFor(x => x.CityId, (IEnumerable<SelectListItem>)ViewBag.CityItems, "Please Select", new { @class = "form-control select" })
</div>
</div>

The javascript

function CallChangefunc(countryId, provinceId) {
    window.location.href = "/Master/SetDropDowns?provinceId=" + provinceId + "&countryId=" + countryId ;
}

First one "Register" loads 1'st then SetDropDowns gets called onchanged.

I load the viewbags(SelectedItemLists) otherwise I get errors on refresh. is there a better way to code the viewbags to not have in two places?

[HttpGet]
    public ActionResult Register()
    {
        IEnumerable<SelectListItem> CountryItems = BusinessAPI.CountryManager.GetAllCountries().Select(ci => new SelectListItem
            {
                Value = ci.Id.ToString(),
                Text = ci.Name
            });

        ViewBag.CountryItems = CountryItems;

        IEnumerable<SelectListItem> ProvinceItems = BusinessAPI.ProvinceManager.GetAllProvincesByCountryId(0).Select(ci => new SelectListItem
        {
            Value = ci.Id.ToString(),
            Text = ci.Name
        });

        ViewBag.ProvinceItems = ProvinceItems;

        IEnumerable<SelectListItem> CityItems = BusinessAPI.CityManager.GetAllCitiesByProvinceId(0).Select(ci => new SelectListItem
        {
            Value = ci.Id.ToString(),
            Text = ci.Name
        });

        ViewBag.CityItems = CityItems;

        return View();
    }

    public ActionResult SetDropDowns(string provinceId, string countryId)
    {
        IEnumerable<SelectListItem> CountryItems = BusinessAPI.CountryManager.GetAllCountries().Select(ci => new SelectListItem
        {
            Value = ci.Id.ToString(),
            Text = ci.Name
        });

        ViewBag.CountryItems = CountryItems;

        int countId = 0;
        if (countryId == "null")
            countryId = string.Empty;

        if (TempData["CountryId"] == null)
        {
            if (!string.IsNullOrEmpty(countryId))
            {
                countId = Convert.ToInt32(countryId);
                TempData["CountryId"] = countId;
            }
        }
        else
            countId = Convert.ToInt32(TempData["ProvinceId"]);

        IEnumerable<SelectListItem> ProvinceItems = BusinessAPI.ProvinceManager.GetAllProvincesByCountryId(Convert.ToInt32(countId)).Select(ci => new SelectListItem
        {
            Value = ci.Id.ToString(),
            Text = ci.Name
        });

        ViewBag.ProvinceItems = ProvinceItems;

        int provId = 0;
        if (provinceId == "null")
            provinceId = string.Empty;

        if (TempData["ProvinceId"] == null)
        {
            if (!string.IsNullOrEmpty(provinceId))
            {
                provId = Convert.ToInt32(provinceId);
                TempData["ProvinceId"] = provId;
            }
        }
        else
            provId = Convert.ToInt32(TempData["ProvinceId"]);

        IEnumerable<SelectListItem> CityItems = BusinessAPI.CityManager.GetAllCitiesByProvinceId(provId).Select(ci => new SelectListItem
        {
            Value = ci.Id.ToString(),
            Text = ci.Name
        });

        ViewBag.CityItems = CityItems;

        return View("Register");
    }
Jorge Y. C. Rodriguez
  • 3,394
  • 5
  • 38
  • 61
JonathanM
  • 61
  • 10
  • @JohnatanM I think my question can help you as I had a similar [problem](http://stackoverflow.com/questions/26835394/select-dropdownlist-is-not-displaying) – Enzero Apr 16 '15 at 07:14

1 Answers1

1

The problem is that you don't tell them to be selected, modify your lists that have SelectListItem like the example below, to tell what item from list is Selected.

IEnumerable<SelectListItem> CountryItems = BusinessAPI.CountryManager.GetAllCountries().Select(ci => new SelectListItem
{
    Value = ci.Id.ToString(),
    Text = ci.Name,
    Selected = ci.id.ToString() == countryId // if match the condition is selected
});

Also, to keep both listed selected you can modify your View. Modify the javascript function to send always the countryId. Doing this the current country will be always selected, even when you change province.

function CallChangefunc(provinceId) {
    var countries = document.getElementById("CountryId");
    var countryId = countries.options[countries.selectedIndex].value; 
    window.location.href = "/Master/SetDropDowns?provinceId=" + provinceId + "&countryId=" + countryId ;
}

Notice that on first dropdown CallChangefunc(null), with this we tell that we don't have a province selected, which is true.

<div class="col-md-9 col-xs-7">
    @Html.DropDownListFor(x => x.CountryId, (IEnumerable<SelectListItem>)ViewBag.CountryItems, "Please Select", new { @class = "form-control select", @onchange = "CallChangefunc(null)" })
</div>

On second dropdown we send CallChangefunc(this.value), with this we tell what province was selected to take cities and to keep the current value selected after postback. And because the countryId is always send it will remain unchanged.

<div class="col-md-9 col-xs-7">
    @Html.DropDownListFor(x => x.ProvinceId, (IEnumerable<SelectListItem>)ViewBag.ProvinceItems, "Please Select", new { @class = "form-control select", @onchange = "CallChangefunc(this.value)" })
</div>
adricadar
  • 9,971
  • 5
  • 33
  • 46
  • Hi adricadar. This makes sense. But on the first post back then I still have everything in my viewmodel and the country stays selected but the second time its gone. should I be storing the viewmodel in a session if I want to really do multiple postbacks? – JonathanM Apr 16 '15 at 06:53
  • @JonathanM see my update, you have to send all the time `countryId`. – adricadar Apr 16 '15 at 07:01
  • That looks like the code I need. I will try it tonight when I get home again and keep you posted. Thanks. 1 more thing - do you recommend I keep the viewmodel in a session so that values that have already been filled out stays in the form? – JonathanM Apr 16 '15 at 07:08
  • @JonathanM If you want to **cache** your items, i recommend you to look on this [answer](http://stackoverflow.com/a/29644863/3790486) where i give some solutions for this, and i'm sure at least 1 will fit your needs :)(or a combination). Just by a glance `OutputCache` will fit pretty well, and if you use `Cache` for countries items, you are perfect :). – adricadar Apr 16 '15 at 07:11