Selection template function and ajax data source


#1

Hi! So far I am enjoying using this excellent plugin but have run into a problem. I’m using version 4.0.7. I’m not really sure how I can provide a JSBin sample to demonstrate, as I’m using an ajax source.

I’m building a “messenger” type application that uses select2 to “search as you type” for contacts in “To:” and “CC:” fields, getting the search matches via ajax. This works without problems. However I’d like to be able to have certain contacts prepopulated (sent as JSON from the server) when the form initializes.

I have a separate functions for the selection and result formatting which use a client-side template (doT.js). The options are:

{
	 multiple:true
	,debug:true
	,placeholder:"Type Last Name or PIN"
	,minimumInputLength:2
	,width:"100%"
	,templateSelection:function(personData){return selectionFormatFunction(personData);}
	,templateResult:function(personData){return resultFormatFunction(personData);}			
	,ajax: {
	     url: "../searchPersons"
	    ,method:"GET"
	    ,dataType:"json"
	    ,delay:250
	}
}

Before Select2 initiales for these two fields, I already have the JSON for the one or more “pre-populated” items. Your documentation says when an ajax source is used, you must add it as a new Option element via javascript, then trigger the select.onchange event (which cause the result format function to run), then trigger the “select2:select” event (which causes the select format function to run).

,prefillItems:function(arrData,$el){
	var dataItm=null,optItm=null;
	if(arrData.length>0){
		for(var i=0,j=arrData.length; i<j; i++){
		  	dataItm=arrData[i];
		  	optItm=new Option(dataItm.text,dataItm.id,true,true);
		  	$el.append(optItm).trigger("change");
		  	$el.trigger({
		  		type:"select2:select"
		  		,params:{data:dataItm}
		  	});
		};
	}
}

After select2 is initialized on the “cc” field, “prefillItems” runs. It works in that I get the proper HTML for the “selection item” in the selection field (apparently the selection template runs OK), but only the “id” and “text” were made available to the template, the rest of the data is “undefined”. (The rest of this data is details about the persons location, title, etc, which appears in both the results and as a tooltip on the selection. This works without problem for things I select via the “as you type” search.)

I found that in the ‘prefillItems’ function:

  1. simply by appending the option element to the select (and not triggering the ‘change’ event), the “selectionFormat” function runs. The ‘resultFormat’ function does not.
  2. Before appending the option, I can add attributes to the element, and they will show up in the hidden select element.
  3. I tried both the regular sel.onchange and the select2 sel.change.select2 event. The ‘formatResult’ function didn’t run.
  4. I also tried using the jquery trigger({event object}), way, but the ‘formatResult’ function is not triggered.
  5. subsequent searches and selections for the same field will function normally, both result and selection format function run with the extra data passed.

Is it expected that because I appended an option element created with the javascript constructor, that there is no way to get the extra data passed to the the “formatSelection” function? I was under the impression that this was possible because the examples in the documentation show triggering the ‘select2:select’ event with data - but this does not seem to do anything.

While I’m sure these preselected items don’t intefere with getting the right array of id values for form submission, I think users would be confused that they don’t give the same tooltip, and I would really like if you can tell me if this behavior was expected in this version or if it’s a bug.


#2

This is the expected behavior. Select2 does not store its data in Javascript Option objects, but rather in an internal data structure. This means that any Options you manually add to the underlying HTML <select> will not the extra data properties that Select2 makes available for data that has been loaded from an array or AJAX data source.

However, I think you can fake it: Just attach your extra data values (for the user’s location, title, etc.) to your manually added Option object(s) (JavaScript lets you declare additional properties on object instances.) Then in your formatSelect and formatResult functions, as you already know, you receive a parameter representing the selected (formatSelect) or current (formatResult) item. This this value includes an “element” property [see note 1 below] that is a reference to the original Option that the item represents. You can use that reference to access the additional properties you added the manually-added Options. The items retrieved by AJAX will not have these additional properties (and in fact, if they haven’t been selected, they won’t have a reference to an Option object, since Select2 only creates Options for items that have been selected).

So your formatResult and formatSelect functions can check for the element property and pull the additional data from there, and if that’s not present (or it’s null)—such items will have been loaded by AJAX—then you can look on the item itself for your additional data.

Notes:
1. This information is not in the Select2 documentation, but you can confirm it by logging the parameter value passed to your templating function in the JavaScript console and inspecting the object. In fact, you can learn quite a lot that isn’t documented by poking around the HTML elements and JavaScript objects that Select2 creates.
2. The formatResult function only runs when the dropdown is displayed (and note that it runs every time the dropdown is displayed, not just once when the data is initially received). Simply creating an Option element and adding it to the <select> does not cause the dropdown to open; therefore the formatResult function will not run. On the other hand, the formatSelect function runs whenever there is a selection—whether by the user or generated programmatically. This should help explain the behavior you were seeing.


#3

Thanks very much! I didn’t know about the “data.element” property in the “selectionFormat(data)” callback. I just stored the extra data in a property of the option, then in the “selectionFormat” function, if the attribute exists (which it would only with the “prefill” items), parse and use that data.

Now that I think of it, I did notice the “element” property when logging, and didn’t think to check why it was there!


#4

Great! I’m glad I was able to help you!