[web2py] share my new typeahead widget

104 views
Skip to first unread message

Richard

unread,
Mar 18, 2013, 9:02:09 PM3/18/13
to web...@googlegroups.com
Hello,

Wrote this and it may be handy for others in the meantime there is a better integration of autocomplete bootstrap typeahead :

# Widget function
def autocomplete_typeahead_widget(f, v, **kwargs):
    table_name = f._tablename
    field_name = f.name
    field_requires = f.requires
    field_value = v
    options = {'_data-provide':'typeahead', '_autocomplete': 'off'}
    options.update(kwargs)
    return CAT(
        INPUT(_type="text", _class="string", _id="%s_%s_ac" % (table_name, field_name), **options),
        INPUT(_type="hidden", _id="%s_%s" % (table_name, field_name), _name=field_name, _value=field_value, requires=field_requires),
        SCRIPT("""$(function() { $('#%(table_name)s_%(field_name)s_ac').typeahead(
            { source: function (query, process) {
                $.get('%(url)s.json',
                    { q: query },
                    function (data) {
                        labels = []
                        mapped = {}
                        $.each(data.options, function (i, item) {
                            mapped[item.option] = item.id
                            labels.push(item.option)
                            })

                        process(labels)
                        }
                    )
              },
              updater:function(item){ $('#%(table_name)s_%(field_name)s').val(mapped[item]); return item}
            })
        });""" % {'url':URL(c='test', f='autocomplete_typeahead_widget_json_feed'), 'table_name':table_name, 'field_name':field_name})
    )

# Generating JSON function
def autocomplete_typeahead_widget_json_feed():
    rows=db(db.table.field>0).select()
    options = []
    for i,row in enumerate(rows):
        options.append({'option': row.field, 'id': row.id})
    from gluon.contrib import simplejson
    return simplejson.dumps({'options': options })

# Init of the widget
db.other_table.select_field.widget = autocomplete_typeahead_widget

or

db.other_table.select_field.widget = lambda field, value:  autocomplete_typeahead_widget(field, value, MORE_ARGS)

Enjoy!

Richard

Richard Vézina

unread,
Mar 19, 2013, 1:29:45 PM3/19/13
to web2py-users
I found an hole in typeahead updater... 

typeahead updater seems to be bit it by a trigger event only when there is something that have been selected... But if for instance someone select something the hidden field that get submitted is updated with the id if the record, but the hidden field is not updated if the field is empty (for some reason : USERS) after been filled once... So the id of the previous selected record stay there and the form submit correctly if there is no other validators that trigger up!!

I googled a lot about this hole and typeahead, maybe I don't have the right keywords or the issue have not been identified since bootstrap typeahead as only recently get the updated to support key/value field.

Richard



Richard

--
 
---
You received this message because you are subscribed to the Google Groups "web2py-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to web2py+un...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Richard Vézina

unread,
Mar 19, 2013, 2:39:05 PM3/19/13
to web2py-users
Ok, here a more generic and a fix for the bootstrap hole (in red) :

def autocomplete_typeahead_widget(f, v, referenced_table, represent_field, id_field, **kwargs):
    table_name = f._tablename
    field_name = f.name
    field_requires = f.requires
    field_value = v
    options = {'_data-provide':'typeahead', '_autocomplete': 'off'}
    options.update(kwargs)
    return CAT(
        INPUT(_type="text", _class="string", _id="%s_%s_ac" % (table_name, field_name), **options),
        INPUT(_type="hidden", _id="%s_%s" % (table_name, field_name), _name=field_name, _value=field_value, requires=field_requires),
        SCRIPT("""jQuery(document).ready(function(){
            $(function() { $('#%(table_name)s_%(field_name)s_ac').typeahead(
                { minLength: 0,
                  items: 10,
                  source: function (query, process) {
                    $.get('%(url)s',
                        { q: query },
                        function (data) {
                            labels = []
                            mapped = {}
                            $.each(data.options, function (i, item) {
                                mapped[item.option] = item.id
                                labels.push(item.option)
                                })

                            process(labels)
                            }
                        )
                  },
                  updater:function(item){ $('#%(table_name)s_%(field_name)s').val(mapped[item]); return item;}
                })
            });
            $("input#%(table_name)s_%(field_name)s_ac").blur(function(){ if($(this).val().length==0) {$('#%(table_name)s_%(field_name)s').val('')} });
            });""" % {'url': URL(c='test',
            f='autocomplete_typeahead_widget_json_feed.json',
            vars=dict(referenced_table=referenced_table,
                represent_field=represent_field,
                id_field=id_field)),
            'table_name': table_name,
            'field_name': field_name})
    )

