UNPKG

@atlassian/aui

Version:

Atlassian User Interface library

207 lines (179 loc) 6.22 kB
import $ from '../jquery'; import { triggerEvtForInst } from './event-handlers'; import { isEmpty, isEqual } from 'underscore'; import Backbone from 'backbone'; import events from './event-names'; /** * A class provided to fill some gaps with the out of the box Backbone.Model class. Most notiably the inability * to send ONLY modified attributes back to the server. */ var EntryModel = Backbone.Model.extend({ sync: function (method, model, options) { var instance = this; var oldError = options.error; options.error = function (xhr) { instance._serverErrorHandler(xhr, this); if (oldError) { oldError.apply(this, arguments); } }; return Backbone.sync.apply(Backbone, arguments); }, /** * Overrides default save handler to only save (send to server) attributes that have changed. * Also provides some default error handling. * * @override * @param attributes * @param options */ save: function (attributes, options) { options = options || {}; var instance = this; var Model; var syncModel; var error = options.error; // we override, so store original var success = options.success; // override error handler to provide some defaults options.error = function (model, xhr) { var data = $.parseJSON(xhr.responseText || xhr.data); // call original error handler if (error) { error.call(instance, instance, data, xhr); } }; // if it is a new model, we don't have to worry about updating only changed attributes because they are all new if (this.isNew()) { // call super Backbone.Model.prototype.save.call(this, attributes, options); // only go to server if something has changed } else if (attributes) { // create temporary model Model = EntryModel.extend({ url: this.url(), }); syncModel = new Model({ id: this.id, }); syncModel.save = Backbone.Model.prototype.save; options.success = function (model, xhr) { // update original model with saved attributes instance.clear().set(model.toJSON()); // call original success handler if (success) { success.call(instance, instance, xhr); } }; // update temporary model with the changed attributes syncModel.save(attributes, options); } }, /** * Destroys the model on the server. We need to override the default method as it does not support sending of * query paramaters. * * @override * @param {object} [options] * @param {function} options.success - Server success callback * @param {function} options.error - Server error callback * @param {object} options.data * * @return EntryModel */ destroy: function (options) { options = options || {}; var instance = this; var url = this.url(); $.ajax({ url: url, type: 'DELETE', dataType: 'json', data: options.data || {}, contentType: 'application/json', success(data) { if (instance.collection) { instance.collection.remove(instance); } if (options.success) { options.success.call(instance, data); } }, error(xhr) { instance._serverErrorHandler(xhr, this); if (options.error) { options.error.call(instance, xhr); } }, }); return this; }, /** * A more complex lookup for changed attributes then default backbone one. * * @param attributes */ changedAttributes: function (attributes) { var changed = {}; var current = this.toJSON(); $.each(attributes, function (name, value) { if (!current[name]) { if (typeof value === 'string') { if ($.trim(value) !== '') { changed[name] = value; } } else if ($.isArray(value)) { if (value.length !== 0) { changed[name] = value; } } else { changed[name] = value; } } else if (current[name] && current[name] !== value) { if (typeof value === 'object') { if (!isEqual(value, current[name])) { changed[name] = value; } } else { changed[name] = value; } } }); if (!isEmpty(changed)) { this.addExpand(changed); return changed; } }, /** * Useful point to override if you always want to add an expand to your rest calls. * * @param changed attributes that have already changed */ addExpand: function (changed) {}, // eslint-disable-line no-unused-vars /** * Throws a server error event unless user input validation error (status 400) * * @param xhr * @param ajaxOptions */ _serverErrorHandler: function (xhr, ajaxOptions) { var data; if (xhr.status !== 400) { data = $.parseJSON(xhr.responseText || xhr.data); triggerEvtForInst(events.SERVER_ERROR, this, [data, xhr, ajaxOptions]); } }, /** * Fetches values, with some generic error handling * * @override * @param options */ fetch: function (options) { options = options || {}; // clear the model, so we do not merge the old with the new this.clear(); // call super Backbone.Model.prototype.fetch.call(this, options); }, }); export default EntryModel;