diff options
| author | David Monjoie <dmo@odoo.com> | 2015-08-25 21:50:31 +0200 |
|---|---|---|
| committer | David Monjoie <dmo@odoo.com> | 2015-08-26 16:01:19 +0200 |
| commit | a77f5cf42faa75a2dd3931d83ad8ea86648248c0 (patch) | |
| tree | 29590b9351bf73ac573928be7f0dbaa5ea8505a8 /addons/website_form/static/src | |
| parent | 57c4787d09eacc548140d7a552a76264eae21a55 (diff) | |
[ADD] website_form: generic form controller
Contains a new ajax post function that comes from mdo's
original code and that I was not able to get rid of.
Diffstat (limited to 'addons/website_form/static/src')
| -rw-r--r-- | addons/website_form/static/src/js/website_form.js | 168 | ||||
| -rw-r--r-- | addons/website_form/static/src/less/website_form.less | 54 | ||||
| -rw-r--r-- | addons/website_form/static/src/xml/website_form.xml | 25 |
3 files changed, 247 insertions, 0 deletions
diff --git a/addons/website_form/static/src/js/website_form.js b/addons/website_form/static/src/js/website_form.js new file mode 100644 index 00000000000..88859643129 --- /dev/null +++ b/addons/website_form/static/src/js/website_form.js @@ -0,0 +1,168 @@ +odoo.define('website_form.animation', function (require) { +'use strict'; + + var core = require('web.core'); + var time = require('web.time'); + var ajax = require('web.ajax'); + var snippet_animation = require('web_editor.snippets.animation'); + + var _t = core._t; + var qweb = core.qweb; + + + snippet_animation.registry.form_builder_send = snippet_animation.Class.extend({ + selector: '.s_website_form', + + start: function() { + var self = this; + qweb.add_template('/website_form/static/src/xml/website_form.xml'); + this.$target.find('.o_website_form_send').on('click',function(e) {self.send(e);}); + + // Initialize datetimepickers + var l10n = _t.database.parameters; + this.$target.find('.o_website_form_datetime').datetimepicker({ + pickTime: true, + useSeconds: true, + startDate: moment({ y: 1900 }), + endDate: moment().add(200, "y"), + calendarWeeks: true, + icons : { + time: 'fa fa-clock-o', + date: 'fa fa-calendar', + up: 'fa fa-chevron-up', + down: 'fa fa-chevron-down' + }, + language : moment.locale(), + format : time.strftime_to_moment_format(l10n.date_format +' '+ l10n.time_format), + }); + }, + + stop: function() { + this.$target.find('button').off('click'); + }, + + send: function(e) { + e.preventDefault(); // Prevent the default submit behavior + + var self = this; + + self.$target.find('#o_website_form_result').empty(); + if (!self.check_error_fields([])) { + self.update_status('invalid'); + return false; + } + + // Prepare form inputs + this.form_fields = this.$target.serializeArray(); + _.each(this.$target.find('input[type=file]'), function(input) { + $.each($(input).prop('files'), function(index, file) { + // Index field name as ajax won't accept arrays of files + // when aggregating multiple files into a single field value + self.form_fields.push({ + name: input.name + '[' + index + ']', + value: file + }); + }); + }); + + // Serialize form inputs into a single object + // Aggregate multiple values into arrays + var form_values = {}; + _.each(this.form_fields, function(input) { + if (input.name in form_values) { + // If a value already exists for this field, + // we are facing a x2many field, so we store + // the values in an array. + if (Array.isArray(form_values[input.name])) { + form_values[input.name].push(input.value); + } else { + form_values[input.name] = [form_values[input.name], input.value]; + } + } else { + if (input.value != '') { + form_values[input.name] = input.value; + } + } + }); + + // Overwrite form_values array with values from the form tag + // Necessary to handle field values generated server-side, since + // using t-att- inside a snippet makes it non-editable ! + for (var key in this.$target.data()) { + if (_.str.startsWith(key, 'form_field_')){ + form_values[key.replace('form_field_', '')] = this.$target.data(key); + } + } + + // Post form and handle result + ajax.post(this.$target.attr('action') + this.$target.data('model_name'), form_values) + .then(function(result_data) { + result_data = $.parseJSON(result_data); + if(!result_data.id) { + // Failure, the server didn't return the created record ID + self.update_status('error'); + if (result_data.error_fields && result_data.error_fields.length) { + // If the server return a list of bad fields, show these fields for users + self.check_error_fields(result_data.error_fields); + } + } else { + // Success, redirect or update status + var success_page = self.$target.attr('data-success_page'); + if(success_page) { + $(location).attr('href', success_page); + } + else { + self.update_status('success'); + } + } + }) + .fail(function(result_data){ + self.update_status('error'); + }); + }, + + check_error_fields: function(error_fields) { + var form_valid = true; + // Loop on all fields + this.$target.find('.form-field').each(function(k, field){ + var $field = $(field); + var $fields = self.$fields; + var field_name = $field.find('.control-label').attr('for') + + // Validate inputs for this field + var field_valid = true; + var inputs = $field.find('.o_website_form_input'); + var invalid_inputs = inputs.toArray().filter(function(input, k, inputs) { + // Special check for multiple required checkbox for same + // field as it seems checkValidity forces every required + // checkbox to be checked, instead of looking at other + // checkboxes with the same name and only requiring one + // of them to be checked. + if (input.required && input.type == 'checkbox') { + // Considering we are currently processing a single + // field, we can assume that all checkboxes in the + // inputs variable have the same name + var checkboxes = _.filter(inputs, function(input){ + return input.required && input.type == 'checkbox' + }) + return !_.any(checkboxes, function(checkbox){return checkbox.checked}) + } else { + return !input.checkValidity(); + } + }) + + // Update field color if invalid or erroneous + $field.removeClass('has-error'); + if(invalid_inputs.length || error_fields.indexOf(field_name) >= 0){ + $field.addClass('has-error'); + form_valid = false; + } + }); + return form_valid; + }, + + update_status: function(status) { + this.$target.find('#o_website_form_result').replaceWith(qweb.render("website_form.status_" + status)) + }, + }); +}); diff --git a/addons/website_form/static/src/less/website_form.less b/addons/website_form/static/src/less/website_form.less new file mode 100644 index 00000000000..f26718da9c1 --- /dev/null +++ b/addons/website_form/static/src/less/website_form.less @@ -0,0 +1,54 @@ +// Radio buttons and checkboxes flex layout +.o_website_form_flex { + display: flex; + flex-wrap: wrap; +} + +.o_website_form_flex_item { + // col-md-4 + flex-basis: 33%; + + // col-sm-6 + @media (max-width: @screen-sm-max) { + flex-basis: 50%; + } + + // col-xs-12 + @media (max-width: @screen-xs-max) { + flex-basis: 100%; + } +} + +// Hidden field is only partially hidden in editor +.o_website_form_field_hidden { + display: none; +} + +.editor_enable .o_website_form_field_hidden { + display: block; + opacity: 0.5; +} + +// Select inputs do not trigger the default browser behavior +// Since we use a custom editable element +.editor_enable .s_website_form .form-field select { + pointer-events: none; +} + +// Required fields have a star which is not part of the field label +.o_website_form_required, .o_website_form_required_custom { + .control-label:after { + content: ' *'; + } +} + +// Quickfix to display the editable select as a single big field +#editable_select.form-control { + height: 100%; +} + +// Fix for firefox browse button which is too big for Bootstrap form-field +// http://stackoverflow.com/questions/22049739/fix-for-firefox-file-input-using-bootstrap-3-1 +.form-field input[type=file].form-control { + height: 100%; +} diff --git a/addons/website_form/static/src/xml/website_form.xml b/addons/website_form/static/src/xml/website_form.xml new file mode 100644 index 00000000000..3a1102bb5e1 --- /dev/null +++ b/addons/website_form/static/src/xml/website_form.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<templates xml:space="preserve"> + + <!-- Success status --> + <t t-name="website_form.status_success"> + <span id="o_website_form_result" class="text-success ml8"> + <i class="fa fa-check mr4"/>The form has been sent successfully. + </span> + </t> + + <!-- Missing field status --> + <t t-name="website_form.status_invalid"> + <span id="o_website_form_result" class="text-danger ml8"> + <i class="fa fa-close mr4"/>Please fill in the form correctly. + </span> + </t> + + <!-- Error status --> + <t t-name="website_form.status_error"> + <span id="o_website_form_result" class="text-danger ml8"> + <i class="fa fa-close mr4"/>An error has occured, the form has not been sent. + </span> + </t> + +</templates> |
