Question

There are many textboxes (Django CharField) for which I am using tokenInput as the JS plugin for autocomplete feature. To intialize each of them, I need to set prepopulate with different tag values. I want to avoid following code duplication as shown below.

$("#id_tags_1").tokenInput("/xyz/tag_search/", {
    theme: "facebook",
    onAdd: function(item){
        tag_ids.push(item.id);
        $("#id_tag_domain").val(tag_ids.join(','));
    },
    onDelete: function(item){
        tag_ids = _.without(tag_ids, item.id);
        $("#id_tag_domain").val(tag_ids.join(','));
    },
    preventDuplicates: true,
    tokenLimit: 3,
    prePopulate: tags_val[0],
});
$("#id_tags_2").tokenInput("/xyz/tag_search/", {
    theme: "facebook",
    onAdd: function(item){
        tag_ids.push(item.id);
        $("#id_tag_domain").val(tag_ids.join(','));
    },
    onDelete: function(item){
        tag_ids = _.without(tag_ids, item.id);
        $("#id_tag_domain").val(tag_ids.join(','));
    },
    preventDuplicates: true,
    tokenLimit: 3,
    prePopulate: tags_val[1],
});

I came across another SO post where regular expression like this [^id_tags_] was suggested but I don't know how to select each tag value corresponding to different tags. I think part of the problem is that I don't know how to access the tag id else I could have split it and extracted the number. If there is any other elegant solution, please do suggest.

Was it helpful?

Solution

Right, you can select them with $("[id^=id_tags_]") (an attribute-starts-with-selector). In the callbacks, if the plug-in is like standard plugins, this will refer to the element on which the original event causing the callback to occur was raised. So (again, if that plugin behaves in the normal way), within the callbacks, this.id should be the id of the element on which the event occured. But I don't know that plugin, sometimes plugins pass that information in a different way.

If that plugin doesn't provide access to the element on which the originating event occurred, you can still de-duplicate the code by using each and using the closure created by the each iterator function to remember the id:

$("[id^=id_tags_]").each(function() {
    // Get the index from the element `id`.
    // Note: If your elements are always in *document* order, instead
    // of this next line, you could just accept `index` as an argument,
    // it'll be `0` for the first matching element, `1` for the next,
    // etc. But I'm not assuming the elements are in document order, so
    // I'm deriving the index from the `id`.
    var index = parseInt(this.id.replace(/\D/g, ''), 10) - 1;

    $(this).tokenInput("/xyz/tag_search/", {
        theme: "facebook",
        onAdd: function(item){
            tag_ids.push(item.id);
            $("#id_tag_domain").val(tag_ids.join(','));
        },
        onDelete: function(item){
            tag_ids = _.without(tag_ids, item.id);
            $("#id_tag_domain").val(tag_ids.join(','));
        },
        preventDuplicates: true,
        tokenLimit: 3,
        prePopulate: tags_val[index],   // <=== Use `index` here (I assume this is the place)
    });
});

In that, we create new tagInput callbacks for each element in the each iterator function, and those are closures over the call to the iterator function, and so they have access to the id local variable (which will be specific to each call, and therefore to each element). If the term "closure" seems a bit alien, don't worry, closures are not complicated.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top