Adjusting pattern options for AjaxSelectFieldWidget

I have a Choice-Field with an AjaxSelectFieldWidget.

from plone.app.z3cform.widget import AjaxSelectFieldWidget
from plone.autoform import directives

    empfaengerauswahl = schema.Choice (
        title = 'Empfängerauswahl',
        missing_value = '',
        required = True,
        vocabulary="my.package.MyVocabulary"
    )

    directives.widget(
        'empfaengerauswahl',
        AjaxSelectFieldWidget,
        vocabulary="my.package.MyVocabulary",
        pattern_options={'orderable':True}
    )

It gives me a select2-widget with a dropdown, but it does not search and find an entry, it only underlines the ones which match the string entered like so:

image

There a even values starting with C-Werte which are way below in the dropdown, not showing up without scrolling.

image

When is use the select2-widget on the example page at Appearance | Select2 - The jQuery replacement for select boxes

it only shows entries matching the entered string. This is what i want, but i don't know how to tell the widget to behave like that.

Are there information available about those pattern-options one can pass to the widget to achieve such a desired behaviour of the select2-widget ?

The widget provides values from your vocabulary, it provides the query parameter in the request and can only display a list of filtered results if your vocabulary handles the parameter.

        query = request.form.get('query','')
        brains = p_catalog.searchResults(
            portal_type = 'produkte',
            Title = {'query': query},
        )
        for brain in brains:
            if brain:
                terms.append(
                    SimpleTerm(
                        value = brain.getPath(),
                        token = brain.UID,
                        title = brain.Title
                    )
                )
    
        terms = sorted(terms, key=lambda match: match.title.lower())
                        
    data = SimpleVocabulary(terms)
    
    return data

edit, add: this also works:

query = request.form.get('query','') and '%s*' % request.form['query']

That makes sense !

But where do i get the request from in my vocabulary ? I only have context. Do i have to make a MultiAdpater out of my vocabulary ?

from Products.CMFCore.utils import getToolByName
from zope.schema.interfaces import IVocabularyFactory
from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary

@implementer(IVocabularyFactory)
class PfadVocabulary(object):
    """                                                                                                                       
    Factory für ein vocabulary für die Pfade.                                                         
    """

    def __call__(self, context):
        portal_catalog = getToolByName(getSite(), 'portal_catalog')

        brains = portal_catalog(object_provides=IOrganisationsordner.__identifier__)
        result =  [ (brain["UID"], brain["pfad"]) for brain in brains ]                                                       
        terms = make_terms(result)

        return SimpleVocabulary(terms)

PfadVocabularyFactory = PfadVocabulary()

You can do this:

from plone import api
from zope.schema.interfaces import IVocabularyFactory
from zope.globalrequest import getRequest


@provider(IVocabularyFactory)
def ProductPathsVocabulary(context):
    """ provide a vocabulary of Products and their paths 
    """

    p_catalog = api.portal.get_tool(name='portal_catalog')
    terms = []
    if context:
        # only provide results when there is a context

        if hasattr(context, 'REQUEST'):
            request = context.REQUEST
        else:
            # called from an EasyForm
            request = getRequest()

and register your vocabulary as an utility

  <utility name="mdb_theme.ProductPathsVocabulary"
        component=".dynamic_vocabularies.ProductPathsVocabulary"
        provides="zope.schema.interfaces.IVocabularyFactory"
        />

Changing the __call__ method to def __call__(self, context, query=None): should be enough to get the query value

Do you have a specific reason to make your vocabulary factory into a class?
see also: Vocabularies — Plone Documentation v5.2

I think it was the solution i found as i need the context to calculate the actual values for the vocabulary.

For learning Zope/Plone, up until you are comfortable with all the benefits the ZCA-architecture offers you, there is quite a long period of time where you are happy to find code-examples you can copy/paste and which simply work :slight_smile:

1 Like

The reason I asked was PEP R0393.

Searched the net for 'plone PEP R0393' without a useful result. Looks like the search engines don't look into the real interesting stuff :slight_smile:

How do i access this Plone proposal ?

Edit: Btw, the vocabulary class was generated by mr.bob plone templates.

If your class has too few public functions, your linter will complain about it and suggest that you convert it into a function. too-few-public-methods / R0903 - Pylint 3.1.0-dev0 documentation

Edit sorry, looks like a typo in my earlier post: R0903 not 0393

Edit2 - you are right about copy/paste code examples. Just in the samples above, I see multiple ways to

  • access the context
  • register the utility / initialize the class
  • get the catalog

Mixing these will lead to chaos later :wink: Not to mention my misnamed vocabulary factory...