Projet

Général

Profil

0001-forms-rewrite-jsonp-select-widget-with-separate-js-t.patch

Frédéric Péters, 18 mars 2019 16:11

Télécharger (12,4 ko)

Voir les différences:

Subject: [PATCH] forms: rewrite jsonp select widget with separate js &
 template (#31506)

 tests/test_form_pages.py                      |  12 +-
 wcs/qommon/form.py                            | 129 ++----------------
 wcs/qommon/static/js/qommon.forms.js          |  67 +++++++++
 .../qommon/forms/widgets/select_jsonp.html    |   9 ++
 wcs/root.py                                   |   5 +
 5 files changed, 93 insertions(+), 129 deletions(-)
 create mode 100644 wcs/qommon/templates/qommon/forms/widgets/select_jsonp.html
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
    // create an additional hidden field to hold the label of the selected
126
    // option, it is necessary as the server may not have any knowledge of
127
    // possible options.
128
    var $input_display_value = $('<input>', {
129
            type: 'hidden',
130
            name: $(elem).attr('name') + '_display',
131
            value: $(elem).data('initial-display-value')
132
    });
133
    $input_display_value.insertAfter($(elem));
134
    var options = {
135
      minimumInputLength: 1,
136
      formatResult: function(result) { return result.text; },
137
      language: {
138
        errorLoading: function() { return WCS_I18N.s2_errorloading; },
139
        noResults: function () { return WCS_I18N.s2_nomatches; },
140
        inputTooShort: function (input, min) { return WCS_I18N.s2_tooshort; },
141
        loadingMore: function () { return WCS_I18N.s2_loadmore; },
142
        searching: function () { return WCS_I18N.s2_searching; }
143
      }
144
    };
145
    if (!required) {
146
      options.placeholder = '...';
147
      options.allowClear = true;
148
    }
149
    options.ajax = {
150
      delay: 250,
151
      dataType: 'json',
152
      data: function(params) {
153
        return {q: params.term, page_limit: 10};
154
      },
155
      processResults: function (data, params) {
156
        return {results: data.data};
157
      },
158
      url: function() {
159
        var url = $(elem).data('select2-url');
160
        url = url.replace(/\[var_.+?\]/g, function(match, g1, g2) {
161
          // compatibility: if there are [var_...] references in the URL
162
          // replace them by looking for other select fields on the same
163
          // page.
164
          var related_select = $('#' + match.slice(1, -1));
165
          var value_container_id = $(related_select).data('valuecontainerid');
166
          return $('#' + value_container_id).val() || '';
167
        });
168
        return url;
169
      }
170
    };
171
    var select2 = $(elem).select2(options);
172
    $(elem).on('change', function() {
173
      // update _display hidden field with selected text
174
      var text = $(elem).find(':selected').first().text();
175
      $input_display_value.val(text);
176
    });
177
    if ($input_display_value.val()) {
178
      // if the _display hidden field was created with an initial value take it
179
      // and create a matching <option> in the real <select> widget, and use it
180
      // to set select2 initial state.
181
      var option = $('<option></option>', {value: $(elem).data('value')});
182
      option.appendTo($(elem));
183
      option.text($input_display_value.val());
184
      select2.val($(elem).data('value')).trigger('change');
185
      $(elem).select2('data', {id: $(elem).data('value'), text: $(elem).data('initial-display-value')});
186
    }
187
  });
121 188
});
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
-