[web2py] need help with typeahead widget

219 views
Skip to first unread message

Richard

unread,
Mar 19, 2013, 7:15:19 PM3/19/13
to web...@googlegroups.com
Hello,

I wrote a typeahead autocomplete widget and work around many little issues, but now I have one that I have no clue how to solve...


When the form get submit and the validators trigger the input value key or value label is cleared out, but the value of the hidden field stick so there is no problem get the proper value in the database but it is kind of anoying to the user to not see his value and didn't have validator error message.

Any help appreciate.

Richard

Ricardo Pedroso

unread,
Mar 19, 2013, 9:59:23 PM3/19/13
to web...@googlegroups.com
Hi Richard,

You probably would have more and accurate answers if you made
a simple application with the problem.

Based on the code you post and the comments I tried to replicate the problem
you describe.

Find attached a small and self contained application,
with your widget (with some little changes), and see if it's what you want.
If not, make a small, self contained application for us to try.

To run it just drop in an someapp/controllers.

Regards,
Ricardo
default.py

Richard Vézina

unread,
Mar 20, 2013, 11:08:35 AM3/20/13
to web2py-users
You right Richardo,

I will do a dummy app... So less time impact on anyone who want to help!

I read at your code and no sure where you were going by the little change you made to the code I provide...

I get back in a couples of minutes with dummy app.

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 20, 2013, 3:04:03 PM3/20/13
to web2py-users
Here the dummy app (see attach)

To reproduce the behavior I want to correct : You need to submit the form without input value for f1 and a picked value with typeahead autocomplete widget...

test2 table f1 and test1_id are required field... You will notice that if you fill field test1_id but not f1 error message triggers... Then you will see that the input value "label" of the inserted choice "a1" for instance it not displayed... But there is no error message because the hidden field value stays there has it should, but it pretty anoying for a user to don't see his input and not having a error message.


NOTE: There is duplicated entries in the typeahead drop down don't no why it should be the bootstrap used, because here I don't get this kind of behavior... So must be a issue with the typeahead bootstrap lib form netdna.bootstrapcdn.com, I don't know...

Richard


web2py.app.typeahead_widget_dummy.w2p

Richard Vézina

unread,
Mar 20, 2013, 3:21:44 PM3/20/13
to web2py-users
My understanding of the problem is that the test2_test1_id_ac input field is not part of the form and web2py don't keep the value on page refresh... But I can't figure out how other jquery plugin does ot keep value of added field... no... I am correcting myseld... I do understand, since they recreate themself from the dom the get the value from the web2py input field (test2_test1_id)...

I my case since the test2_test1_ac input is recreate I guess on form error when the form is reload with error so the inputed value is overwrite...

So I need a piece of js that will set the a value "label" on form reload, but I am afraid a circular reference thought

Richard

Richard Vézina

unread,
Mar 20, 2013, 5:04:39 PM3/20/13
to web2py-users
Finally solve my problem like that... Not very elegant :

def autocomplete_typeahead_widget(f, v, referenced_table, represent_field, id_field, controller, **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), _value="", **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 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) {
                            var labels = []
                            $.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('')}
                });
            if($('#%(table_name)s_%(field_name)s_ac').val().length==0 && $('#%(table_name)s_%(field_name)s').val().length!=0) 
                {
                    var reverseMap = {}
                    $.get('%(url)s',
                        function (data) {
                            $.each(data.options, function (i, item) {
                                reverseMap[item.id] = item.option
                                })
                            $('#%(table_name)s_%(field_name)s_ac').val(reverseMap[$('#%(table_name)s_%(field_name)s').val()])
                            }
                        )
                }
            });""" % {'url': URL(c=controller,
            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})
    )


I attach the corrected app...

Richard
web2py.app.typeahead_widget_dummy (1).w2p

Richard Vézina

unread,
Mar 20, 2013, 5:06:13 PM3/20/13
to web2py-users
It could be less complicated if the hidden field were of select I guess since value and representation could be easier to get on form reload... I may refactor this when I get time...

Richard

Richard Vézina

unread,
Mar 22, 2013, 11:58:53 AM3/22/13
to web2py-users
With the SELECT, better initialisation and no more roundtrip to get the value representation :

def autocomplete_typeahead_widget(field, value, referenced_table, represent_field, id_field, controller, **kwargs):
    table_name = field._tablename
    field_name = field.name
    field_requires = field.requires
    if value != None and value != '':
        field_value = value
        option = sample_id_represent[int(value)]
    else:
        field_value = ''
        option = ''
    input_args = {'_data-provide':'typeahead', '_autocomplete': 'off'}
    input_args.update(kwargs)
    return CAT(
        INPUT(_type="text", _class="string", _id="%s_%s_ac" % (table_name, field_name), _value=option, **input_args),
        SELECT(OPTION(option, _value=field_value),
            value=field_value,
            _id="%s_%s" % (table_name, field_name),
            _name=field_name,
            _style='display: none;',
            requires=field_requires),
        SCRIPT("""jQuery(document).ready(function(){
            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) {
                            var labels = []
                            $.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')
                        .find('option')
                        .remove()
                        .end()
                        .append('<option value="'+mapped[item]+'" selected="selected">'+item+'</option>');
                    return item;
                    }
                })
            });
            $('input#%(table_name)s_%(field_name)s_ac').blur(
                function(){
                    if($(this).val().length==0) {
                        $('#%(table_name)s_%(field_name)s')
                            .find('option')
                            .remove()
                            .end();
                        }
                    else if($('#%(table_name)s_%(field_name)s').val()!=mapped[$(this).val()]) {
                        $('#%(table_name)s_%(field_name)s')
                            .find('option')
                            .remove()
                            .end();
                        }
                });
            });""" % {'url': URL(c=controller,
            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 22, 2013, 3:09:38 PM3/22/13
to web2py-users
Oups!

option = sample_id_represent[int(value)]

Should be replaced by this line :

 db[referenced_table](int(value))[represent_field]

I attach a updated version of the dummy app...

Richard


web2py.app.typeahead_widget_dummy (2).w2p
Reply all
Reply to author
Forward
0 new messages