def autocomplete_typeahead_widget_json_feed():
    referenced_table = request.vars.referenced_table
    id_field = request.vars.id_field
    represent_field = request.vars.represent_field
    rows=db(db[referenced_table][id_field]>0).select(db[referenced_table][id_field],
        db[referenced_table][represent_field])
    options = []
    for i,row in enumerate(rows):
        options.append({'option': row[represent_field], 'id': row.id})
    from gluon.contrib import simplejson
    return simplejson.dumps({'options': options })


db.table.field.widget = lambda field, value: autocomplete_typeahead_widget(field, value, referenced_table='referenced_table_name', represent_field='represent_field_name', id_field='id_field_name_of_the_referenced_table', _placeholder=T('Start typing...'))

Richard Vézina

unread,
Mar 19, 2013, 4:23:09 PM3/19/13
to web2py-users
Finally it there were still a issue with the above code in case length was greater then 0 but the typeahead field was alter before submit... Here a fix :

def autocomplete_typeahead_widget(f, v, referenced_table, represent_field, id_field, **kwargs):
    table_name = f._tablename
    field_name = f.name
    field_requires = f.requires
    field_value = v
    options = {'_data-provide':'typeahead', '_autocomplete': 'off'}
    options.update(kwargs)
    return CAT(
        INPUT(_type="text", _class="string", _id="%s_%s_ac" % (table_name, field_name), **options),
        INPUT(_type="hidden", _id="%s_%s" % (table_name, field_name), _name=field_name, _value=field_value, requires=field_requires),
        SCRIPT("""jQuery(document).ready(function(){
            var labels = []
            var mapped = {}
            $(function() { $('#%(table_name)s_%(field_name)s_ac').typeahead(
                { minLength: 0,
                  items: 10,
                  source: function (query, process) {
                    $.get('%(url)s',
                        { q: query },
                        function (data) {
                            $.each(data.options, function (i, item) {
                                mapped[item.option] = item.id
                                labels.push(item.option)
                                })

                            process(labels)
                            }
                        )
                  },
                  updater:function(item){ $('#%(table_name)s_%(field_name)s').val(mapped[item]); return item;}
                })
            });
            $('input#%(table_name)s_%(field_name)s_ac').blur(
                function(){
                    if($(this).val().length==0) {$('#%(table_name)s_%(field_name)s').val('')}
                    else if($('#%(table_name)s_%(field_name)s').val()!=mapped[$(this).val()]) {$('#%(table_name)s_%(field_name)s').val('')}
                });
            });""" % {'url': URL(c='test',
            f='autocomplete_typeahead_widget_json_feed.json',
            vars=dict(referenced_table=referenced_table,
                represent_field=represent_field,
                id_field=id_field)),
            'table_name': table_name,
            'field_name': field_name})
    )

Richard Vézina

unread,
Mar 19, 2013, 4:39:23 PM3/19/13
to web2py-users
One more issue... The representation in the visible referenced field get cleared out when form has error...

Stay tune!!

Richard

Richard Vézina

unread,
Mar 20, 2013, 5:07:27 PM3/20/13
to web2py-users
Reply all
Reply to author
Forward
0 new messages