WTF are javascript mixins in smartUI?

WTF are javascript mixins in smartUI?

When it comes to advanced class composition, JavaScript has quite a number of approaches - a true smorgasbord of options. A type of pattern that is rarely spotted in the wild is the mixin-based inheritance pattern.Don't complain but mixins can sometimes be quite dense to write and comprehend. But they come with a bunch of features that are worth looking into.

The mixin pattern - as the name suggests - is a pattern of mixing together an object with other objects to add properties we need. Think of it like add-ons that can give your object additional properties, but these individual properties are not really subclasses themselves.

On the surface, mixins behave like object mixing layers, where we pass-in the target (the mixin) and the source. The target is appended to the source and a new object is returned.

A more accurate description is that a mixin works as factory where new a subclass object is returned. Through this whole process there is no definition of the subclass anywhere.

Example

//The swim property here is the mixin
let swim = {
  location() {    console.log(`Heading ${this.direction} at ${this.speed}`);
  }
};
let Alligator = function(speed, direction) {
  this.speed = speed,
  this.direction = direction
};
//This is our source object
let alligator = new Alligator('20 mph','North');
alligator = Object.assign(alligator, swim);
console.log(alligator.location());        

In the above snippet, we want to create an alligator that can swim. So we create a new alligator and then give it the swim feature. The swim object is the mixin or an extension that we want the alligator object to have using the Object.assign method.

Usage in smartUI

Mixins add functionality to the view or model prototypes of Marionette. They use the static method

–MyMixin.mixin(MyView.prototype)

?Mixins für views:

LayoutViewEventsPropagationMixin

ViewEventsPropagationMixin

?Some Mixins for models:

AutoFetchableMixin

NodeAutoFetchableMixin

ConnectableMixin

NodeConnectableMixin

FetchableMixin

ResourceMixin

NodeResourceMixin

UploadableMixin

CommandableMixin

ExpandableMixin

AdditionalResourcesV2Mixin

CommandableV2Mixin

ExpandableV2Mixin

FieldsV2Mixin


Now lets talk on the mixins in the SDK

LayoutViewEventsPropagationMixin

Propagates (re-triggers) this events to the child views shown in the inner regions. Marionette.LayoutView does not do this.

‘before:show’

‘show’?

‘dom:refresh’

The propagateEventsToRegions should be called anytime after the parent Marionette.LayoutView constructor and before the view can be shown.

var MyView = Marionette.LayoutView.extend({

  constructor: function MyView(options) {
    Marionette.LayoutView.prototype.constructor.apply(this, arguments);

    // Cause the show events triggered on the parent view re-triggered
    // on the views placed in the inner regions 
    this.propagateEventsToRegions();
  }

});

// Add the mixin functionality to the target view
LayoutViewEventsPropagationMixin.mixin(MyView.prototype);;        

ViewEventsPropagationMixin

Propagates (re-triggers) the ‘dom:refresh’ event to the child views.

If you build a complex view and maintain the child views on your own, you usually need to re-trigger the ‘dom-refresh’ event to inform the views, that the parent view was placed into the HTML document.

Every created child view should be connected to propagateEventsToViews; anytime between their construction and showing the parent view.

Removing the child view requires to cancel the event propagation by passing the child view into the cancelEventsToViewsPropagation.

var MyView = Marionette.View.extend({

  render: function () {
    this.firstView = new FirstView(...);
    this.secondView = new SecondView(...);

    // Register the two child views, so that they get the events propagated 
    this.propagateEventsToViews(this.firstView, this.secondView);

    this.$el
      .html('')
      .append(this.firstView.render().el)
      .append(this.secondView.render().el);
  }

});

// Add the mixin functionality to the target view
ViewEventsPropagationMixin.mixin(MyView.prototype);        

AutoFetchableMixin

Check, if the model can be fetched.

Fetch the model automatically, when the identifier changes (if requested)

