Skip to main content

Context

Gathers models, collections, or plain objects to be shared among multiple scenarios and fetch them together. Objects in context are managed by their factories.

This is a base class. PageContext, PortalContext, BrowsingContext or PerspectiveContext are classes to create instances of.

// Create a new context.
var context = new PageContext();
// Get the (main contextual) authenticated user
var currentUser = context.getModel(UserModelFactory);

Factory

Is the "overlord" of objects in the context. The parent class returned from 'nuc/contexts/factories/factory' is usually called by different names like ObjectFactory, ModelFactory or CollectionFactory to express what the descended factory will take care of.

  • Creates an instance of the object, which will be returned to the caller.
  • Assigns a unique prefix to the object, so that the same object can be obtained using the factory at different places.
  • Can override how the model or collection is fetched.

A factory has to specify a unique propertyPrefix in the prototype and set the object managed by it to this.property:

var TestObjectFactory = ObjectFactory.extend({

propertyPrefix: 'test',

constructor: function TestObjectFactory(context, options) {
ObjectFactory.prototype.constructor.apply(this, arguments);

this.property = new TestObject();
}

});

// Request an object with the default identifier
// (internally stored with prefix 'test')
var test = context.getObject(TestObjectFactory);

Objects are stored using propertyPrefix in the context. The propertyPrefix is used alone for globally unique objects, or as a base for multiple objects having the same factory, but different attributes:

// Request a separate object with a specific identifier
// (internally stored with prefix 'test-id-1')
var test = context.getObject(TestObjectFactory, {
attributes: {id: 1}
});

Factory can be used just for the object creation, if you don't want to learn about its constructor parameters.

// Request a standalone object, not shareable by the context
var test = context.getObject(TestObjectFactory, {
unique: true,
temporary: true,
detached: true
});

Fetchable Factory

Exposes fetch method, which should fetch its model. Whenever the context is fetched, this method will be called.

var FavoriteCollectionFactory = CollectionFactory.extend({

propertyPrefix: 'favorites',

constructor: function FavoritesCollectionFactory(context, options) {
CollectionFactory.prototype.constructor.apply(this, arguments);

var connector = context.getObject(ConnectorFactory, options);
this.property = new FavoritesCollection(undefined, {
connector: connector,
autoreset: true
});
},

fetch: function (options) {
return this.property.fetch(options);
}

});

The isFetchable method can be added to be able to check dynamically, if the object is fetchable or not.

var NodeModelFactory = ModelFactory.extend({

propertyPrefix: 'node',

constructor: function NodeModelFactory(context, options) {
ModelFactory.prototype.constructor.apply(this, arguments);

var connector = context.getObject(ConnectorFactory, options);
this.property = new NodeModel(undefined, {connector: connector});
},

isFetchable: function () {
return this.property.isFetchable();
},

fetch: function (options) {
return this.property.fetch(options);
}

});

Configurable Factory

Factories are usually created once per object type, but they need to be able to create multiple object instances. With just the factory provided, the object will be constructed with default options:

// Get the (main contextual) node
var currentNode = context.getModel(NodeModelFactory);

With the second argument, additional options can be passed to control the object creation. The attributes will be used to uniquely stamp the new object, so future calls to getObject with the same attributes will return the same object. Also the attributes will be passed to the constructor of the object, if it is a Backbone.Model:

// Get original where the (main contextual) node points to, if it is
// a shortcut
var originalId = currentNode.get('original_id'),
original = context.getModel(NodeModelFactory, {
attributes: {id: originalId}
});

Below the property called like the factory prefix you can pass additional options to the newly created object's constructor by the options property:

// Get original where the (main contextual) node points to, if it is
// a shortcut and make it fetchable by the connector
var originalId = currentNode.original.get('id'),
original = context.getModel(NodeModelFactory, {
attributes: {id: originalId},
node: {
options: {connector: currentNode.connector}
}
});

