Hi all,
I have a normal TextField in a form, and I'd like to use DAL to provide list of possible selection, but also allow user to type in an input if no match found. Basically, I'd like to present the autocomplete result as a suggestion to the user, not as a finite option for selection.
I am currently using the Select2ListView and building the list of suggestions, but during user input the widget does not accept any value outside of the autocomplete list. I think the current DAL assumes that you MUST choose from one of the selection.
Is there a way to render the suggestion, but when no match found, accept whatever input the user types in? A pointer to the right direction would help!
edit: to clarify, I am not after the behavior of creation of new choices as in: https://django-autocomplete-light.readthedocs.io/en/master/tutorial.html#creation-of-new-choices-in-the-autocomplete-form - I would simply like to be able to accept whatever the user type in into the TextField, thus making AutoComplete to provide a list of suggestion, not a finite option to select from.
Regards,
Zen
OK, so I got my own fix for this. So here's what I ended up doing:
To allow for 'arbitrary input' we need to use the 'tags' option of select2. Had a look and found that select2.js parsed Boolean, so I needed to use "true" instead of True on the widget code to make it work.
in the form:
# email is just a plain TextField
widgets = {
'email':autocomplete.Select2(url='api>autocomplete.memberEmail', attrs={
"data-tags":"true", "data-html":True,
})
}
For the autocomplete view, it's just the good ol' Select2QuerySetView - because that's what I wanted. I want it to search and return a QuerySet. This bit is working as expected.
class AutoComplete_MemberEmail(autocomplete.Select2QuerySetView):
def get_queryset(self):
return Member.Q_searchEmailNameUname(self.q)
def get_selected_result_label(self, item):
return item.user.email
def get_result_label(self, item):
return "%s <br />[@%s] %s" % (item.user.email, item.user.username, item.displayName )
So now in the UI form, when I type in a text in the email field, it searches AutoComplete_MemberEmail for members with matching username, name, or email. But when I don't see a suggestion that fits, I can type in another e-mail directly. the tags option creates a new option on the fly, and the new e-mail is set as the selected value.
Problem is, when I DO select an existing value, the get_result_label() output is set as the option value, instead of the get_selected_result_label() output. After some digging, I found the code in the select2.js that does it, and it only triggers when data-tags is present
...
processResults: function (data, page) {
if (element.attr('data-tags')) {
$.each(data.results, function(index, value) {
value.id = value.text; <-- this bit here
});
}
return data;
},
So I had to override it with my custom js code which basically copy paste the same stuff, but set that line into
'''value.id = value.selected_text'''
I checked on the backend, if I did not define get_selected_result_label(), it automatically sets selected_text as the value of get_result_label(). So I think value.id = value.selected_text is better, because it make use of get_selected_result_label() output as intended.
So ... I'd consider this issue as solved ! not an issue with the DAL itself, but the select2.js when data-tags is set to true ignores get_selected_result_label()
For setting initial value (when form error re-render, or for editing existing instance / record,
This is the code that got me going:
def __init__(self, *args, **kwargs):
super(TheForm, self).__init__(*args, **kwargs)
email = None
if self.instance.id: email = self.instance.email
if 'email' in self.data: email = self.data['email']
self.fields['email'].widget.choices=[[email,email]]
If anyone has better way I'd appreciate it. Example if there's somehow a way of doing the below:
if self.field['email'].value :
self.fields['email'].widget.choices=[[self.field['email'].value ,self.field['email'].value ]]
Anyway the above works for me, for the time being.
Hi @zsugiart, brilliant solution to this.
Would be good to get this added to the docs.
As a sidenote the DAL library uses Select2 under the hood but is not an exhaustive wrapper around it. Select2 is under active development and syntax and options keep changing all the time. If you want to further customise the behavior you can just interface directly with Select2 as you've done.
OK, so I got my own fix for this. So here's what I ended up doing:
To allow for 'arbitrary input' we need to use the 'tags' option of select2. Had a look and found that select2.js parsed Boolean, so I needed to use "true" instead of True on the widget code to make it work.in the form:
# email is just a plain TextField widgets = { 'email':autocomplete.Select2(url='api>autocomplete.memberEmail', attrs={ "data-tags":"true", "data-html":True, }) }For the autocomplete view, it's just the good ol' Select2QuerySetView - because that's what I wanted. I want it to search and return a QuerySet. This bit is working as expected.
class AutoComplete_MemberEmail(autocomplete.Select2QuerySetView): def get_queryset(self): return Member.Q_searchEmailNameUname(self.q) def get_selected_result_label(self, item): return item.user.email def get_result_label(self, item): return "%s <br />[@%s] %s" % (item.user.email, item.user.username, item.displayName )So now in the UI form, when I type in a text in the email field, it searches AutoComplete_MemberEmail for members with matching username, name, or email. But when I don't see a suggestion that fits, I can type in another e-mail directly. the tags option creates a new option on the fly, and the new e-mail is set as the selected value.
Problem is, when I DO select an existing value, the get_result_label() output is set as the option value, instead of the get_selected_result_label() output. After some digging, I found the code in the select2.js that does it, and it only triggers when data-tags is present
... processResults: function (data, page) { if (element.attr('data-tags')) { $.each(data.results, function(index, value) { value.id = value.text; <-- this bit here }); } return data; },So I had to override it with my custom js code which basically copy paste the same stuff, but set that line into
'''value.id = value.selected_text'''
I checked on the backend, if I did not define get_selected_result_label(), it automatically sets selected_text as the value of get_result_label(). So I think value.id = value.selected_text is better, because it make use of get_selected_result_label() output as intended.
So ... I'd consider this issue as solved ! not an issue with the DAL itself, but the select2.js when data-tags is set to true ignores get_selected_result_label()
It saved my day. Thanks for your help
Most helpful comment
OK, so I got my own fix for this. So here's what I ended up doing:
To allow for 'arbitrary input' we need to use the 'tags' option of select2. Had a look and found that select2.js parsed Boolean, so I needed to use "true" instead of True on the widget code to make it work.
in the form:
For the autocomplete view, it's just the good ol' Select2QuerySetView - because that's what I wanted. I want it to search and return a QuerySet. This bit is working as expected.
So now in the UI form, when I type in a text in the email field, it searches AutoComplete_MemberEmail for members with matching username, name, or email. But when I don't see a suggestion that fits, I can type in another e-mail directly. the tags option creates a new option on the fly, and the new e-mail is set as the selected value.
Problem is, when I DO select an existing value, the get_result_label() output is set as the option value, instead of the get_selected_result_label() output. After some digging, I found the code in the select2.js that does it, and it only triggers when data-tags is present
So I had to override it with my custom js code which basically copy paste the same stuff, but set that line into
'''value.id = value.selected_text'''
I checked on the backend, if I did not define get_selected_result_label(), it automatically sets selected_text as the value of get_result_label(). So I think value.id = value.selected_text is better, because it make use of get_selected_result_label() output as intended.
So ... I'd consider this issue as solved ! not an issue with the DAL itself, but the select2.js when data-tags is set to true ignores get_selected_result_label()