forms: rewrite jsonp select widget with separate js & template (#31506)
This commit is contained in:
parent
f841ceb34f
commit
416f4c5b6b
|
@ -3875,19 +3875,9 @@ def test_form_jsonp_item_field(http_requests, pub):
|
|||
formdef.data_class().wipe()
|
||||
|
||||
resp = get_app(pub).get('/test/')
|
||||
assert 'url: "http://remote.example.net/jsonp"' in resp.body
|
||||
assert 'data-select2-url="http://remote.example.net/jsonp"' in resp.body
|
||||
assert 'select2.min.js' in resp.body
|
||||
|
||||
formdef.fields = [
|
||||
fields.ItemField(id='1', label='string', type='item',
|
||||
data_source={'type': 'jsonp', 'value': '[var_XXX]'}),
|
||||
]
|
||||
formdef.store()
|
||||
|
||||
resp = get_app(pub).get('/test/')
|
||||
assert 'url: function(' in resp.body
|
||||
assert "wcs_base_url = '[var_XXX]';" in resp.body
|
||||
|
||||
def test_form_string_regex_field_submit(pub):
|
||||
formdef = create_formdef()
|
||||
formdef.fields = [fields.StringField(id='0', label='string', type='string',
|
||||
|
|
|
@ -1790,60 +1790,27 @@ class RankedItemsWidget(CompositeWidget):
|
|||
|
||||
|
||||
class JsonpSingleSelectWidget(Widget):
|
||||
template_name = 'qommon/forms/widgets/select_jsonp.html'
|
||||
|
||||
def __init__(self, name, value=None, url=None, **kwargs):
|
||||
Widget.__init__(self, name, value=value, **kwargs)
|
||||
super(JsonpSingleSelectWidget, self).__init__(name, value=value, **kwargs)
|
||||
self.url = url
|
||||
|
||||
def add_media(self):
|
||||
get_response().add_javascript(['jquery.js', 'select2.js'])
|
||||
get_response().add_javascript(['jquery.js', '../../i18n.js', 'qommon.forms.js', 'select2.js'])
|
||||
get_response().add_css_include('../js/select2/select2.css')
|
||||
|
||||
def render_content(self):
|
||||
def get_display_value(self):
|
||||
if self.value is None:
|
||||
value = None
|
||||
else:
|
||||
value = htmlescape(self.value)
|
||||
r = TemplateIO(html=True)
|
||||
|
||||
attrs = {'id': 'form_' + self.name}
|
||||
r += htmltag('select', name=self.name, value=value, **attrs)
|
||||
r += htmltext('</select>')
|
||||
|
||||
attrs = {'id': 'form_' + self.name + '_display'}
|
||||
if value and get_session().jsonp_display_values:
|
||||
key = '%s_%s' % (self.url, value)
|
||||
if key in get_session().jsonp_display_values:
|
||||
attrs['value'] = get_session().jsonp_display_values.get(key)
|
||||
r += htmltag('input', xml_end=True, type="hidden", name=self.name + '_display', **attrs)
|
||||
initial_display_value = attrs.get('value')
|
||||
|
||||
# init select2 widget
|
||||
allowclear = ""
|
||||
if not self.required:
|
||||
allowclear = "placeholder: '...', allowClear: true,"
|
||||
|
||||
r += htmltext("""
|
||||
<script id="script_%(id)s">
|
||||
$(document).ready(function() {
|
||||
var wcs_select2_%(id)s = $("#form_%(id)s").select2({
|
||||
minimumInputLength: 1,
|
||||
%(allowclear)s
|
||||
language: {
|
||||
errorLoading: function() { return "%(errorloading)s"; },
|
||||
noResults: function () { return "%(nomatches)s"; },
|
||||
inputTooShort: function (input, min) { return "%(tooshort)s"; },
|
||||
loadingMore: function () { return "%(loadmore)s"; },
|
||||
searching: function () { return "%(searching)s"; },
|
||||
},
|
||||
ajax: {
|
||||
""" % {'id': self.name,
|
||||
'allowclear': allowclear,
|
||||
'errorloading': _('The results could not be loaded'),
|
||||
'nomatches': _('No matches found'),
|
||||
'tooshort': _('Please enter more characters'),
|
||||
'loadmore': _('Loading more results...'),
|
||||
'searching': _('Searching...')})
|
||||
if not value or not get_session().jsonp_display_values:
|
||||
return None
|
||||
key = '%s_%s' % (self.url, value)
|
||||
return get_session().jsonp_display_values.get(key)
|
||||
|
||||
def get_select2_url(self):
|
||||
if Template.is_template_string(self.url):
|
||||
vars = get_publisher().substitutions.get_context_variables()
|
||||
# skip variables that were not set (None)
|
||||
|
@ -1851,81 +1818,7 @@ var wcs_select2_%(id)s = $("#form_%(id)s").select2({
|
|||
url = misc.get_variadic_url(self.url, vars, encode_query=False)
|
||||
else:
|
||||
url = self.url
|
||||
|
||||
if not '[var_' in url:
|
||||
# if the url is not parametric, set the url directly
|
||||
r += htmltext("""url: "%(url)s",""" % {'url': url})
|
||||
else:
|
||||
# otherwise declare that as a function.
|
||||
r += htmltext("""url: function() { return $(this).data('select2').options.options.ajax.url; },""")
|
||||
|
||||
# setting up the select2 widget continues here
|
||||
r += htmltext("""
|
||||
delay: 250,
|
||||
dataType: 'json',
|
||||
data: function (params) {
|
||||
return {
|
||||
q: params.term,
|
||||
page_limit: 10
|
||||
};
|
||||
},
|
||||
processResults: function (data, params) {
|
||||
return {results: data.data};
|
||||
}
|
||||
},
|
||||
formatResult: function(result) { return result.text; }
|
||||
});""")
|
||||
|
||||
if '[var_' in url:
|
||||
# if this is a parametric url, store template url and hook to the
|
||||
# appropriate onchange event to give the url to select2
|
||||
r += htmltext("""
|
||||
$("#form_%(id)s").data('select2').options.wcs_base_url = '%(url)s';
|
||||
""" % {'id': self.name, 'url': url})
|
||||
variables = re.findall(r'\[(var_.+?)\]', url)
|
||||
r += htmltext("""
|
||||
function url_replace_%(id)s() {
|
||||
var url = $("#form_%(id)s").data('select2').options.wcs_base_url;""" % {'id': self.name})
|
||||
for variable in variables:
|
||||
r += htmltext("""
|
||||
selector = '#' + $('#%(variable)s').data('valuecontainerid');
|
||||
url = url.replace('[%(variable)s]', $(selector).val() || '');""" % {'variable': variable})
|
||||
r += htmltext("""
|
||||
$("#form_%(id)s").data('select2').options.options.ajax.url = url;
|
||||
$("#form_%(id)s").data('select2').results.clear();
|
||||
}
|
||||
""" % {'id': self.name} )
|
||||
for variable in variables:
|
||||
r += htmltext("""
|
||||
$('#%(variable)s').change(url_replace_%(id)s);
|
||||
$('#%(variable)s').change();
|
||||
""" % {'id': self.name, 'variable': variable})
|
||||
|
||||
# finish setting up select2, update the _display hidden field with the
|
||||
# selected text
|
||||
r += htmltext("""
|
||||
|
||||
$("#form_%(id)s").change(function() {
|
||||
var text = $("#form_%(id)s").next().find('.select2-selection__rendered').text();
|
||||
if ($("#form_%(id)s").next().find('.select2-selection__rendered .select2-selection__clear').length) {
|
||||
text = text.slice(1);
|
||||
}
|
||||
$('#form_%(id)s_display').val(text);
|
||||
});
|
||||
|
||||
""" % {'id': self.name})
|
||||
|
||||
if initial_display_value:
|
||||
r += htmltext("""
|
||||
var option = $('<option>').attr('value', "%(value)s").text("%(text)s");
|
||||
option.appendTo(wcs_select2_%(id)s);
|
||||
wcs_select2_%(id)s.val("%(value)s").trigger('change');
|
||||
$("#form_%(id)s").select2("data", {id: "%(value)s", text: "%(text)s"});
|
||||
""" % {'id': self.name, 'value': self.value, 'text': initial_display_value})
|
||||
|
||||
r += htmltext("""});</script>""")
|
||||
|
||||
return r.getvalue()
|
||||
return url
|
||||
|
||||
def parse(self, request=None):
|
||||
if request and request.form.get(self.name) and request.form.get(self.name + '_display'):
|
||||
|
|
|
@ -118,4 +118,71 @@ $(function() {
|
|||
$(this).parents('form').trigger('wcs:change', {modified_field: modified_field});
|
||||
});
|
||||
$('form div[data-live-source]').parents('form').trigger('wcs:change');
|
||||
|
||||
/* searchable select */
|
||||
$('select[data-select2-url]').each(function(i, elem) {
|
||||
var required = $(elem).data('required');
|
||||
// create an additional hidden field to hold the label of the selected
|
||||
// option, it is necessary as the server may not have any knowledge of
|
||||
// possible options.
|
||||
var $input_display_value = $('<input>', {
|
||||
type: 'hidden',
|
||||
name: $(elem).attr('name') + '_display',
|
||||
value: $(elem).data('initial-display-value')
|
||||
});
|
||||
$input_display_value.insertAfter($(elem));
|
||||
var options = {
|
||||
minimumInputLength: 1,
|
||||
formatResult: function(result) { return result.text; },
|
||||
language: {
|
||||
errorLoading: function() { return WCS_I18N.s2_errorloading; },
|
||||
noResults: function () { return WCS_I18N.s2_nomatches; },
|
||||
inputTooShort: function (input, min) { return WCS_I18N.s2_tooshort; },
|
||||
loadingMore: function () { return WCS_I18N.s2_loadmore; },
|
||||
searching: function () { return WCS_I18N.s2_searching; }
|
||||
}
|
||||
};
|
||||
if (!required) {
|
||||
options.placeholder = '...';
|
||||
options.allowClear = true;
|
||||
}
|
||||
options.ajax = {
|
||||
delay: 250,
|
||||
dataType: 'json',
|
||||
data: function(params) {
|
||||
return {q: params.term, page_limit: 10};
|
||||
},
|
||||
processResults: function (data, params) {
|
||||
return {results: data.data};
|
||||
},
|
||||
url: function() {
|
||||
var url = $(elem).data('select2-url');
|
||||
url = url.replace(/\[var_.+?\]/g, function(match, g1, g2) {
|
||||
// compatibility: if there are [var_...] references in the URL
|
||||
// replace them by looking for other select fields on the same
|
||||
// page.
|
||||
var related_select = $('#' + match.slice(1, -1));
|
||||
var value_container_id = $(related_select).data('valuecontainerid');
|
||||
return $('#' + value_container_id).val() || '';
|
||||
});
|
||||
return url;
|
||||
}
|
||||
};
|
||||
var select2 = $(elem).select2(options);
|
||||
$(elem).on('change', function() {
|
||||
// update _display hidden field with selected text
|
||||
var text = $(elem).find(':selected').first().text();
|
||||
$input_display_value.val(text);
|
||||
});
|
||||
if ($input_display_value.val()) {
|
||||
// if the _display hidden field was created with an initial value take it
|
||||
// and create a matching <option> in the real <select> widget, and use it
|
||||
// to set select2 initial state.
|
||||
var option = $('<option></option>', {value: $(elem).data('value')});
|
||||
option.appendTo($(elem));
|
||||
option.text($input_display_value.val());
|
||||
select2.val($(elem).data('value')).trigger('change');
|
||||
$(elem).select2('data', {id: $(elem).data('value'), text: $(elem).data('initial-display-value')});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
{% extends "qommon/forms/widget.html" %}
|
||||
{% block widget-control %}
|
||||
<select id="form_{{widget.name}}" name="{{widget.name}}"
|
||||
data-select2-url="{{widget.get_select2_url}}"
|
||||
{% if widget.value %}data-value="{{ widget.value }}"{% endif %}
|
||||
data-required="{% if widget.is_required %}true{% endif %}"
|
||||
data-initial-display-value="{{widget.get_display_value|default_if_none:''}}">
|
||||
</select>
|
||||
{% endblock %}
|
|
@ -372,6 +372,11 @@ class RootDirectory(Directory):
|
|||
'map_zoom_in': _('Zoom in'),
|
||||
'map_zoom_out': _('Zoom out'),
|
||||
'map_display_position': _('Display my position'),
|
||||
's2_errorloading': _('The results could not be loaded'),
|
||||
's2_nomatches': _('No matches found'),
|
||||
's2_tooshort': _('Please enter more characters'),
|
||||
's2_loadmore': _('Loading more results...'),
|
||||
's2_searching': _('Searching...'),
|
||||
}
|
||||
return 'WCS_I18N = %s;\n' % json.dumps(strings)
|
||||
|
||||
|
|
Loading…
Reference in New Issue