Alex
Alex

Reputation: 2062

Typeahead - Unable to use dynamically created array as source of data

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()
    });

Upvotes: 1

Views: 639

Answers (1)

Michael Doye
Michael Doye

Reputation: 8171

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

Upvotes: 1

Related Questions