Scripts: the javascript side of autocompletes¶
Note
This chapter assumes that you have been through Quick start: adding simple autocompletes and Autocomplete classes and Form, fields and widgets.
Design documentation¶
Before installing your own autocomplete scripts, you should know about the humble provided scripts:
autocomplete.js
providesyourlabs.Autocomplete
via the$.yourlabsAutocomplete()
jQuery extension: add an autocomplete box to a text input, it can be used on its own to create a navigation autocomplete like facebook and all the cool kids out there.widget.js
providesyourlabs.Widget
via the$.yourlabsWidget()
jQuery extension: combine an text input with an autocomplete box with a django form field which is represented by a hidden<select>
.addanother.js
enables adding options to a<select>
via a popup from outside the admin, code mostly comes from Django admin BTW,remote.js
providesyourlabs.RemoteAutocompleteWidget
, which extendsyourlabs.Widget
and overridesyourlabs.Widget.getValue
to create choices on-the-fly.text_widget.js
providesyourlabs.TextWidget
, used to manage the value of a text input that has an autocomplete box.
Why a new autocomplete script you might ask ? What makes this script unique is
that it relies on the server to render the contents of the autocomplete-box.
This means that you can fully design it like you want, including new HTML tags
like <img>
, using template tags like {% url %}
, and so on.
If you want to change something on the javascript side, chances are that you
will be better off overriding a method like
yourlabs.RemoteAutocompleteWidget
instead of installing your own script
right away.
What you need to know is that:
- widgets don’t render any inline javascript: the have HTML attributes that
will tell the scripts how to instanciate objects with
$.yourlabsWidget()
,$.yourlabsTextWidget()
and so on. This allows to support dynamically inserted widgets ie. with a dynamic formsets inside or outside of django admin. - the particular attribute that is watched for is
data-bootstrap
. If an HTML element with class.autocomplete-light-widget
is found or created withdata-bootstrap="normal"
thenwidget.js
will call$.yourlabsWidget
. - if you customize
data-bootstrap
,widget.js
will not do anything and you are free to implement your script, either by extending a provided a class, either using a third-party script, either completely from scratch.
Examples¶
django-autocomplete-light provides consistent JS plugins. A concept that you understand for one plugin is likely to be appliable for others.
Overriding a JS option in Python¶
Javascript widget and autocomplete objects options can be overidden via HTML data attributes:
yourlabs.Autocomplete
will use anydata-autocomplete-*
attribute on the input tag,yourlabs.Widget
will use anydata-widget-*
attribute on the widget container.
Those can be set in Python either with register()
, as Autocomplete class
attributes or as widget attributes. See next examples for details.
Per registered Autocomplete¶
These options can be set with the register()
shortcut:
autocomplete_light.register(Person,
attrs={
'placeholder': 'foo',
'data-autocomplete-minimum-characters': 0
},
widget_attrs={'data-widget-maximum-values': 4}
)
Per Autocomplete class¶
Or equivalently on a Python Autocomplete class:
class YourAutocomplete(autocomplete_light.AutocompleteModelBase):
model = Person
attrs={
'placeholder': 'foo',
'data-autocomplete-minimum-characters': 0
},
widget_attrs={'data-widget-maximum-values': 4}
Per widget¶
Or via the Python widget class:
autocomplete_light.ChoiceWidget('FooAutocomplete',
attrs={
'placeholder': 'foo',
'data-autocomplete-minimum-characters': 0
},
widget_attrs={'data-widget-maximum-values': 4}
)
NOTE the difference of the option name here. It is attrs
to match
django and not attrs
. Note that Autocomplete.attrs
might be
renamed to Autocomplete.attrs
before v2 hits RC.
Override autocomplete JS options in JS¶
The array passed to the plugin function will actually be used to $.extend the autocomplete instance, so you can override any option, ie:
$('#yourInput').yourlabsAutocomplete({
url: '{% url "your_autocomplete_url" %}',
// Hide after 200ms of mouseout
hideAfter: 200,
// Choices are elements with data-url attribute in the autocomplete
choiceSelector: '[data-url]',
// Show the autocomplete after only 1 character in the input.
minimumCharacters: 1,
// Override the placeholder attribute in the input:
placeholder: '{% trans 'Type your search here ...' %}',
// Append the autocomplete HTML somewhere else:
appendAutocomplete: $('#yourElement'),
// Override zindex:
autocompleteZIndex: 1000,
});
Note
The pattern is the same for all plugins provided by django-autocomplete-light.
Override autocomplete JS methods¶
Overriding methods works the same, ie:
$('#yourInput').yourlabsAutocomplete({
url: '{% url "your_autocomplete_url" %}',
choiceSelector: '[data-url]',
getQuery: function() {
return this.input.val() + '&search_all=' + $('#searchAll').val();
},
hasChanged: function() {
return true; // disable cache
},
});
Note
The pattern is the same for all plugins provided by django-autocomplete-light.
Overload autocomplete JS methods¶
Use call to call a parent method. This example automatically selects the choice if there is only one:
$(document).ready(function() {
var autocomplete = $('#id_city_text').yourlabsAutocomplete();
autocomplete.show = function(html) {
yourlabs.Autocomplete.prototype.show.call(this, html)
var choices = this.box.find(this.choiceSelector);
if (choices.length == 1) {
this.input.trigger('selectChoice', [choices, this]);
}
}
});
Get an existing autocomplete object and chain autocompletes¶
You can use the jQuery plugin yourlabsAutocomplete()
to get an existing
autocomplete object. Which makes chaining autocompletes with other form fields
as easy as:
$('#country').change(function() {
$('#yourInput').yourlabsAutocomplete().data = {
'country': $(this).val();
}
});
Overriding widget JS methods¶
The widget js plugin will only bootstrap widgets which have
data-bootstrap="normal"
. Which means that you should first name your new
bootstrapping method to ensure that the default behaviour doesn’t get in the
way.
autocomplete_light.register(City,
widget_attrs={'data-widget-bootstrap': 'your-custom-bootstrap'})
Note
You could do this at various level, by setting the bootstrap
argument
on a widget instance, via register()
or directly on an autocomplete
class. See Overriding JS options in Python for details.
Now, you can instanciate the widget yourself like this:
$(document).bind('yourlabsWidgetReady', function() {
$('.your.autocomplete-light-widget[data-bootstrap=your-custom-bootstrap]').live('initialize', function() {
$(this).yourlabsWidget({
// Override options passed to $.yourlabsAutocomplete() from here
autocompleteOptions: {
url: '{% url "your_autocomplete_url" %}',
// Override any autocomplete option in this array if you want
choiceSelector: '[data-id]',
},
// Override some widget options, allow 3 choices:
maxValues: 3,
// or method:
getValue: function(choice) {
// This is the method that returns the value to use for the
// hidden select option based on the HTML of the selected
// choice.
//
// This is where you could make a non-async post request to
// this.autocomplete.url for example. The default is:
return choice.data('id')
},
})
});
});
You can use the remote autocomplete as an example.
Note
You could of course call $.yourlabsWidget()
directly, but using the
yourlabsWidgetReady
event takes advantage of the built-in
DOMNodeInserted event: your widgets will also work with dynamically created
widgets (ie. admin inlines).