If the new object is a Backbone.Model, you can specify different attributes for the constructor, than the attributes, which control the unique stamp of the object. While the former should be as minimum as to compose the unique stamp, the latter could be more complete to pre-initialize the new object:

// Get original where the (main contextual) node points to, if it is
// a shortcut, make it fetchable by the connector, but pre-initialize
// it will all properties available so far
var originalId = node.original.get('id'),
original = context.getModel(NodeModelFactory, {
attributes: {id: originalId},
node: {
attributes: node.original.attributes,
options: {connector: currentNode.connector}
}
});

Finally, if you already have the new object created and you only need the context to make it shareable, you can pass it to the property called like the factory as-is:

// Get original where the (main contextual) node points to, if it is
// a shortcut, and share the same object, which has been obtained
// with the contextual node
var originalId = node.original.get('id'),
original = context.getModel(NodeModelFactory, {
attributes: {id: originalId},
node: node.original
});

Detached Objects

Objects, which are added to the context after the context was fetched are needed to be fetched manually, if they need fetching at all. Also, as manually fetched objects, when the context is re-fetched, they are not re-fetched again. Their users decide, when they should be re-fetched.

// User information, which does not refresh automatically and will be
// discarded, when clear() is called on the context
var ownerId = node.get('owner_user_id'),
owner = context.getModel(MemberModelFactory, {
attributes: {id: ownerId},
detached: true
});

Detached objects should merge the Fetchable mixin, which allows fetching only once on demand by ensureFetched:

// Make sure, that the model was fetched once, before accessing
// its properties
owner
.ensureFetched()
.done(function () {
console.log('Login:', owner.get('name'));
});

Permanent Objects

Objects like the authenticated user need not be re-created during the application lifecycle. After being requested for the first time, they should remain in the context for all scenarios. (The only way how to re-create them is to reload the entire application - the application page.)

// User information, which does not refresh automatically and will not be
// discarded, when clear() or fetch() is called on the context
var ownerId = node.get('owner_user_id'),
owner = context.getModel(MemberModelFactory, {
attributes: {id: ownerId},
permanent: true,
detached: true
});

Permanent objects are usually detached too, unless they should be re-fetched with every context re-fetch.

Temporary Objects

Objects like the original node need to be shared across function scopes and object boundaries, but should not be re-created and re-fetched multiple times. When the lifecycle of the current (main contextual) node ends, they should be discarded from the context, so that they would not get re-fetched with the new context content.

// Shareable original node information, which will be discarded, as soon
// as clear() or fetch() is called on the context
var originalId = shortcut.original.get('id'),
original = context.getModel(NodeModelFactory, {
attributes: {id: originalId},
temporary: true
});

Factory Life-Cycle

The context is a single-instance object that lives as long as the web page lives. (There may be multiple contexts, if parts of the page were supposed to work separately, but that would be a rare case.) The web page serves different purposes during its life. Having just single context instance means that the content of the context has to be able to be exchanged to reflect the current page content.

The context supports two changes of the page content:

  • refresh - the page (views) will be reused, only the data will be reloaded
  • exchange - the page will be rebuilt (current views will be destroyed and new ones will be created) and new data wil be loaded

These changes can be induced by the following methods of the context: clear and fetch. The clear removes the factories and thus their data from the context. The fetch reloads (or loads, initially) the data by letting the factories fetch.

// render a new page    <----------------------------------+
context.getObject(...) // get objects from the context |
context.fetch() // fetch collected factories <--+ |
// work with the page | |
// open another object on the same page ---------------+ |
context.clear() // prepare for the next page |
// navigate to other page --------------------------------+

If the page has to show a different scenario (exchange), the clear will be called, then the page will be rebuilt and eventually the fetch will be called to load the data. If the page should show the same scenario with different data (refresh), just fetch will be called.

