Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to dynamically ajax add elements through jquery chosen plugin?

I am trying to use "Chosen" plugin by harvest (http://harvesthq.github.com/chosen/) and it works for the static set of options I am passing. However, what I want is that whenever anybody types something that is not in the pre-filled options, then it should send that to the server as a new option and on successful response, I want to not only add that to the valid list of options, but also make it select it.

Reloading the options is fairly simple:

// In ajax response
var newOption = new Option("Text", __value__);
$("#tagSelection").append(newOption);
$("#tagSelection").trigger("liszt:updated");

However, I don't know how to make "Chosen" plugin pick this as the value. I would love to do something like

$("#tagSelection").trigger("liszt:select:__value__"); 

or something similar.

Any suggestions?

(ps: I am trying to build a "tagging" plugin based on chosen. So, if the tag being typed doesn't exist, it will add it to the server and then select it straight away.)

like image 395
Shreeni Avatar asked Mar 13 '12 11:03

Shreeni


5 Answers

I've just been doing this. I didn't like Shreeni's line for setting selected (no offence) so i went with

$('#tagSelection').append(
    $('<option></option>')
        .val(data.Item.Id)
        .html(data.Item.Value)
        .attr("selected", "selected"));
$("#tagSelection").trigger("liszt:updated");

which i personally think is a bit cleaner (tested with multiselect box and it works fine)

like image 70
Manatherin Avatar answered Nov 03 '22 18:11

Manatherin


These are the complete set of changes I did to the chosen plugin (jquery version) to solve this problem

Chosen.prototype.choice_build = function(item) {
  this.new_term_to_be_added = null; 
  // ....
};

Chosen.prototype.no_results = function(terms) {
  // ....
  no_results_html.find("span").first().html(terms);
  this.new_term_to_be_added = terms;
  return this.search_results.append(no_results_html);
};


Chosen.prototype.keydown_checker = function(evt) {
       // ...
        case 13:
          if(this.new_term_to_be_added != null &&  this.options.addNewElementCallback != null) {
              var newElement = this.options.addNewElementCallback(this.new_term_to_be_added);


              if(newElement!=null && newElement.length == 1) {
                  // KEY TO SOLVING THIS PROBLEM
                  this.result_highlight = newElement;
                  // This will automatically trigger the change/select events also. 
                  // Nothing more required.
                  this.result_select(evt);
              }
              this.new_term_to_be_added = null;
          }
          evt.preventDefault();
          break;
          // ...
};

The this.new_term_to_be_added maintains the currently typed string which is not among the pre-defined options.

The options.addNewElementCallback is the callback to the calling function to allow them to process it (send it to server etc.) and it has to be synchronous. Below is the skeleton:

var addTagCallback = function(tagText) {
    var newElement = null;
    $.ajax({url : that.actionUrl, 
        async : false, 
        dataType: "json",
        data : { // ....},
        success : function(response) {
        if(response) {
                            $('#tagSelection').append(
                                $('<option></option>')
                                      .val(data.Item.Id)
                                      .html(data.Item.Value)
                                      .attr("selected", "selected"));
            $("#tagSelection").trigger("liszt:updated");
            // Get the element - will necessarily be the last element - so the following selector will work
            newElement = $("#tagSelection_chzn li#tagSelection_chzn_o_" + ($("#tagSelection option").length - 1));
        } else {
            // Handle Error
        }
    }});
    return newElement;
};

The newElement is a jquery element - the latest added li object to list of options.

By doing this.result_highlight = newElement; and this.result_select(evt); I tell the Chosen plugin to select this. This works whether it is a single select or a multi-select. Rifky's solution will work only for single select.

like image 10
Shreeni Avatar answered Nov 03 '22 19:11

Shreeni


A fork containing the feature you want has been created here. This fork is referred to as the koenpunt fork.

You can follow through the discussion for this feature on the harvesthq github site

  • https://github.com/harvesthq/chosen/pull/506
  • https://github.com/harvesthq/chosen/pull/320
  • https://github.com/harvesthq/chosen/pull/166

My summary of what happened:

  1. Many people want this feature
  2. A few people have created pull-requests with a proposed solution
  3. The best solution is pull-request 166
  4. The author of 'chosen' decided he won't include the feature
  5. koenpunt (the author of pull-request 166) created a fork of 'chosen'
  6. People have started abandoning the harvesthq version of 'chosen' in favor of the koenpunt fork
like image 6
lance-java Avatar answered Nov 03 '22 18:11

lance-java


Since version 1.0 some of the suggestions above are not relevant any longer. Here is all that is needed:

--- /home/lauri/Downloads/chosen_v1.0.0 (original)/chosen.jquery.js
+++ /home/lauri/Downloads/chosen_v1.0.0 (modified)/chosen.jquery.js
@@ -408,8 +408,18 @@
           break;
         case 13:
           evt.preventDefault();
-          if (this.results_showing) {
+          if (this.results_showing && this.result_highlight) {
             return this.result_select(evt);
+          }
+          var new_term_to_be_added = this.search_field.val();
+          if(new_term_to_be_added && this.options.add_term_callback != null) {
+              var newTermID = this.options.add_term_callback(new_term_to_be_added);
+              if(newTermID) {
+                this.form_field_jq.append(
+                  $('<option></option>').val(newTermID).html(new_term_to_be_added).attr("selected", "selected")
+                );
+                this.form_field_jq.trigger("chosen:updated");
+              }
           }
           break;
         case 27:

The options are as follows:

{add_term_callback: addTagCallback, no_results_text:'Press Enter to add:'}

This means that if "Orange" is not in the list of fruits, it would simply prompt:

Press Enter to add: "Orange"

The callback should register the new term by whatever means necessary and return the ID (or value in the context of the underlying select element) for the new option.

var addTagCallback = function(tagText) {
  // do some synchronous AJAX 
  return tagID;
};
like image 4
LauriE Avatar answered Nov 03 '22 18:11

LauriE


I do it simply this way and it works perfectly:

// this variable can be filled by an AJAX call or so
var optionsArray = [ 
    {"id": 1, "name": "foo"}, 
    {"id": 2, "name": "bar"}
];

var myOptions = "<option value></option>";
for(var i=0; i<optionsArray.length; i++){
    myOptions +=  '<option value="'+optionsArray[i].id+'">'+optionsArray[i].name+'</option>';
}

// uses new "chosen" names instead of "liszt"
$(".chosen-select").html(myOptions).chosen().trigger("chosen:updated");
like image 1
artgrohe Avatar answered Nov 03 '22 17:11

artgrohe