The widget
Place the following code into a model file (models/A_widget.py)
# coding: utf8 from gluon.sqlhtml import * class SELECT_OR_ADD_OPTION(object): #and even EDIT def __init__(self, referenced_table, controller="default", function="referenced_data", dialog_width=450): self.referenced_table = referenced_table self.controller = controller self.function = function self.dialog_width = dialog_width def widget(self, field, value): #generate the standard widget for this field select_widget = OptionsWidget.widget(field, value) #get the widget's id (need to know later on so can tell receiving controller what to update) my_select_id = select_widget.attributes.get('_id', None) wrapper = DIV(_id=my_select_id+"__reference-actions__wrapper") wrapper.components.extend([select_widget, ]) style_icons = {'new':"icon plus icon-plus", 'edit': "icon pen icon-pencil" } actions = ['new'] if value: actions.append('edit') # if we already have selected value for action in actions: extra_args = [my_select_id, action, self.referenced_table] if action == 'edit': extra_args.append(value) #create a div that will load the specified controller via ajax form_loader_div = DIV(LOAD(c=self.controller, f=self.function, args=extra_args, ajax=True), _id=my_select_id+"_"+action+"_dialog-form", _title=action+": "+self.referenced_table) #generate the "add/edit" button that will appear next the options widget and open our dialog action_button = A([SPAN(_class=style_icons[action]), SPAN( _class="buttontext button") ], _title=T(action), _id=my_select_id+"_option_%s_trigger"%action, _class="button btn", _style="vertical-align:top" ) #create javascript for creating and opening the dialog js = '$( "#%s_%s_dialog-form" ).dialog({autoOpen: false, not__modal:true, show: "blind", hide: "explode", width: %s});' % (my_select_id, action, self.dialog_width) js += '$( "#%s_option_%s_trigger" ).click(function() { $( "#%s_%s_dialog-form" ).dialog( "open" );return false;}); ' % (my_select_id, action, my_select_id, action, ) js += '$(function() { $( "#%s_option_%s_trigger" ).button({text: true, icons: { primary: "ui-icon-circle-plus"} }); });' % (my_select_id, action, ) if action=='edit': # hide if reference changed - as load is constructed for initial value only (or would need some lazy loading mechanizm) js += '$(function() {$("#%s").change(function() { $( "#%s_option_%s_trigger" ).hide(); } ) });' % (my_select_id, my_select_id, 'edit', ) jq_script=SCRIPT(js, _type="text/javascript") wrapper.components.extend([form_loader_div, action_button, jq_script]) return wrapper
DB Model (assign widgets)
db.define_table('category', Field('name', 'string', notnull=True, unique=True), Field('description', 'text'), format="%(name)s" ) db.define_table('product', Field('category_id', db.category, widget=SELECT_OR_ADD_OPTION("category").widget), # can override defaults: controller="default", function="referenced_data", Field('name', 'string', notnull=True), Field('description', 'text'), Field('price', 'decimal(10,2)', notnull=True) )
Controller functions
def create_product(): # can be any function which displays SQLFORM form = crud.create(db.product) return response.render('generic.html', context=form) def update_product(): # for edit functionality (again can be any edti form generating function) form = crud.update(db.product, int(request.args[0])) return response.render('generic.html', context=form)
#you need jqueryUI for the widget to work, include in calling controllers or comment out in layout.html
response.files.append("//ajax.googleapis.com/ajax/libs/jqueryui/1.8.9/jquery-ui.js")
response.files.append("//ajax.googleapis.com/ajax/libs/jqueryui/1.8.9/themes/smoothness/jquery-ui.css")
############ important function - it should be indicated in widget constructor ####### def referenced_data(): """ shows dialog with reference add/edit form the idea is taken from "data" function, just first argument is the id of calling select box """ try: references_options_list_id = request.args[0] except: return T("ERR: references_options_list_id lacking") try: action = request.args[1] except: return T("ERR: action lacking") try: referenced_table= request.args[2] except: return T("ERR: referenced_table lacking") if action=="edit": try: referenced_record_id = int( request.args[3] ) except: response.flash = T("ERR: referenced_record_id lacking"); return (response.flash) form = SQLFORM(db[referenced_table], referenced_record_id) # edit/update/change else: form = SQLFORM(db[referenced_table]) # new/create/add if form.accepts(request.vars): #Then let the user know adding via our widget worked response.flash = "done: %s %s" %( T(action), referenced_table) # added / edited #close the widget's dialog box response.js = '$( "#%s_%s_dialog-form" ).dialog( "close" ); ' %(references_options_list_id, action) def format_referenced(id): #return format(db[referenced_table], id) #should get from table table = db[referenced_table] if isinstance(table._format, str): return table._format % table[id] elif callable(table._format): return table._format(table[id]) else: return "???" if action=='new': #update the options they can select their new category in the main form response.js += """$("#%s").append("<option value='%s'>%s</option>");""" % (references_options_list_id, form.vars.id, format_referenced(form.vars.id)) #and select the one they just added response.js += """$("#%s").val("%s");""" % (references_options_list_id, form.vars.id) if action=='edit': #response.js += """alert( $('#%s option[value="%s"]').html());""" % (references_options_list_id, form.vars.id) # format_referenced(form.vars.id) ) response.js += """$('#%s option[value="%s"]').html('%s')""" % (references_options_list_id, form.vars.id, format_referenced(form.vars.id) ) return BEAUTIFY(form)
In Action
****************************************************************************
Comments (2)
0
titogarrido 11 years ago
When I try to use it I get:
TypeError: a.curCSS is not a function
[Break On This Error]
...his,"marginTop",true))||0,r=m+q+(parseInt(a.curCSS(this,"marginRight",true))||0)...
jquery-ui.min.js (line 366)
Any clue?
0
jurgis 11 years ago
if using date/time picker , it pops under the dialog ,
so change in your app /static/css/calendar.css:
.calendar{z-index:1099; ..
as picker/calendar has z-index up to 99, and jqueryUI dialog has 1000