NOTE: A newer version of this widget has been created by Jurgis - please see http://www.web2pyslices.com/slice/show/1616/widget-select-or-add-option-ng.
This widget will create a <select> object with an "Add" button next to it, allowing users to add new catagories, etc on the fly without having to visit a different screen. Works with IS_IN_DB and uses web2py components and jqueryUI dialogs
This widget was inspired by the OPTION_WITH_ADD_LINK slice
The widget
Place the following code into a model file (models/A_widget.py)
from gluon.sqlhtml import *
class SELECT_OR_ADD_OPTION(object):
def __init__(self, controller=None, function=None, form_title=None, button_text = None, dialog_width=450):
if form_title == None:
self.form_title = T('Add New')
else:
self.form_title = T(form_title)
if button_text == None:
self.button_text = T('Add')
else:
self.button_text = T(button_text)
self.dialog_width = dialog_width
self.controller = controller
self.function = function
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)
add_args = [my_select_id]
#create a div that will load the specified controller via ajax
form_loader_div = DIV(LOAD(c=self.controller, f=self.function, args=add_args,ajax=True), _id=my_select_id+"_dialog-form", _title=self.form_title)
#generate the "add" button that will appear next the options widget and open our dialog
activator_button = A(T(self.button_text), _id=my_select_id+"_option_add_trigger")
#create javascript for creating and opening the dialog
js = '$( "#%s_dialog-form" ).dialog({autoOpen: false, show: "blind", hide: "explode", width: %s});' % (my_select_id, self.dialog_width)
js += '$( "#%s_option_add_trigger" ).click(function() { $( "#%s_dialog-form" ).dialog( "open" );return false;}); ' % (my_select_id, my_select_id) #decorate our activator button for good measure
js += '$(function() { $( "#%s_option_add_trigger" ).button({text: true, icons: { primary: "ui-icon-circle-plus"} }); });' % (my_select_id)
jq_script=SCRIPT(js, _type="text/javascript")
wrapper = DIV(_id=my_select_id+"_adder_wrapper")
wrapper.components.extend([select_widget, form_loader_div, activator_button, jq_script])
return wrapper
You assign the widget to a field using
#Initialize the widget
add_option = SELECT_OR_ADD_OPTION(form_title="Add a new something", controller="product", function="add_category", button_text = "Add New", dialog_width=500)
#assign widget to field
db.product.category_id.widget = add_option.widget
The widget accepts the following arguments:
- form_title: string. This will appear as the jQueryUI dialog's title. Default = "Add New"
- controller: string. Name of the controller that will handle record creation.
- function: string. Name of the function that will handle record creation. (Should both create form, accept it and be prepared to issue javascript to interact with widget - see below.)
- button_text: string. The text that should appear on the button that will activate our form dialog. Default is "Add".
- dialog_width: integer. The desired width in pixels of the dialog box. Default is 450
The Model
Define your database tables in models/db.py
db.define_table('category',
Field('name', 'string', notnull=True, unique=True),
Field('description', 'text')
)
db.define_table('product',
Field('category_id', db.category, requires=IS_IN_DB(db, 'category.id', 'category.name')),
Field('name', 'string', notnull=True),
Field('description', 'text'),
Field('price', 'decimal(10,2)', notnull=True)
)
Create your controller functions
def create():
#This is the main function, the one your users go to
#Initialize the widget
add_option = SELECT_OR_ADD_OPTION(form_title="Add new Product Category", controller="product", function="add_category", button_text = "Add New")
#assign widget to field
db.product.category_id.widget = add_option.widget
form = SQLFORM(db.product)
if form.accepts(request.vars, session):
response.flash = "New product created"
elif form.errors:
response.flash = "Please fix errors in form"
else:
response.flash = "Please fill in the form"
#you need jquery for the widget to work, include here or just put in your master layout.html
response.files.append("http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.9/jquery-ui.js")
response.files.append("http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.9/themes/smoothness/jquery-ui.css")
return dict(message="Create your product",
form = form)
def add_category():
#this is the controller function that will appear in our dialog
form = SQLFORM(db.category)
if form.accepts(request.vars):
#Successfully added new item
#do whatever else you may want
#Then let the user know adding via our widget worked
response.flash = T("Added")
target= request.args[0]
#close the widget's dialog box
response.js = '$( "#%s_dialog-form" ).dialog( "close" ); ' %(target)
#update the options they can select their new category in the main form
response.js += """$("#%s").append("<option value='%s'>%s</option>");""" \
% (target, form.vars.id, form.vars.name)
#and select the one they just added
response.js += """$("#%s").val("%s");""" % (target, form.vars.id)
#finally, return a blank form incase for some reason they wanted to add another option
return form
elif form.errors:
#silly user, just send back the form and it'll still be in our dialog box complete with error messages
return form
else:
#hasn't been submitted yet, just give them the fresh blank form
return form
Create Required Views
As of web2py 1.97.1, generic views have been disabled for security reason, so you'll need to explicitly create views for your create and add_category controller functions. (Thanks Richard for pointing this out!)
views/product/create.html
{{extend 'layout.html'}}
<h1>Create a New Product</h1>
{{=message}}
{{=form}}
views/product/add_category.load (I just created the new file via the admin interface & used the generic one web2py created for me, tweak as needed.)
{{if len(response._vars)==1:}}{{=response._vars.values()[0]}}{{else:}}{{=BEAUTIFY(response._vars)}}{{pass}}
Use with CRUD Forms
You can also use this widget with your crud forms, just remember to tell web2py to use our widget, also include the jqueryUI stuff. (Hint, if you're gonna use this lots just do the widget setup in the model & jqueryUI include in your main template.)
In your controller:
def create_crud():
from gluon.tools import Crud
crud = Crud(globals(), db)
add_option = SELECT_OR_ADD_OPTION(form_title="Add new Product Category", controller="product", function="add_category", button_text = "Add New")
#assign widget to field
db.product.category_id.widget = add_option.widget
form = crud.create(db.product)
#you need jquery for the widget to work, include here or just put in your master layout.html
response.files.append("http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.9/jquery-ui.js")
response.files.append("http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.9/themes/smoothness/jquery-ui.css")
return dict(form = form)
In Action
A form with the SELECT_OR_ADD_OPTION widget
Click on the "Add New" button and the dialog opens. (Hmm, can't type my own widget's name right!)
Submit and the new option is created and automatically selected in the main form.
Comments (12)
0
jurgis 11 years ago
Hi I made a newer version :)
http://www.web2pyslices.com/slice/show/1616/widget-select-or-add-option-ng
https://groups.google.com/forum/?fromgroups=#!topic/web2py/y4a5wedy6ks
0
naleenyadav 12 years ago
I am not getting the Add New Button, rather getting Add New & nothing happens on its click. I am using web2py 2.0 release.
0
lzacchetti-15312 12 years ago
just use following code to use supported jquery version:
response.files.append("http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.23/jquery-ui.js")
response.files.append("http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.23/themes/smoothness/jquery-ui.css")
0
lzacchetti-15312 12 years ago
The widget won't work properly in new web2py 2.0 release.
Trying to understand the reason...
0
johannspies 13 years ago
0
brianm 13 years ago
0
monotasker 13 years ago
0
simakwm 13 years ago
0
brianm 13 years ago
0
simakwm 13 years ago
0
brianm 13 years ago
0
richard 13 years ago