var MyModel = Backbone.Model.extend({

  constructor: function MyModel(attributes, options) {
    Backbone.Model.prototype.constructor.apply(this, arguments);
    this
      .makeConnectable(options)
      .makeFetchable(options)
      .makeAutoFetchable(options);
  }

});

ConnectableMixin.mixin(MyModel.prototype);
FetchableMixin.mixin(MyModel.prototype);
AutoFetchableMixin.mixin(MyModel.prototype);
// Enable watching for the model identifier changes
var model = new MyModel(undefined, {
      connector: connector,
      autofetch: true
    });

// A fetch will take place, notifying the event listeners about its progress
model.set('id', 2000);        


NodeAutoFetchableMixin

check if the model or collection can be fetched; it checks only if the related node is fetchable by default

fetch the model or collection automatically when the identifier of the related node changes (if requested); the default event to listen to is ‘change:id’

var MyCollection = Backbone.Collection.extend({

  constructor: function MyCollection(models, options) {
    Backbone.Collection.prototype.constructor.apply(this, arguments);
    this
      .makeNodeConnectable(options)
      .makeFetchable(options)
      .makeNodeAutoFetchable(options);
  }

});

NodeConnectableMixin.mixin(MyCollection.prototype);
FetchableMixin.mixin(MyCollection.prototype);
NodeAutoFetchableMixin.mixin(MyCollection.prototype);
// Enable watching for the model identifier changes
var connector = new Connector(...),
    node = new NodeModel({id: 2000}, {connector: connector}),
    collection = new MyCollection(undefined, {
      node: node,
      autofetch: true
    });

// A fetch of the collection will take place, notifying the event
// listeners about its progress
node.set('id', 2000);        


ConnectableMixin

Provides support for server connection using the? connector to fetch or update the target model or collection

This mixin overrides the _prepareModel method and calls the original implementation afterwards. If you supply your own custom implementation of this method, or use another mixin which overrides it, you should apply this mixin after yours.

This mixin is very important for all models trying to send JSON data to the content server. The content server can not deal with incoming JSON arrays, so without adding the mixin to the prototype and making the actual model connectable, the JSON array send to the server will never appear there


var MyModel = Backbone.Model.extend({

  constructor: function MyModel(attributes, options) {
    Backbone.Model.prototype.constructor.apply(this, arguments);
    this.makeConnectable(options);
  },

  url: function () {
    return Url.combine(this.connector.connection.url, 'myresource');
  }

});
// Specify the connector when creating the model
var connector = new Connector(...),
    model = new MyModel(undefined, {connector: connector});
model.fetch();

// Set the connector after creating the model
connector.assignTo(model);
model.fetch();        

NodeConnectableMixin

Provides support for the server connection via a node, represented by NodeModel, its descendant or by other class with the ConnectableMixin applied.

Many resources are associated with a node, which can be a parent, for example, or in other relation. The node provides the Connector to update the target model or collection and its identifier usually takes part in the resource URL.

This mixin overrides the _prepareModel method and calls the original implementation afterwards. If you supply your own custom implementation of this method, or use another mixin which overrides it, you should apply this mixin after yours.

var MyCollection = Backbone.Collection.extend({

  constructor: function MyCollection(models, options) {
    Backbone.Collection.prototype.constructor.apply(this, arguments);
    this.makeNodeConnectable(options);
  },

  url: function () {
    return Url.combine(this.node.urlBase(), 'mysubresources');
  }

});

NodeConnectableMixin.mixin(MyCollection.prototype);
// Specify the connector when creating the model
var connector = new Connector(...),
    node = new NodeModel({id: 2000}, {connector: connector}),
    collection = new MyCollection(undefined, {node: node});
collection.fetch();        

FetchableMixin

Makes the calls to the fetch method more robust by:

–preventing multiple server calls when the fetch is called quickly one call after another

–setting the reset option automatically to optimize collections loading (if requested)

–checking, if the model has already been fetched and ensuring, that a model has always been fetched before it is used

This mixin overrides the fetch method and calls the original implementation from it. If you supply your own custom implementation of this method, or use another mixin which overrides it, you should apply this mixin after yours.


