-
Notifications
You must be signed in to change notification settings - Fork 68
Factories
Factories are responsible for creating component instances. Wire.js has several builtin factories, and plugins can provide additional factories that allow specialized creation of components, or allow you to create components the builtin factories can't already create.
The create factory loads an AMD module and uses it to create a component instance by calling the module either as a constructor using new
, or as a regular function.
myComponent: {
create: {
// Load my/app/ModuleA and call it as a constructor or function
module: 'my/app/ModuleA',
// Optional: Pass these args when calling my/app/ModuleA
// If not supplied, module will be called with no args
args: [arg1, arg2, arg3...],
// Optional: Force calling the module as a constructor using new.
// You won't need to use most of the time. See Notes section below.
useNew: true, // or false
}
}
// This shorter syntax loads my/app/ModuleA and calls it with no args
myComponent: {
create: 'my/app/ModuleA'
}
The create factory uses a set of simple heuristics to determine automatically whether to call the module as a constructor using new
, or as a regular function.
There is a situation where the heuristics will guess incorrectly. If you have a module that returns a constructor function with an empty prototype, there is no reliable way to determine if that function is a constructor. For example:
define('my/app/ModuleWithConstructor', function() {
// This is a constructor function, but has an empty prototype.
// The create factory will guess incorrectly that this is a
// regular function, and will call it without using new.
//
// Specify useNew: true to ensure it is called as a constructor
//
function ThisIsAConstructor() {
this.name = "Bob";
}
//
// Defining an empty prototype or no prototype at all will have the
// same outcome. In either case, useNew: true is the answer.
//
// ThisIsAConstructor.prototype = {};
//
return ThisIsAConstructor;
});
The module factory loads an AMD module, but does not call it. The module is used directly as the component.
myComponent: {
module: 'my/app/ModuleA'
}
The prototype factory begets a new component from an existing component, using the Javascript prototype chain. This is useful when you want to create several similar components. You can create a base component, configure it, then beget several components from the base, and specialize their configuration as needed.
// Create a component to use as a prototype. See create factory above.
myBaseComponent: {
create: 'my/app/ModuleA',
// Set its properties
properties: {
firstName: 'Bob',
lastName: 'Smith'
}
},
// Beget a second component, using myBaseComponent as the prototype
myComponent1: {
prototype: 'myBaseComponent',
// Specialize the firstName, lastName will be 'Smith'
properties: {
firstName: 'John'
}
},
// Beget another
myComponent2: {
prototype: 'myBaseComponent',
// Specialize the firstName, lastName will be 'Smith'
properties: {
firstName: 'Harry',
// Can add new properties
occupation: 'Javascript guy'
}
}
// Can even beget from components that were themselves created using
// the prototype factory, to further specialize.
myComponent3: {
prototype: 'myComponent2',
// Specialize the firstName and occupation, lastName will still be 'Smith'
properties: {
firstName: 'Mary',
occupation: 'Javascript gal',
// And add a new property
title: 'VP of Javascriptiness'
}
}
It can be useful to have object literals in wire specs for various reasons, such as reference data or common configuration shared between several components. Most times, object literals can be declared directly:
// An object literal can be declared directly in a wire spec
myReferenceData: {
name: 'I\'m an object literal',
number: 10
// ... more properties here
}
However, if your object literal has a property that is the name of a factory, wire.js will attempt to use that factory to create the component, instead of simply using the object literal. For example:
// An object literal can be declared directly in a wire spec
myReferenceData: {
name: 'I\'m an object literal',
number: 10,
module: 'Bob\'s module'
// Oops, wire.js will try to use the module factory to load an AMD
// module with the id 'Bob\'s module'!
}
To solve this, use the literal factory. The result will be a component named myReferenceData
that has the four properties declared inside the literal
.
// An object literal can be declared directly in a wire spec
myReferenceData: {
// Anything inside this literal will be used as-is
literal: {
name: 'I\'m an object literal',
number: 10,
// Using module here is ok, wire.js will not attempt to use
// the module factory.
module: 'Bob\'s module',
// This will also be used as-is. The literal factory WILL NOT be
// invoked again.
literal: 'This is a literal string'
}
}