1
\$\begingroup\$

In this scenario I've got a textbox to which I have added autocomplete functionality. The kicker is that I want to dynamically change the source option of the AJAX call based on the value of a dropdown list. To do this I encapsulated the differences between the AJAX calls in an object of type Resolver. The name property of the Resolver object is the same as the text value coming from the dropdown list. I was looking for a way to make this a little cleaner. The code works, just looking for more extensible options as the project grows.

$(document).ready(function ()
    {
        //encapsulate the URL of the AJAX call
        //the name property maps to a dropdown list value
        //so if Drugs is chosen from the dropdown menu we look for the name property which = drugs
        Resolver = function (name, url)
        {
            this.name = name;
            this.url = url;

        }
        Resolver.prototype.resolve = function (request, response)
        {
            $.ajax(
            {
                type: "POST",
                url: "Service.asmx/" + this.url,
                data: JSON.stringify({ 'param': $('#auto').val() }),
                dataType: "json",
                contentType: "application/json",
                success: function (data)
                {
                    response(data.d);
                },
                error: function (xhr)
                {
                    console.log(xhr.status);
                }
            });
        }
        //in the future I expect the Resolver object to have more properties
        //this is the simplest example I could think of
        var drugs = new Resolver('drugs', 'GetDrugNames');
        var hospitals = new Resolver('hospitals', 'GetHospitalNames');
        var resolverArray = [];
        resolverArray.push(drugs);
        resolverArray.push(hospitals);
        //on the change even of the ddl we're looking for the index of the resolverArray
        //which has the name name property as the value selected in the ddl.
        $('#ddl').change(function ()
        {
            var target = $(this).val().toLowerCase();
            var index = 0;
            $.each(resolverArray, function (ix, val)
            {
                if (resolverArray[ix].name == target)
                {
                    index = ix;
                }
            });
            $('#auto').autocomplete(
            {
                source: function (request, response)
                {
                    resolverArray[index].resolve(request, response);
                }
            });

        });
    });
\$\endgroup\$
  • \$\begingroup\$ You needn't, and probably shouldn't, call JSON.stringify when passing an object as the data property to jQ's $.ajax, jQ will do that for you \$\endgroup\$ – Elias Van Ootegem Oct 8 '13 at 9:52
  • \$\begingroup\$ @EliasVanOotegem how else will this work if the data property of the ajax call doesn't know what the name of the parameter in the web method is? \$\endgroup\$ – wootscootinboogie Oct 8 '13 at 12:36
  • \$\begingroup\$ passing { param: $('#auto').val() } to the data property will sent the $('#auto').val() value as $_POST['param']... the request will look like this: param=<encodeURI($('#auto').val()>, so no need to decode/parse the data \$\endgroup\$ – Elias Van Ootegem Oct 8 '13 at 14:53
  • \$\begingroup\$ @EliasVanOotegem I couldn't get { param: $('#auto').val() } to work. I'm using .NET 4.0 and get an InvalidJsonPrimitive error. \$\endgroup\$ – wootscootinboogie Oct 8 '13 at 15:25
  • \$\begingroup\$ That's because jQ doesn't send its data as JSON by default, but defaults to the x-www-form-urlencoded mime \$\endgroup\$ – Elias Van Ootegem Oct 8 '13 at 15:35
2
\$\begingroup\$

One way to make it extensible is to make it reusable rather than stick it to one dropdown. You can even make it into a jQuery plugin so that it looks like this:

$('.someAutocompleteBox').dependentAutoComplete({

  /* the selector of the element to monitor changes */
  elementToMonitor : '.theElementToMonitor',

  /* value set, which correspond to the value of the previous input */
  sets : {
    drugs : DRUG_URL,
    hospitals : HOSPITAL_URL,
  }

  /* you can monitor depending on the event and which value to grab */
  triggeringEvent : 'keyup',
  propertyToCheck : 'value'
});

So in this example, I set .someAutocompleteBox to change sets when a keyup event is triggered from .theElementToMonitor. Then the plugin will grab the property defined in propertyToCheck and check it against the sets object which URL to use as source. Pretty simple isn't it?

As for jQuery plugin creation, you can use the jQuery Boilerplate to start off with a template. Everything else is self-explanatory, which is:

  • In the init() function of your plugin code, attach an event stated by triggeringEvent to the element stated by elementToMonitor.

  • When the event is fired, the handler should grab the value of the property defined in propertyToCheck.

  • Do a lookup from the passed sets property and grab the URL of the corresponding key (which is the value grabbed from the property earlier)

  • Update the current element's autocomplete source.

The plugin code (in the init) should look something like this:

init: function () {
  // nothing to check, nothing to check against
  if(!this.settings.elementToMonitor || !this.settings.sets) return;

  // Save the context
  var instance = this;

  // Attach the defined event to defined dependency
  $(this.settings.elementToMonitor)
    .on(this.settings.triggeringEvent,function(){

      // Get the value at defined property
      var value = $(this).prop(instance.settings.propertyToCheck);
      var url;

      // Check if we have that url in the set, return if not
      if(!(url = instance.set[value])) return;

      // Update the source accordingly
      $(instance.element).autocomplete({
        source = url;
      });
    });
}
\$\endgroup\$
  • \$\begingroup\$ I see several things I can use in here to improve upon. Great stuff! \$\endgroup\$ – wootscootinboogie Oct 8 '13 at 12:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.