When the fetch method is called and the previous call has not ended yet, the new call will not be made. The promise returned by the previous call will be returned instead. If you specified different options than for the previous call, they will not be reflected.

var MyCollection = Backbone.Collection.extend({

  constructor: function MyCollection(models, options) {
    Backbone.Collection.prototype.constructor.apply(this, arguments);
    this
      .makeConnectable(options)
      .makeFetchable(options);
  }

});

ConnectableMixin.mixin(MyCollection.prototype);
FetchableMixin.mixin(MyCollection.prototype);
// Set the "reset" option for the future "fetch" calls automatically
var collection = new MyCollection(undefined, {
      connector: connector,
      autoreset: true
    });
collection.fetch();

// Ensure that the collection has been fetched and process it (I)
collection
  .ensureFetched()
  .done(function () {
    ...
  });        

ResourceMixin

Helps implementing a model for a typical server resource, which has an identifier (the property ‘id’ by default), by combining the following three mixins: ConnectableMixin, FetchableMixin and AutoFetchableMixin.

var MyModel = Backbone.Model.extend({

  constructor: function MyModel(attributes, options) {
    Backbone.Model.prototype.constructor.apply(this, arguments);
    this.makeResource(options);
  },

  urlRoot: function () {
    return Url.combine(this.connector.connection.url, 'myresources');
  }

});

ResourceMixin.mixin(MyModel.prototype);
// Specify the attributes and the connector when creating the model
var connector = new Connector(...),
    model = new MyModel({
      id: 2000
    }, {
      connector: connector
    });
model.fetch();

// Set the attributes and the connector after creating the model
model.set('id', 2000);
connector.assignTo(model);
model.fetch();
        

NodeResourceMixin

Helps implementing a model or collection for a typical server resource, which is related to a node (represented by the NodeModel, its descendant or by other class with the ConnectableMixin applied), by combining the three mixins:

–NodeConnectableMixin

–FetchableMixin

–NodeAutoFetchableMixin

var MyCollection = Backbone.Collection.extend({

  constructor: function MyCollection(models, options) {
    Backbone.Collection.prototype.constructor.apply(this, arguments);
    this.makeNodeResource(options);
  },

  urlRoot: function () {
    return Url.combine(this.node.urlBase(), 'mysubresources');
  }

});

NodeResourceMixin.mixin(MyCollection.prototype);
// Specify the node attributes when creating the node
var connector = new Connector(...),
    model = new MyModel({
      id: 2000
    }, {
      connector: connector
    }),
    collection = new MyCollection(undefined, {
      node: node
    });
collection.fetch();

// Set the node attributes after creating it
node.set('id', 2000);
collection.fetch();
        

UploadableMixin

Enables creating and modifying the resource behind a Backbone.Model.

Usage:

It just works whenever you call the save method of the model.

var MyModel = Backbone.Model.extend({

  constructor: function MyModel(attributes, options) {
    Backbone.Model.prototype.constructor.apply(this, arguments);
    this
      .makeConnectable(options)
      .makeUploadable(options);
  },
  url: function () {
    return Url.combine(this.connector.connection.url, 'myresource')
  }
});
ConnectableMixin.mixin(MyModel.prototype);
UploadableMixin.mixin(MyModel.prototype);        

UploadableMixin Rename a Node

// Rename a concrete node
var connector = new Connector({
      connection: {
        url: '//server/instance/cs/api/v1',
        supportPath: '/support'
      }
    }),
    node = new NodeModel({
      id: 12345
    }, {
      connector: connector
    });
node.save({
      // properties to change on the server and in the model
      name: 'New name'
    }, {
      patch: true,       // send only properties specified above;
                                 // not everything from this.attributes
      wait: true         // set the properties to this.attributes
                                // only if and after the request succeeds
    }) .then(function () {
      return node.fetch(); // refresh node from server to get mod dates!!!
    })
    .done(function () {
      console.log('New name:', node.get('name'));
    })
    .fail(function () {
      console.log('Renaming the node failed.',
        'Old name:', node.get('name'));
    });
        

