0001-forms-rewrite-jsonp-select-widget-with-separate-js-t.patch
tests/test_form_pages.py | ||
---|---|---|
3875 | 3875 |
formdef.data_class().wipe() |
3876 | 3876 | |
3877 | 3877 |
resp = get_app(pub).get('/test/') |
3878 |
assert 'url: "http://remote.example.net/jsonp"' in resp.body
|
|
3878 |
assert 'data-select2-url="http://remote.example.net/jsonp"' in resp.body
|
|
3879 | 3879 |
assert 'select2.min.js' in resp.body |
3880 | 3880 | |
3881 |
formdef.fields = [ |
|
3882 |
fields.ItemField(id='1', label='string', type='item', |
|
3883 |
data_source={'type': 'jsonp', 'value': '[var_XXX]'}), |
|
3884 |
] |
|
3885 |
formdef.store() |
|
3886 | ||
3887 |
resp = get_app(pub).get('/test/') |
|
3888 |
assert 'url: function(' in resp.body |
|
3889 |
assert "wcs_base_url = '[var_XXX]';" in resp.body |
|
3890 | ||
3891 | 3881 |
def test_form_string_regex_field_submit(pub): |
3892 | 3882 |
formdef = create_formdef() |
3893 | 3883 |
formdef.fields = [fields.StringField(id='0', label='string', type='string', |
wcs/qommon/form.py | ||
---|---|---|
1791 | 1791 | |
1792 | 1792 | |
1793 | 1793 |
class JsonpSingleSelectWidget(Widget): |
1794 |
template_name = 'qommon/forms/widgets/select_jsonp.html' |
|
1795 | ||
1794 | 1796 |
def __init__(self, name, value=None, url=None, **kwargs): |
1795 |
Widget.__init__(self, name, value=value, **kwargs)
|
|
1797 |
super(JsonpSingleSelectWidget, self).__init__(name, value=value, **kwargs)
|
|
1796 | 1798 |
self.url = url |
1797 | 1799 | |
1798 | 1800 |
def add_media(self): |
1799 |
get_response().add_javascript(['jquery.js', 'select2.js']) |
|
1801 |
get_response().add_javascript(['jquery.js', '../../i18n.js', 'qommon.forms.js', 'select2.js'])
|
|
1800 | 1802 |
get_response().add_css_include('../js/select2/select2.css') |
1801 | 1803 | |
1802 |
def render_content(self):
|
|
1804 |
def get_display_value(self):
|
|
1803 | 1805 |
if self.value is None: |
1804 | 1806 |
value = None |
1805 | 1807 |
else: |
1806 | 1808 |
value = htmlescape(self.value) |
1807 |
r = TemplateIO(html=True) |
|
1808 | ||
1809 |
attrs = {'id': 'form_' + self.name} |
|
1810 |
r += htmltag('select', name=self.name, value=value, **attrs) |
|
1811 |
r += htmltext('</select>') |
|
1812 | ||
1813 |
attrs = {'id': 'form_' + self.name + '_display'} |
|
1814 |
if value and get_session().jsonp_display_values: |
|
1815 |
key = '%s_%s' % (self.url, value) |
|
1816 |
if key in get_session().jsonp_display_values: |
|
1817 |
attrs['value'] = get_session().jsonp_display_values.get(key) |
|
1818 |
r += htmltag('input', xml_end=True, type="hidden", name=self.name + '_display', **attrs) |
|
1819 |
initial_display_value = attrs.get('value') |
|
1820 | ||
1821 |
# init select2 widget |
|
1822 |
allowclear = "" |
|
1823 |
if not self.required: |
|
1824 |
allowclear = "placeholder: '...', allowClear: true," |
|
1825 | ||
1826 |
r += htmltext(""" |
|
1827 |
<script id="script_%(id)s"> |
|
1828 |
$(document).ready(function() { |
|
1829 |
var wcs_select2_%(id)s = $("#form_%(id)s").select2({ |
|
1830 |
minimumInputLength: 1, |
|
1831 |
%(allowclear)s |
|
1832 |
language: { |
|
1833 |
errorLoading: function() { return "%(errorloading)s"; }, |
|
1834 |
noResults: function () { return "%(nomatches)s"; }, |
|
1835 |
inputTooShort: function (input, min) { return "%(tooshort)s"; }, |
|
1836 |
loadingMore: function () { return "%(loadmore)s"; }, |
|
1837 |
searching: function () { return "%(searching)s"; }, |
|
1838 |
}, |
|
1839 |
ajax: { |
|
1840 |
""" % {'id': self.name, |
|
1841 |
'allowclear': allowclear, |
|
1842 |
'errorloading': _('The results could not be loaded'), |
|
1843 |
'nomatches': _('No matches found'), |
|
1844 |
'tooshort': _('Please enter more characters'), |
|
1845 |
'loadmore': _('Loading more results...'), |
|
1846 |
'searching': _('Searching...')}) |
|
1809 |
if not value or not get_session().jsonp_display_values: |
|
1810 |
return None |
|
1811 |
key = '%s_%s' % (self.url, value) |
|
1812 |
return get_session().jsonp_display_values.get(key) |
|
1847 | 1813 | |
1814 |
def get_select2_url(self): |
|
1848 | 1815 |
if Template.is_template_string(self.url): |
1849 | 1816 |
vars = get_publisher().substitutions.get_context_variables() |
1850 | 1817 |
# skip variables that were not set (None) |
... | ... | |
1852 | 1819 |
url = misc.get_variadic_url(self.url, vars, encode_query=False) |
1853 | 1820 |
else: |
1854 | 1821 |
url = self.url |
1855 | ||
1856 |
if not '[var_' in url: |
|
1857 |
# if the url is not parametric, set the url directly |
|
1858 |
r += htmltext("""url: "%(url)s",""" % {'url': url}) |
|
1859 |
else: |
|
1860 |
# otherwise declare that as a function. |
|
1861 |
r += htmltext("""url: function() { return $(this).data('select2').options.options.ajax.url; },""") |
|
1862 | ||
1863 |
# setting up the select2 widget continues here |
|
1864 |
r += htmltext(""" |
|
1865 |
delay: 250, |
|
1866 |
dataType: 'json', |
|
1867 |
data: function (params) { |
|
1868 |
return { |
|
1869 |
q: params.term, |
|
1870 |
page_limit: 10 |
|
1871 |
}; |
|
1872 |
}, |
|
1873 |
processResults: function (data, params) { |
|
1874 |
return {results: data.data}; |
|
1875 |
} |
|
1876 |
}, |
|
1877 |
formatResult: function(result) { return result.text; } |
|
1878 |
});""") |
|
1879 | ||
1880 |
if '[var_' in url: |
|
1881 |
# if this is a parametric url, store template url and hook to the |
|
1882 |
# appropriate onchange event to give the url to select2 |
|
1883 |
r += htmltext(""" |
|
1884 |
$("#form_%(id)s").data('select2').options.wcs_base_url = '%(url)s'; |
|
1885 |
""" % {'id': self.name, 'url': url}) |
|
1886 |
variables = re.findall(r'\[(var_.+?)\]', url) |
|
1887 |
r += htmltext(""" |
|
1888 |
function url_replace_%(id)s() { |
|
1889 |
var url = $("#form_%(id)s").data('select2').options.wcs_base_url;""" % {'id': self.name}) |
|
1890 |
for variable in variables: |
|
1891 |
r += htmltext(""" |
|
1892 |
selector = '#' + $('#%(variable)s').data('valuecontainerid'); |
|
1893 |
url = url.replace('[%(variable)s]', $(selector).val() || '');""" % {'variable': variable}) |
|
1894 |
r += htmltext(""" |
|
1895 |
$("#form_%(id)s").data('select2').options.options.ajax.url = url; |
|
1896 |
$("#form_%(id)s").data('select2').results.clear(); |
|
1897 |
} |
|
1898 |
""" % {'id': self.name} ) |
|
1899 |
for variable in variables: |
|
1900 |
r += htmltext(""" |
|
1901 |
$('#%(variable)s').change(url_replace_%(id)s); |
|
1902 |
$('#%(variable)s').change(); |
|
1903 |
""" % {'id': self.name, 'variable': variable}) |
|
1904 | ||
1905 |
# finish setting up select2, update the _display hidden field with the |
|
1906 |
# selected text |
|
1907 |
r += htmltext(""" |
|
1908 | ||
1909 |
$("#form_%(id)s").change(function() { |
|
1910 |
var text = $("#form_%(id)s").next().find('.select2-selection__rendered').text(); |
|
1911 |
if ($("#form_%(id)s").next().find('.select2-selection__rendered .select2-selection__clear').length) { |
|
1912 |
text = text.slice(1); |
|
1913 |
} |
|
1914 |
$('#form_%(id)s_display').val(text); |
|
1915 |
}); |
|
1916 | ||
1917 |
""" % {'id': self.name}) |
|
1918 | ||
1919 |
if initial_display_value: |
|
1920 |
r += htmltext(""" |
|
1921 |
var option = $('<option>').attr('value', "%(value)s").text("%(text)s"); |
|
1922 |
option.appendTo(wcs_select2_%(id)s); |
|
1923 |
wcs_select2_%(id)s.val("%(value)s").trigger('change'); |
|
1924 |
$("#form_%(id)s").select2("data", {id: "%(value)s", text: "%(text)s"}); |
|
1925 |
""" % {'id': self.name, 'value': self.value, 'text': initial_display_value}) |
|
1926 | ||
1927 |
r += htmltext("""});</script>""") |
|
1928 | ||
1929 |
return r.getvalue() |
|
1822 |
return url |
|
1930 | 1823 | |
1931 | 1824 |
def parse(self, request=None): |
1932 | 1825 |
if request and request.form.get(self.name) and request.form.get(self.name + '_display'): |
wcs/qommon/static/js/qommon.forms.js | ||
---|---|---|
118 | 118 |
$(this).parents('form').trigger('wcs:change', {modified_field: modified_field}); |
119 | 119 |
}); |
120 | 120 |
$('form div[data-live-source]').parents('form').trigger('wcs:change'); |
121 | ||
122 |
/* searchable select */ |
|
123 |
$('select[data-select2-url]').each(function(i, elem) { |
|
124 |
var required = $(elem).data('required'); |
|
125 |
var $input_display_value = $('<input>', { |
|
126 |
type: 'hidden', |
|
127 |
name: $(elem).attr('name') + '_display', |
|
128 |
value: $(elem).data('initial-display-value') |
|
129 |
}); |
|
130 |
$input_display_value.insertAfter($(elem)); |
|
131 |
var options = { |
|
132 |
minimumInputLength: 1, |
|
133 |
formatResult: function(result) { return result.text; }, |
|
134 |
language: { |
|
135 |
errorLoading: function() { return WCS_I18N.s2_errorloading; }, |
|
136 |
noResults: function () { return WCS_I18N.s2_nomatches; }, |
|
137 |
inputTooShort: function (input, min) { return WCS_I18N.s2_tooshort; }, |
|
138 |
loadingMore: function () { return WCS_I18N.s2_loadmore; }, |
|
139 |
searching: function () { return WCS_I18N.s2_searching; } |
|
140 |
} |
|
141 |
}; |
|
142 |
if (!required) { |
|
143 |
options.placeholder = '...'; |
|
144 |
options.allowCrear = true; |
|
145 |
} |
|
146 |
options.ajax = { |
|
147 |
delay: 250, |
|
148 |
dataType: 'json', |
|
149 |
data: function(params) { |
|
150 |
return {q: params.term, page_limit: 10}; |
|
151 |
}, |
|
152 |
processResults: function (data, params) { |
|
153 |
return {results: data.data}; |
|
154 |
}, |
|
155 |
url: function() { |
|
156 |
var url = $(elem).data('select2-url'); |
|
157 |
url = url.replace(/\[var_.+?\]/g, function(match, g1, g2) { |
|
158 |
// compatibility: if there are [var_...] references in the URL |
|
159 |
// replace them by looking for other select fields on the same |
|
160 |
// page. |
|
161 |
var related_select = $('#' + match.slice(1, -1)); |
|
162 |
var value_container_id = $(related_select).data('valuecontainerid'); |
|
163 |
return $('#' + value_container_id).val() || ''; |
|
164 |
}); |
|
165 |
return url; |
|
166 |
} |
|
167 |
}; |
|
168 |
var select2 = $(elem).select2(options); |
|
169 |
$(elem).on('change', function() { |
|
170 |
// update _display hidden field with selected text |
|
171 |
var text = $(elem).parent().find('.select2-selection__rendered').text(); |
|
172 |
if ($(elem).parent().find('.select2-selection__rendered .select2-selection__clear').length) { |
|
173 |
text = text.slice(1); |
|
174 |
} |
|
175 |
$input_display_value.val(text); |
|
176 |
}); |
|
177 |
if ($input_display_value.val()) { |
|
178 |
var option = $('<option></option>', {value: $(elem).data('value')}); |
|
179 |
option.appendTo($(elem)); |
|
180 |
option.text($input_display_value.val()); |
|
181 |
select2.val($(elem).data('value')).trigger('change'); |
|
182 |
$(elem).select2('data', {id: $(elem).data('value'), text: $(elem).data('initial-display-value')}); |
|
183 |
} |
|
184 |
}); |
|
121 | 185 |
}); |
wcs/qommon/templates/qommon/forms/widgets/select_jsonp.html | ||
---|---|---|
1 |
{% extends "qommon/forms/widget.html" %} |
|
2 |
{% block widget-control %} |
|
3 |
<select id="form_{{widget.name}}" name="{{widget.name}}" |
|
4 |
data-select2-url="{{widget.get_select2_url}}" |
|
5 |
{% if widget.value %}data-value="{{ widget.value }}"{% endif %} |
|
6 |
data-required="{% if widget.is_required %}true{% endif %}" |
|
7 |
data-initial-display-value="{{widget.get_display_value|default_if_none:''}}"> |
|
8 |
</select> |
|
9 |
{% endblock %} |
wcs/root.py | ||
---|---|---|
372 | 372 |
'map_zoom_in': _('Zoom in'), |
373 | 373 |
'map_zoom_out': _('Zoom out'), |
374 | 374 |
'map_display_position': _('Display my position'), |
375 |
's2_errorloading': _('The results could not be loaded'), |
|
376 |
's2_nomatches': _('No matches found'), |
|
377 |
's2_tooshort': _('Please enter more characters'), |
|
378 |
's2_loadmore': _('Loading more results...'), |
|
379 |
's2_searching': _('Searching...'), |
|
375 | 380 |
} |
376 | 381 |
return 'WCS_I18N = %s;\n' % json.dumps(strings) |
377 | 382 | |
378 |
- |