yourlabs / django-autocomplete-light

A fresh approach to autocomplete implementations, specially for Django. Status: v4 alpha, v3 stable, v2 & v1 deprecated.
https://django-autocomplete-light.readthedocs.io
MIT License
1.8k stars 468 forks source link

Search Input not receiving focus #856

Open jsbUSMC opened 7 years ago

jsbUSMC commented 7 years ago

I have a really odd problem that I can't seem to nail down the cause. I am using DAL v3.2.7 with Django 1.11.1. In one of my forms, I am trying to create a form whereby the user can add a person, and then choose the department that the person belongs to. There is a FK field on Person -> Department, and M2M fields for Person <-> AccessLevel, Key. The issue lies with the autocomplete for Department. While the form renders nicely in my modal component (I use django-fm and crispy forms), and the Department is successfully queried and results are populated in the select2 dropdown, the search input cannot receive focus and won't allow any input for autocompletion. The funny thing is, my ModelSelect2Multiple widgets work fine! There is one last interesting thing that is happening, and that is when I have the field selected in the UI, and the dropdown is open, if I hit the Esc key and the modal window is closed, the entire widget remains on screen and the input element receives focus and the autocomplete works like a charm! I don't receive any errors anywhere, either from the Chrome Dev Tools, Django Debug Toolbar, nor from the system console where I have django running.

Here is the relevant code in my app:

models.py

class Department(models.Model):
    history = AuditlogHistoryField()

    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name

    class Meta:
        ordering = ['name']

class AccessLevel(models.Model):
    history = AuditlogHistoryField()

    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name

    class Meta:
        ordering = ['name']

class Key(models.Model):
    history = AuditlogHistoryField()

    number = models.PositiveSmallIntegerField()

    class Meta:
        ordering = ['number']

class Person(models.Model):
    history = AuditlogHistoryField()

    name = models.CharField(max_length=100)
    escort_req = models.BooleanField()
    department = models.ForeignKey(Department, related_name='pers_dept')
    levels = models.ManyToManyField(AccessLevel, related_name='access_levels', related_query_name='level')
    keys = models.ManyToManyField(Key, related_name='key_list', related_query_name='key')

    class Meta:
        ordering = ['name']

department_urls.py

# ...
url(r'^autocomplete/$',
        login_required(DepartmentAutocomplete.as_view()),
        name="department_autocomplete"),
# ...

department_views.py

# ...
class DepartmentAutocomplete(autocomplete.Select2QuerySetView):
    def get_queryset(self):
        if not self.request.user.is_authenticated():
            return Department.objects.none()

        qs = Department.objects.all()

        if self.q:
            qs = qs.filter(name__icontains=self.q)

        return qs
# ...

forms.py

# ...
class PersonForm(autocomplete.FutureModelForm):
    department = forms.ModelChoiceField(
        queryset=Department.objects.all(),
        widget=autocomplete.ModelSelect2(url='department_autocomplete')
    )
    levels = forms.ModelMultipleChoiceField(
        queryset=AccessLevel.objects.all(),
        widget=autocomplete.ModelSelect2Multiple(url='access_level_autocomplete')
    )
    keys = forms.ModelMultipleChoiceField(
        queryset=Key.objects.all(),
        widget=autocomplete.ModelSelect2Multiple(url='key_autocomplete')
    )
    escort_req = forms.BooleanField(required=False)

    class Meta:
        model = Person
        fields = ['name', 'escort_req', 'department', 'levels', 'keys']
        labels = {
            'escort_req': 'Escort Required?'
        }

    def __init__(self, *args, **kwargs):
        super(PersonForm, self).__init__(*args, **kwargs)
        self.helper = FormHelper(self)
        self.helper.form_tag = True
        self.helper.form_class = 'form-horizontal'
        self.helper.label_class = 'col-sm-3'
        self.helper.field_class = 'col-sm-9'
        self.helper.layout = Layout(
            Field('name'),
            Field('escort_req'),
            Field('department'),
            Field('levels'),
            Field('keys'),
        )
# ...

I really appreciate any kind of help the community can provide! I will continue doing what I can to see if I can figure out the problem. Could it possibly be a problem with my front-end dependencies? It doesn't seem to be a problem on the backend, since the ModelSelect2Multiple widgets work just fine. I'm stumped.

jpic commented 7 years ago

If I had to add an object with more than a name, I'd rather use django-addanother.

Then, you can have the department in the form.

jsbUSMC commented 7 years ago

The thing is, I can already use the department in the form. The dropdown on the Select2 widget populates with the correct objects in the queryset from the db. It's just that the actual <input class="select2-search__field"> does not receive focus, and I cannot even get it to receive focus when using the Chrome Dev Tools. I highlight the element, then $0.focus(); and nothing happens. The thing is, if I hit Esc, while the dropdown is still active, the modal window is closed, and suddenly that search input will work and autocomplete based on character input.

jpic commented 7 years ago

Never had this issue before, perhaps reproduce it in a jsfiddle and ask on stackoverflow, in this case please post the link to your question here.

Otherwise, perhaps jquery-autocomplete-light will work better for you #749

Madalosso commented 7 years ago

Same problem here. Everything looks like working right. Proper dropdown choices do select, but can't focus on input to search.

jpic commented 7 years ago

If anybody reproduces this in the test_project i'll take a look.

Madalosso commented 7 years ago

I'm still researching how to solve this. But it looks like the problem it's related to the Select2. I tried to insert select2 outside of dal and the same problem occurs.

Madalosso commented 7 years ago

Found the fix to my case. It's a problem when trying to use select2 in a bootstrap modal. https://github.com/select2/select2/issues/1436 So... Not a django-autocomplete-light problem.

jpic commented 7 years ago

I'm going to have to face this soon myself...

jsbUSMC commented 7 years ago

I also am using the select2 widget inside of a bootstrap modal. I'm going to try and implement your fix, @Madalosso hopefully it'll also clear up. I'm in the process of refactoring my project into a SPA, but this will help in the meantime.

serguitus commented 4 years ago

I know this discussion is old but I still faced the same issue today. as @Madalosso pointed, it IS a Select2 issue. There is a SO discussion on solutions to this. Even if there is a dirty hack to deal with this (removing tabindex="-1" on your modal HTML) I think this is something that DAL should fix (and can fix easily) I did some research on the code and propose this couple lines at ./select2.js#72 to fix this:

parent = $(this).attr('dropdownParent') || 'body';
$(this).select2({
            dropdownParent: $(parent),

Note I just added the option to set dropdownParent parameter on Select2 Initialization so it can be set on widget definition via

       widget=autocomplete.ModelSelect2(
            attrs={'dropdownParent': '.modal-content'},
        ),

and it just works :-) @jpic, what do you think about it?

snpradhan commented 2 years ago

@serguitus's solution worked for me, however, I had to set the 'dropdownParent' to the immediate parent of the select field for the autocomplete dropdown to properly align