UploadableMixin File Upload

If the newly created resource needs a raw file content, you can pass the fields of the file type via options and let the mixin build the right request payload and set ist content type

// Upload a new document
var connector = new Connector({
      connection: {
        url: '//server/instance/cs/api/v1',
        supportPath: '/instancesupport'
      }
    }),
    node = new NodeModel({
      type: 144
    }, {
      connector: connector
    }),
    file = ...; // a File or Blob object
node.save({
      name: 'New document',
      parent_id: 2000
    }, {
      files: {
        file: file
      }
    }) .then(function () {
      return node.fetch(); // get new Node from server
    })
    .done(function () {
      console.log('New document ID:', node.get('id'));
    })
    .fail(function (request) {
      var error = new base.Error(request);
      console.log('Uploading document failed:', error);
    });        

CommandableMixin

Provides support for the setting commands URL query parameter as introduced by the api/v1/nodes/:id/nodes (V1) resource

var MyModel = Backbone.Model.extend({

  constructor: function MyModel(attributes, options) {
    Backbone.Model.prototype.constructor.apply(this, arguments);
    this
      .makeConnectable(options)
      .makeCommandable(options);
  },
 url: function () {
    var url = Url.combine(this.connector.connection.url, 'myresource'),
        query = Url.combineQueryString(
          this.getRequestedCommandsUrlQuery()
        );
    return query ? url + '?' + query : url;
  }

});

ConnectableMixin.mixin(MyModel.prototype);
CommandableMixin.mixin(MyModel.prototype);        

Set up the URL parameters by calling setCommands and resetCommands and fetch the model

// Set the commands for requesting when creating the model
var model = new MyModel(undefined, {
      connector: connector,
      commands: ['delete', 'reserve']
    });
model.fetch();

// Set the commands for requesting after creating the model
model.setCommands(['delete', 'reserve']);
model.fetch();        

ExpandableMixin

Provides support for the setting expand URL query parameter as introduced by the api/v1/nodes/:id or api/v1/nodes/:id/nodes (V1) resources.

Server responses can contain references to other resources; typically IDs or URLs. The expansion means replacing them with object literals containing the resource information, so that the caller does not have to request every associated resource by an additional server call

Expandable resource types:

–node

??? nodes and volumes (original_id, parent_id, volume_id etc.)

–user

??? users or user groups (create_user_id, modify_user_id, owner_user_id etc.)

var MyModel = Backbone.Model.extend({

  constructor: function MyModel(attributes, options) {
    Backbone.Model.prototype.constructor.apply(this, arguments);
    this
      .makeConnectable(options)
      .makeExpandable(options);
  },
url: function () {
    var url = Url.combine(this.connector.connection.url, 'myresource'),
        query = Url.combineQueryString(
          this.getExpandableResourcesUrlQuery()
        );
    return query ? url + '?' + query : url;
  }

});

ConnectableMixin.mixin(MyModel.prototype);
ExpandableMixin.mixin(MyModel.prototype);        
// Set the expansion when creating the model
var model = new MyModel(undefined, {
      connector: connector,
      expand: ['node', 'user']
    });
model.fetch();

// Set the expansion after creating the model
model.setExpand(['node', 'user']);
model.fetch();
        

AdditionalResourcesV2Mixin

Provides support for the setting URL query parameter flags as introduced by the api/v2/nodes/:id or api/v2/nodes/:id/nodes (V2) resources.

Server responses can contain associated resources to avoid requesting every associated resource by an additional server call, or other data, which may not be needed every time. For example:

perspective

–??? Include the perspective configuration, if the resource can carry one.

metadata

–??? Include the definitions of object properties.

var MyModel = Backbone.Model.extend({

  constructor: function MyModel(attributes, options) {
    Backbone.Model.prototype.constructor.apply(this, arguments);
    this
      .makeConnectable(options)
      .makeAdditionalResourcesV2Mixin(options);
  },
 url: function () {
    var url = Url.combine(this.connector.connection.url, 'myresource'),
        query = Url.combineQueryString(
          this.getAdditionalResourcesUrlQuery()
        );
    return query ? url + '?' + query : url;
  }

});

