|
| 1 | +/* globals jQuery */ |
| 2 | + |
| 3 | +/****************************************************************** |
| 4 | + * Author: Dipcode |
| 5 | + * |
| 6 | + * Django forms ajax submission |
| 7 | + * Example: |
| 8 | + * <form data-ajax-submit> |
| 9 | + *****************************************************************/ |
| 10 | + |
| 11 | + |
| 12 | + |
| 13 | +(function($) { |
| 14 | + "use strict"; |
| 15 | + |
| 16 | + function CacheFile(name, filename, file) { |
| 17 | + this.name = name; |
| 18 | + this.filename = filename; |
| 19 | + this.file = new Blob([file]); |
| 20 | + } |
| 21 | + |
| 22 | + function ManageCacheFile($form) { |
| 23 | + this.$form = $form; |
| 24 | + this.$inputFiles = this.$form.find("input[type='file']"); |
| 25 | + this.cachedFiles = {}; |
| 26 | + this.bindEvents(); |
| 27 | + } |
| 28 | + |
| 29 | + ManageCacheFile.prototype = { |
| 30 | + |
| 31 | + getFormData: function getFormData() |
| 32 | + { |
| 33 | + this.$inputFiles.prop('disabled', true); |
| 34 | + |
| 35 | + var data = new FormData(this.$form.get(0)); |
| 36 | + |
| 37 | + for (var i in this.cachedFiles) { |
| 38 | + var cachedFile = this.cachedFiles[i]; |
| 39 | + data.append(cachedFile.name, cachedFile.file, cachedFile.filename); |
| 40 | + } |
| 41 | + |
| 42 | + this.$inputFiles.prop('disabled', false); |
| 43 | + |
| 44 | + return data; |
| 45 | + }, |
| 46 | + |
| 47 | + bindEvents: function bindEvents() |
| 48 | + { |
| 49 | + var self = this; |
| 50 | + |
| 51 | + this.$form.find("input[type='file']").on('change', function(e) |
| 52 | + { |
| 53 | + var name = $(this).attr('name'), |
| 54 | + file = e.target.files[0], |
| 55 | + reader = new FileReader(); |
| 56 | + |
| 57 | + if (file !== undefined) { |
| 58 | + reader.onload = function(evt) { |
| 59 | + self.cachedFiles[name] = new CacheFile(name, file.name, evt.target.result); |
| 60 | + }; |
| 61 | + |
| 62 | + reader.readAsBinaryString(file); |
| 63 | + } |
| 64 | + else if (name in self.cachedFiles) { |
| 65 | + delete self.cachedFiles[name]; |
| 66 | + } |
| 67 | + }); |
| 68 | + } |
| 69 | + }; |
| 70 | + |
| 71 | + var methods = { |
| 72 | + submit: function (data) { |
| 73 | + var daf = this.data('daf-data'); |
| 74 | + if (daf) { |
| 75 | + daf.submit(data); |
| 76 | + } |
| 77 | + } |
| 78 | + }; |
| 79 | + |
| 80 | + $.fn.djangoAjaxForms = function(options) |
| 81 | + { |
| 82 | + var opts = $.extend({ |
| 83 | + fieldIdSelector: "div_id_", |
| 84 | + fieldErrorClass: "form-control-feedback", |
| 85 | + errorClass: "has-danger", |
| 86 | + cacheFilesAttr: "[data-ajax-submit-cachefiles]", |
| 87 | + canSubmitFn: null, |
| 88 | + onRenderErrorFn: null, |
| 89 | + handleSubmitEvent: true, |
| 90 | + }, $.fn.djangoAjaxForms.defaults, options); |
| 91 | + |
| 92 | + function DjangoAjaxForms($form) |
| 93 | + { |
| 94 | + this.$form = $form; |
| 95 | + this.$form.data('daf-data', this); |
| 96 | + |
| 97 | + if (opts.handleSubmitEvent) { |
| 98 | + var self = this, |
| 99 | + canSubmit = true; |
| 100 | + |
| 101 | + this.$form.on('submit', function(e) { |
| 102 | + e.preventDefault(); |
| 103 | + |
| 104 | + if ( $.isFunction( opts.canSubmitFn ) ) { |
| 105 | + canSubmit = opts.canSubmitFn(self.$form); |
| 106 | + } |
| 107 | + |
| 108 | + if (self.$form.length > 0 && canSubmit) { |
| 109 | + self.submit(); |
| 110 | + } |
| 111 | + }); |
| 112 | + } |
| 113 | + |
| 114 | + if (this.$form.filter(opts.cacheFilesAttr).length) { |
| 115 | + this.cachedFiles = new ManageCacheFile(this.$form); |
| 116 | + } |
| 117 | + } |
| 118 | + |
| 119 | + DjangoAjaxForms.prototype = { |
| 120 | + |
| 121 | + request: function request(url, data, isCustomData) |
| 122 | + { |
| 123 | + var options = { |
| 124 | + data: data, |
| 125 | + method: 'POST', |
| 126 | + dataType: 'json' |
| 127 | + }; |
| 128 | + |
| 129 | + if ( !isCustomData ) { |
| 130 | + options.contentType = false; |
| 131 | + options.processData = false; |
| 132 | + } |
| 133 | + |
| 134 | + return $.ajax(url, options); |
| 135 | + }, |
| 136 | + |
| 137 | + submit: function submit(customData) |
| 138 | + { |
| 139 | + var self = this; |
| 140 | + |
| 141 | + this.$form.trigger("ajaxforms:beforesubmit"); |
| 142 | + |
| 143 | + var url = this.$form.attr("action") || window.location.href; |
| 144 | + var disabled_fields = this.$form.find(":input:disabled"); |
| 145 | + var data = customData; |
| 146 | + var isCustomData = true; |
| 147 | + |
| 148 | + if (customData === undefined) { |
| 149 | + data = new FormData(this.$form.get(0)); |
| 150 | + isCustomData = false; |
| 151 | + } |
| 152 | + |
| 153 | + if (this.$form.filter(opts.cacheFilesAttr).length) { |
| 154 | + data = this.cachedFiles.getFormData(); |
| 155 | + } |
| 156 | + |
| 157 | + this.$form.find(':input').prop('disabled', true); |
| 158 | + this.$form.trigger("ajaxforms:submit"); |
| 159 | + |
| 160 | + return this.request(url, data, isCustomData) |
| 161 | + |
| 162 | + .done(function(response) { |
| 163 | + self.$form.trigger("ajaxforms:submitsuccess"); |
| 164 | + self.$form.trigger('form:submit:success'); |
| 165 | + |
| 166 | + if( response.action ){ |
| 167 | + self.processResponse(response.action, response.action_url); |
| 168 | + } |
| 169 | + }) |
| 170 | + |
| 171 | + .fail(function ($xhr) { |
| 172 | + var response = $xhr.responseJSON; |
| 173 | + |
| 174 | + if (response && response.hasOwnProperty('extra_data')) { |
| 175 | + var errors_list = response.extra_data.errors_list; |
| 176 | + |
| 177 | + self.processFormErrors(self.$form, errors_list); |
| 178 | + } else { |
| 179 | + self.$form.trigger("ajaxforms:fail"); |
| 180 | + } |
| 181 | + |
| 182 | + self.$form.find(':input').not(disabled_fields).prop('disabled', false); |
| 183 | + }) |
| 184 | + |
| 185 | + .always(function() { |
| 186 | + self.$form.trigger("ajaxforms:submitdone"); |
| 187 | + }); |
| 188 | + }, |
| 189 | + |
| 190 | + processFormErrors: function processFormErrors($form, errors_list) |
| 191 | + { |
| 192 | + var $wrappers = $form.find("[id^='" + opts.fieldIdSelector + "']"); |
| 193 | + var nonfielderror = false; |
| 194 | + |
| 195 | + $wrappers.removeClass(opts.errorClass).find("." + opts.fieldErrorClass).remove(); |
| 196 | + |
| 197 | + for (var fieldName in errors_list) { |
| 198 | + var errors = errors_list[fieldName]; |
| 199 | + |
| 200 | + if (fieldName.search("__all__") >= 0) { |
| 201 | + $form.trigger("ajaxforms:nonfielderror", [errors]); |
| 202 | + nonfielderror = true; |
| 203 | + } else { |
| 204 | + var $field = $form.find("#" + opts.fieldIdSelector + fieldName); |
| 205 | + |
| 206 | + var onChange = function () { |
| 207 | + $field.removeClass('error', 200).find('.errorlist').fadeOut(200, function () { |
| 208 | + $(this).remove(); |
| 209 | + }); |
| 210 | + }; |
| 211 | + |
| 212 | + $field.addClass(opts.errorClass).append(this.renderErrorList(errors)); |
| 213 | + $field.one('change', onChange); |
| 214 | + } |
| 215 | + } |
| 216 | + if ( !nonfielderror ){ |
| 217 | + $form.trigger("ajaxforms:fielderror"); |
| 218 | + } |
| 219 | + }, |
| 220 | + |
| 221 | + processResponse: function processResponse(action, value) |
| 222 | + { |
| 223 | + switch (action) { |
| 224 | + case 'refresh': |
| 225 | + window.location.reload(true); |
| 226 | + break; |
| 227 | + case 'redirect': |
| 228 | + window.location.href = value; |
| 229 | + break; |
| 230 | + default: |
| 231 | + return; |
| 232 | + } |
| 233 | + }, |
| 234 | + |
| 235 | + renderErrorList: function renderErrorList(errorsList) |
| 236 | + { |
| 237 | + var $elem = $("<div>").addClass(opts.fieldErrorClass).text(errorsList.join(', ')); |
| 238 | + |
| 239 | + if ( $.isFunction( opts.onRenderErrorFn ) ) { |
| 240 | + $elem = opts.onRenderErrorFn( $elem, errorsList ); |
| 241 | + } |
| 242 | + |
| 243 | + return $elem; |
| 244 | + } |
| 245 | + }; |
| 246 | + |
| 247 | + if ( methods[options] ) { |
| 248 | + return methods[ options ].apply( this, Array.prototype.slice.call( arguments, 1 )); |
| 249 | + } else if ( typeof options === 'object' || ! options ) { |
| 250 | + return this.each(function() |
| 251 | + { |
| 252 | + var $this = $(this); |
| 253 | + var daf = $this.data('daf-data'); |
| 254 | + |
| 255 | + if (!daf) { |
| 256 | + new DjangoAjaxForms($this); |
| 257 | + } |
| 258 | + }); |
| 259 | + } else { |
| 260 | + $.error( 'Method ' + options + ' does not exist.' ); |
| 261 | + } |
| 262 | + }; |
| 263 | + |
| 264 | + $.fn.djangoAjaxForms.defaults = {}; |
| 265 | + |
| 266 | +})(jQuery); |
0 commit comments