Jquery select on change
Asked Answered
H

2

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 ?

Hypochondrium answered 8/5, 2018 at 8:28 Comment(15)
It sounds like you may need to use a delegated event handler. Is the process() function re-creating the .specie-breed element?Acinaciform
You're actually creating a new typeahead instance for every single onchange event fired. That's going to slow the browser down tremendously after awhile.Menken
@RoryMcCrossan Yeah process() function re-creating the .specie-breed element.Hypochondrium
@Menken then whats the best approach >Hypochondrium
Use a delegated event handler. See the duplicate I marked for more information.Acinaciform
@Menken it should be fine. The browser should garbage collect any data associated with removed elements. Unless the OP is doing this several hundred thousand times performance shouldn't be an issue.Acinaciform
@RoryMcCrossan , Same result with $(document.body).on('change', '.specie-breed', function(e) {.Hypochondrium
Try just $(document).on(... also, check the console for errorsAcinaciform
@RoryMcCrossan exact same thing with no console errorsHypochondrium
If you can edit the question to show your updated code, I'll re-open it for youAcinaciform
@RoryMcCrossan, Question updated.Hypochondrium
@RoryMcCrossan Didn't got anything from the community.Hypochondrium
Could you please add HTML content or add code snippet ?Bookseller
@AlpeshJikadra Question updated.Hypochondrium
Could you please add complete url instead of /get/breeds/ ?Bookseller
L
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').

Logician answered 11/5, 2018 at 8:39 Comment(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.Autostability
@Autostability 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.Logician
D
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.

Diskin answered 10/5, 2018 at 9:21 Comment(4)
Uncaught TypeError: asyncResults is not a function. AJAX call is fine there.Hypochondrium
But getting the above exception.Hypochondrium
I've added an explicit async option. Does that help? According to the docs that option should be inferred from the source signature.Diskin
@Hypochondrium any chance you could put together a runnable sample? With full URL to that API and all. It's hard to debug without trying out things.Diskin

© 2022 - 2024 — McMap. All rights reserved.