ConnectableMixin.mixin(MyModel.prototype);
AdditionalResourcesV2Mixin.mixin(MyModel.prototype);        
// Set the inclusion when creating the model
var model = new MyModel(undefined, {
      connector: connector,
      includeResources: ['perspective']
    });
model.fetch();

// Set the inclusion after creating the model
model.includeResources('perspective');
model.fetch();        

V2CommandableMixin

Provides support for the setting actions URL query parameter as introduced by the api/v2/members/favorites or api/v2/members/accessed (V2) resources.

Server responses can contain permitted actions to be able to support enabling and disabling in the corresponding UI; how many and which ones should be checked by the server can be specified.

var MyModel = Backbone.Model.extend({

  constructor: function MyModel(attributes, options) {
    Backbone.Model.prototype.constructor.apply(this, arguments);
    this
      .makeConnectable(options)
      .makeCommandable(options);
  },
 url: function () {
    var url = Url.combine(this.connector.connection.url, 'myresource'),
        query = Url.combineQueryString(
          this.getRequestedCommandsUrlQuery()
        );
    return query ? url + '?' + query : url;
  }

});

ConnectableMixin.mixin(MyModel.prototype);
CommandableMixin.mixin(MyModel.prototype);        
// Set the commands for requesting when creating the model
var model = new MyModel(undefined, {
      connector: connector,
      commands: ['delete', 'reserve']
    });
model.fetch();

// Set the commands for requesting after creating the model
model.setCommands(['delete', 'reserve']);
model.fetch();        

ExpandableV2Mixin

Provides support for the setting expand URL query parameter as introduced by the api/v2/nodes/:id or api/v2/nodes/:id/nodes (V2) resources.

Server responses can contain references to other resources; typically IDs or URLs. The expansion means replacing them with object literals containing the resource information, so that the caller does not have to request every associated resource by an additional server call.

Expanding needs the role to expand from (properties, versions etc.) and optionally the name or names of properties to expand (parent_id, create_user_id etc.).

var MyModel = Backbone.Model.extend({

  constructor: function MyModel(attributes, options) {
    Backbone.Model.prototype.constructor.apply(this, arguments);
    this
      .makeConnectable(options)
      .makeExpandableV2(options);
  },
 url: function () {
    var url = Url.combine(this.connector.connection.url, 'myresource'),
        query = Url.combineQueryString(
          this.getExpandableResourcesUrlQuery()
        );
    return query ? url + '?' + query : url;
  }

});

ConnectableMixin.mixin(MyModel.prototype);
ExpandableV2Mixin.mixin(MyModel.prototype);        
// Set the expansion when creating the model
var model = new MyModel(undefined, {
      connector: connector,
      expand: {
        properties: ['parent_id', 'create_user_id']
      }
    });
model.fetch();

// Set the expansion after creating the model
model.setExpand('properties', ['parent_id', 'create_user_id']);
model.fetch();        

FieldsV2Mixin

Provides support for the setting fields URL query parameter as introduced by the api/v2/members/favorites or api/v2/members/accessed (V2) resources.

Server responses can contain various properties, which can increase the response size, if all of them were always returned.

Adding a field needs the role, which contains them (properties, versions etc.), and optionally their names too (parent_id, create_user_id etc.).

var MyModel = Backbone.Model.extend({

  constructor: function MyModel(attributes, options) {
    Backbone.Model.prototype.constructor.apply(this, arguments);
    this
      .makeConnectable(options)
      .makeFieldsV2(options);
  },
 url: function () {
    var url = Url.combine(this.connector.connection.url, 'myresource'),
        query = Url.combineQueryString(
          this.getResourceFieldsUrlQuery()
        );
    return query ? url + '?' + query : url;
  }

});