Factories together with the objects that they maintain can be removed from the context when fetch and clear are called to allow some objects to stay forever and the other objects temporarily only after new data are to be loaded. When factories are used to request objects from context, they can be passed options, or these options can be set to this.options in the factory's constructor: permanent and temporary take care of the life-cycle, detached and unique have other purposes.

How factories are removed from the context when clear and fetch are called:

operation / flagrefresh (fetch)exchange (clear + fetch)
permanentstaystay
normalstaydrop
temporarydropdrop

The detached flag does not affect the factory's life. It prevents the factory ever getting fetched. The unique flag appends a unique number to the factory prefix, so that one factory can be put to the context multiple times to maintain different objects.

Declarative options control what factories are allowed to fetch when fetch is called. In addition to the static rules below, the actual fetchability is checked by the isFetchable method of the context:

method / flagfetch
permanentallowed
normalallowed
temporaryN/A (*)
detachedforbidden

(*) Temporary factories are removed from the context when the fetch method starts executing. It does not make sense to discuss their fetchability.

Methods

getObject(factory, options): object

Returns an object maintained by the specified factory. If the object has not existed yet, it will be created, otherwise the previously created instance will be returned.

The object existence is made unique by the property prefix defined by the factory. The full unique property stamp consists of this prefix and of the context attributes, which can be passed in the second argument.

If the object is to be created, the second argument can carry parameters for its constructor under the property named by the factory's property prefix; usually attributes and options for a model or models and options for a collection. Instead of constructor parameters, this property can point to an already created object, so that the factory just stores it as-is.

The second argument can contain boolean flags to control how the context will handle the object: detached, permanent, temporary and unique.

// Create a favorite node collection pre-initialized with some nodes
// until it gets fetched with the context
var favorites = context.getCollection(FavoriteCollectionFactory, {
favorites: {
models: [{type: 141}, {type: 142}]
}
});

getCollection(factory, options): object

Behaves just like getObject, but looks more intuitive, if the expected result is Backbone.Collection.

getModel(factory, options): object

Behaves just like getObject, but looks more intuitive, if the expected result is Backbone.Model.

hasObject(factory, options): boolean

Returns if there is an object maintained by the specified factory.

hasCollection(factory, options): boolean

Behaves just like hasObject, but looks more intuitive, if the expected object is Backbone.Collection.

hasModel(factory, options): boolean

Behaves just like hasObject, but looks more intuitive, if the expected object is Backbone.Model.

clear(options): void

Discards all objects from the context, which are not permanent. When options.all is set to true, all objects will be discarded.

fetch(options): Promise

Fetches all objects in the context, which are not detached. Discards all temporary objects before that. The options will be passed to the fetch methods in factories that take care of the fetchable objects.

suppressFetch(): boolean

Aborts fetching started by the fetch method. You can interrupt a running fetchin order to start another one, because the earlier result has become irrelevant. (Because a navigation got interrupted by yet another navigation, for example.)

Error event on the context will be never triggered and the returned promise will be never resolved. Sync event will be triggered immediately as the suppressFetch method is called to balance the earlier triggered request event. Events on the models and and collection will be triggered eventually, as their AJAX calls will finish.

This method does not abort the operation. It only allows another call to fetch be made and replace the one in progress.

Properties

fetching: Promise

The promise returned by fetch during fetching or null if no fetching is in progress.

fetched: boolean

true if the most recent fetch succeeded, false if the context has not been fetched yet, or fetching is in progress, or it failed.

error: Error

null if the most recent fetch succeeded, or fetching is in progress, or the context has not been fetched yet, an instance of Error if the most recent fetch failed.

Events

'before:clear', context

The context is going to be cleared.

'clear', context

The context has been cleared.

'request', context

The context is going to be fetched.

'sync', context

Fetching the context succeeded.

'error', error, context

Fetching the context failed.

'add:factory', context, propertyName, factory

A new factory has been added to the context.

'remove:factory', context, propertyName, factory

A factory has been destroyed and will be removed from the context.