Note
This chapter assumes that you have been through tutorial
and autocomplete
and form
.
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.
django-autocomplete-light provides consistent JS plugins. A concept that you understand for one plugin is likely to be appliable for others.
If your website has a lot of data, it might be useful to add a search input somewhere in the design. For example, there is a search input in Facebook's header. You will also notice that the search input in Facebook provides an autocomplete which allows to directly navigate to a particular object's detail page. This allows a visitor to jump to a particular page with very few effort.
Our autocomplete script is designed to support this kind of autocomplete. It can be enabled on an input field and query the server for a rendered autocomplete with anything like images and nifty design. Just create a view that renders just a list of links based on request.GET.q
.
Then you can use it to make a global navigation autocomplete using autocomplete.js
directly. It can look like this:
// Make a javascript Autocomplete object and set it up
var autocomplete = $('#yourInput').yourlabsAutocomplete({
url: '{% url "your_autocomplete_url" %}',
});
So when the user clicks on a link of the autocomplete box which is generated by your view: it is like if he clicked on a normal link.
You've learned that you can have a fully functional navigation autocomplete like on Facebook with just this:
$('#yourInput').yourlabsAutocomplete({
url: '{% url "your_autocomplete_url" %}',
choiceSelector: 'a',
}).input.bind('selectChoice', function(e, choice, autocomplete) {
window.location.href = choice.attr('href');
});
autocomplete.js
doesn't do anything but trigger selectChoice
on the input when a choice is selected either with mouse or keyboard, let's enable some action:
Because the script doesn't know what HTML the server returns, it is nice to tell it how to recognize choices in the autocomplete box HTML:: This will allow to use the keyboard arrows up/down to navigate between choices.
Refer to navigation
for complete help on making a navigation autocomplete.
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.
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}
)
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}
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.
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.
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.
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]);
}
}
});
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();
}
});
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).