ConnectableMixin.mixin(MyModel.prototype);
FieldsV2Mixin.mixin(MyModel.prototype);        
// Set the expansion when creating the model
var model = new MyModel(undefined, {
      connector: connector,
      fields: {
        properties: ['parent_id', 'create_user_id']
      }
    });
model.fetch();

// Set the expansion after creating the model
model.setFields('properties', ['parent_id', 'create_user_id']);
model.fetch();        

RulesMatchingMixin

Helps implementing a collection of rule models. which are supposed to select one model, which rules match the input object. For example, choose an icon or the default click-action for a particular node.

How to apply the mixin to a model

var NodeActionSelectingModel = Backbone.Model.extend({

  idAttribute: 'signature',

  defaults: {
    sequence: 100,
    signature: null
  },
  constructor: function NodeActionSelectingModel(attributes, options) {
    Backbone.Model.prototype.constructor.apply(this, arguments);
    this.makeRulesMatching(options);
  },
  enabled: function (node) {
    return this.matchRules(node, this.attributes);
  }
});
RulesMatchingMixin.mixin(NodeActionSelectingModel.prototype);

var NodeActionSelectingCollection = Backbone.Collection.extend({

  model: NodeActionSelectingModel,

  comparator: 'sequence',

  constructor: function NodeActionSelectingCollection(models, options) {
    Backbone.Collection.prototype.constructor.apply(this, arguments);
  },

  findByNode: function (node) {
    return this.find(function (model) {
      return model.enabled(node);
    });
  }
  
});        

Application

var nodeActions = new NodeActionSelectingCollection([
  {
    equals: {
      container: true
    },
    signature: 'Browse'
  },
  {
    equals: {
      type: [144, 749]
    },
    signature: 'Open'
  },
  {
    and: [
      equals: {
        type: 144
      },
 containsNoCase: {
        mime_type: [
          "application/msword",
          "application/vnd.ms-word",
          "application/vnd.msword",
          "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
          "application/vnd.wordprocessing-openxml",
          "application/vnd.ces-quickword",
          "application/vnd.ms-word.document.macroEnabled.12",
          "application/vnd.ms-word.document.12",
          "application/mspowerpoint",
          "application/vnd.ms-powerpoint",
          "application/vnd.openxmlformats-officedocument.presentationml.presentation",
          "application/vnd.ces-quickpoint",
          "application/vnd.presentation-openxml",
          "application/vnd.presentation-openxmlm",
          "application/vnd.ms-powerpoint.presentation.macroEnabled.12",
          "application/msexcel",
          "application/vnd.ms-excel",
          "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
          "application/vnd.ces-quicksheet",
          "application/vnd.spreadsheet-openxml",
          "application/vnd.ms-excel.sheet.macroEnabled.12",
        ]
      }
    },
    signature: 'Edit',
    sequence: 50
  },
{
    and: {
      equals: {
        type: 144
      },
      or: {
        startsWithNoCase: {
          mime_type: 'image/'
        },
        equalsNoCase: {
          mime_type: ['application/pdf', 'application/x-pdf']
        }       }     },
    signature: 'Convert',
    sequence: 50
  },
  {
    and: [
      {
        equals: {
          type: 144
        }
      },
      {
        equalsNoCase: {
          mime_type: 'text/plain'
        }      }    ],
    signature: 'Read',
    sequence: 50
  },
  {
    signature: 'Properties',
    sequence: 200
  }
]);        

makeRulesMatching(options) : this - Must be called in the constructor to initialize the mixin functionality. Expects the Backbone.Model constructor options passed in.

Conditions

–Conditions from "equals" to "includes" below are case-sensitive, if their operands are strings. They have their case-insensitive couterparts ending

–with "NoCase". For example, "equalsNoCase" or "includesNoCase".?

?equals - the value of the selected property has to equal at least one of the specified values. Strings comparisons are case-insensitive.

?contains - the value of the selected property has to be a part of at least one of the specified values. String case-insensitive "indexOf" operation is applied.

