1

I have a json file which consists of countries, and cities per country, in the following structure:

{
  "United States": [
    "New York",
    "Los Angeles",
    "San Francisco",
    "Baltimore",
    "Washington"
  ],
  "England": [
    "Manchester",
    "Liverpool",
    "Newcastle",
    "Bradford",
    "London"
  ]
}

On page load I extract the countries and push them into an array. On country select, I create an array of cities according to the selected country. The code works with a pre-defined array, but not with the newly created one. Any ideas?

Jsfiddle here.

HTML:

<div id="countryContainer">
    <label>Heroes from country:</label>
    <select multiple="multiple" data-placeholder="Select a country" id="countryList"></select>
</div>
<br><br>
<input type="text" id="search">

The JS code is a bit long:

    $(document).ready(function () {
        var source = [];
        $.ajax({
            url: "https://api.myjson.com/bins/22mnl",
            dataType: "json",
            success: function (response) {
                response1 = response;

                var keys = Object.keys(response);
                keys.forEach(function (key) {
                    source.push(key);
                });
                console.log(source);
                for (var i = 0; i < source.length; i++) {
                    var option1 = $('<option value=' + source[i] + '>' + source[i] + '</option>');
                    $("#countryList").append(option1);
                }
            }
        });
    });

    $('#countryList').select2();
    var citiesArray = [];
    var citiesObjArr = [];

    //creation of array of countries from the json
    $("#countryList").on("change", function () {
        if ($("#countryContainer li").length > 1) {
            console.log("changed");
            var selectedCountry = $("#countryList option:selected")[0].innerHTML;
            console.log("country selected: " + selectedCountry);
            citiesObjArr.length = 0;
            citiesArray.length = 0;
            createArrayOfCities(selectedCountry);
        } else {
            console.log("empty");
        }
    });

    //extraction of cities per selected country from the original json and insertion into an array of objects
    function createArrayOfCities(key) {
        $.ajax({
            url: "https://api.myjson.com/bins/22mnl",
            dataType: "json",
            success: function (response) {
                if (response.hasOwnProperty(key)) {
                    var j = 0;
                    var i = 0;
                    response[key].forEach(function (i) {
                        citiesArray[j] = i;
                        //CREATION OF THE CITIES OBJECTS ARRAY
                        citiesObjArr.push({
                            val: i
                        });
                        j++;
                    });
                }
                console.log(citiesObjArr);
                console.log(typeof (citiesObjArr));
            }
        });
    }

    //typeahead autocomplete here

//THIS ARRAY OF OBJECTS WORK, BUT citiesObjArr DOES NOT. WHY??

/*
    var data1 = [{
        val: "city1111"
    }, {
        val: "city2222",
    }];
    */

    var titles = new Bloodhound({
        datumTokenizer: function (data) {
            return Bloodhound.tokenizers.whitespace(data.val);
        },
        queryTokenizer: Bloodhound.tokenizers.whitespace,
        local: citiesObjArr
    });
    titles.initialize();
    $("#search").typeahead({
        highlight: true
    }, {
        name: 'titles',
        displayKey: 'val',
        source: titles.ttAdapter()
    });

1 Answer 1

1

The problem is here is that you are loosing access to citiesObjArr outside of your success callback. To get around this, you can defer your response object:

Inside your change handler, you can do the following:

$.when(createArrayOfCities())
  .then(function(data) {
      // Pass in the response and selected country to a new function
      handleResponse(data, selectedCountry);
   });

You will need to change your createArrayOfCities() function to the following:

function createArrayOfCities() {
  return $.ajax({
      url: "https://api.myjson.com/bins/22mnl",
      dataType: "json",
      success: function (response) {
          return response;
      }
  });
}

NOTE: you might want to change the name of the above function for readability (since it no longer builds the array of cities)

And then you can call the new handleResponse function:

function handleResponse(response, key) {

  if (response.hasOwnProperty(key)) {
    var j = 0;
    var i = 0;
    response[key].forEach(function (i) {
       citiesArray[j] = i;
       ////CREATION OF THE CITIES OBJECTS ARRAY
       citiesObjArr.push({
          val: i
       });
       j++;
    });
  };

  var titles = new Bloodhound({
    datumTokenizer: function (data) {
        return Bloodhound.tokenizers.whitespace(data.val);
    },
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    local: citiesObjArr
  });

  titles.initialize();
  $("#search").typeahead({
    highlight: true
  }, {
    name: 'titles',
    displayKey: 'val',
    source: titles.ttAdapter()
  });

}

Here is your updated Fiddle

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the help, works beautifully. Do you have any idea why typeahead "remembers" previous selections? When I switch between the two countries, the previous country suggestion are still present. - EDIT: I managed to overcome this issue by deleting tt-menu element and setting the input hint to transparent. I wonder if there's a better way of achieving the same result.

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.