5

I have a select box (Specie) and a typeAhead input field(Breed), Which i need to update on the change of selectbox, I have the following code.

<div class="col-lg-4 col-md-4 col-sm-12 col-xs-12 profile-fields-margin-bottom">
    <select class="form-control select_field_style specie-breed" id="species" name="species" required>
        <option disabled selected>Select a Species</option>
        <option value="Dog">Dog</option>
        <option value="Cat">Cat</option>
        <option value="Horse">Horse</option>
    </select>
</div>

<div class="col-lg-4 col-md-4 col-sm-12 col-xs-12 profile-fields-margin-bottom">
    <input id="breed" type="text" class="form-control charsonly" name="breed" placeholder="Breed">
</div>


$(document).on('change', '.specie-breed', function() {
    let specie = this.value;
    $('#breed').typeahead({
        source:  function (query, process) {
            return $.get('/get/breeds/' + specie, { query: query }, function (data) {
                console.log(data);
                return process(data);

            });
        }
    });
});

Its working but for the first time, The second time change doesn't change the values of the typeahead,

What am i missing here ?

Gammer
  • 5,453
  • 20
  • 78
  • 121

2 Answers2

5

Its working but for the first time, The second time change doesn't change the values of the typeahead,

I think that's because the second, third, etc. time, the AJAX URL doesn't get changed anymore; and that's because the specie remains as the value it was initially assigned to.

I mean, let's say you selected Cat from the drop-down menu; so the first change event of that menu gets triggered, and specie is set to Cat. However, when later you selected Horse (or Dog), the specie from within the closure for the source option remains as Cat.

This Pen may help you understand it.

So a simple fix would be to reference that specie to this and not this.value.

$(document).on('change', '.specie-breed2', function() {
    let specie = this; // Reference the Element.
    $('#breed').typeahead({
        source:  function (query, process) {
            return $.get('/get/breeds/' + specie.value, { query: query }, function (data) {
                console.log(data);
                return process(data);

            });
        }
    });
});

Or you can do it like this:

$('#breed').typeahead({
    source:  function (query, process) {
        if ( ! $( '#species' ).val() ) {
          return;
        }

        return $.get('/get/breeds/' + $( '#species' ).val(), { query: query }, function (data) {
            console.log(data);
            return process(data);

        });
    }
});

UPDATE

The actual solution, or what you're missing in your code, (I think) is: Destroy typeahead on the #breed field before re-initializing typeahead on that field.

$(document).on('change', '.specie-breed', function() {
    let specie = this.value;

    // Destroy existing instance.
    $('#breed').typeahead( 'destroy' );

    // (Re-)Initialize `typeahead`.
    $('#breed').typeahead({
        source:  function (query, process) {
            return $.get('/get/breeds/' + specie, { query: query }, function (data) {
                console.log(data);
                return process(data);

            });
        }
    });
});

That way, specie can be assigned or referenced to the this.value (i.e. the currently selected "Species"), and you'd get the correct value (e.g. Cat or Dog).

See jQuery#typeahead('destroy').

Sally CJ
  • 15,362
  • 2
  • 16
  • 34
  • 2
    The problem that remains in your first code snippet is that every time the value changes in the drop down list, the typeahead plugin is called, as if trying to reinitialize it (which doesn't work). You corrected this in your second snippet where you completely eliminated the unnecessary event handler. – gilly3 May 11 '18 at 15:01
  • @gilly3 Yes, I was actually aware of that. I myself would probably use the second snippet or that I would not re-initialize `typeahead` on the same `input`/field. But it might be necessary for the OP to do the re-initialization, hence (in the updated answer) I suggested to destroy the `typeahead` instance before re-initializing it on the `#breed` field. – Sally CJ May 11 '18 at 16:10
1

According to the docs typeahead's source function can either accept two or three parameters. If you only use two, the second one, called process in your code, must be called with the result synchronously. In your code however, that callback is only served after an async AJAX request.

You have a few options here:

a) Use a synchronous/blocking GET. This is good for testing, but shouldn't be considered a real solution.

b) Use the source function's third parameter.

Please try this:

$(document).on('change', '.specie-breed', function() {
    let specie = this.value;
    $('#breed').typeahead({
        source:  function (query, syncResults, asyncResults) {
            $.get('/get/breeds/' + specie, { query: query }, function (data) {
                console.log(data);
                asyncResults(data);
            });
        },
        async: true
    });
});

If that doesn't help, make sure your AJAX requests are successful. The default handler on $.get works only for success but not for errors. In particular that query containing object looks suspicious on a GET request. Please try this line out in the console while your page is open:

$.get('/get/breeds/' + specie, { query: query }, function(data) {
    console.log(data);
});

If that doesn't print your data, then you need to fix this part first.

Hubert Grzeskowiak
  • 15,137
  • 5
  • 57
  • 74