?startsWith - the value of the selected property has to be at the beginning of at least one of the specified values. String case-insensitive "startsWith“ operation is applied.

?endsWith - the value of the selected property has to be at the end of at least one of the specified values. String case-insensitive "endsWith“ operation is applied.

?matches - the regular expression has to match at least one of the specified values. The operation is case-insensitive.

?includes - if the value of the selected property is an object, it has to include at least one the specified keys; if the value of the selected property is an array, it has to include at least one the specified items. The standard equal operator is applied when comparing keys and items.

?has - at least one of the specified properties must exist and not be null.

?decides - the function has to return "true" at least for one of the specified values.

?or - at least one of the sub-conditions in the arry or object has to return "true".

?all - all of the sub-conditions in the arry or object has to return "true".

?not - negates the result of the sub-condition.

?Conditions are performed on properties of the scenario-controlling object - on attributes of the controlling model. Property name is the key in the object, which is the value of the operation:

{
  equals: {
    type: 144
  },
  signature: 'Open'
}        

?The key can be a dot-separated expression, which would evaluate like when a nested object is accessed in JavaScript:

  {equals: {
    'id_expand.type': 5574
  },
  signature: 'OpenWikiPageVersion'
}        

?The object passed to matchRules can be either an object literal, which will become the direct source of the properties to test, or a Backbone.Model instance, which attributes will be used of.

SyncableFromMultipleSourcesMixin

Simplifies implementing models and collections, which have to be fetched by issuing multiple asynchronous calls. Either by executing multiple $.ajax statements, or by fetching multiple models or collections and merging their content.

var MyModel = Backbone.Model.extend({

  constructor: function MyModel(attributes, options) {
    Backbone.Model.prototype.constructor.apply(this, arguments);
    this.makeSyncableFromMultipleSources(options);
  },

  sync: function (method, model, options) {
    return this.syncFromMultipleSources(
      [...promises], this._mergeSources,
      this._convertError, options);
  },

  _mergeSources: function (...results) {
    return merged response;
  },

  _convertError: function (result) {
    return {
      status: ...,
      statusText: '...',
      responseJSON: ...
    };
  }
  
});        

How to combine multiple $.ajax calls

 sync: function (method, model, options) {
    var first = $.ajax(this.connector.extendAjaxOptions({
                   url: '...'
                 })
                 .then(function (response) {
                   return response;
                 }),
        second = $.ajax(this.connector.extendAjaxOptions({
                    url: '...'
                  })
                  .then(function (response) {
                    return response;
                  });
    return this.syncFromMultipleSources(
        [first, second], this._mergeSources, options);
  },

  _mergeSources: function (first, second) {
    return merged response to be parsed;
  }        

How to combine multiple model/collection fetches

 sync: function (method, model, options) {
    var first = new FirstModel(...),
        second = new SecondCollection(...);
    first = first.fetch(options)
                 .then(function () {
                   return first.toJSON()
                 }),
    second = second.fetch(options)
                   .then(function () {
                     return second.toJSON()
                   }),
    options.parse = false;
    return this.syncFromMultipleSources(
        [first, second], this._mergeSources, options);
  },

  _mergeSources: function (first, second) {
    return already parsed merged response;
  }        

SyncableFromMultipleSourcesMixin

makeSyncableFromMultipleSources(options) : this - Must be called in the constructor to initialize the mixin functionality. Expects the Backbone.Model or Backbone.Collection constructor options passed in.

syncFromMultipleSources(promises, mergeSources, convertError, options) : promise

–Implements the interface (events, callbacks and promise) of Backbone.sync by waiting on the source promises and by resolving the target promise with the merged response returned by the caller's callback, which receives results of the source promises to merge them.

–If one of the source promises fails, the rejected result will be passed to convertError, which is an optional parameter. (A function expecting rejected result from $.ajax will be used by default.) If specified, it has to return an object simulating the jQuery AJAX object:

    {
      statusText: '...',
      responseJSON: {...}
    }        


Happy Mixin


要查看或添加评论,请登录

社区洞察

其他会员也浏览了