A grade is a block of configuration (representable as JSON) with a global name. A new grade is typically registered
into an Infusion runtime by a call to the function fluid.defaults
,
supplying both the global name (the grade name) and the configuration block.
Here's a simple example of defining a new grade, derived from the base framework grade fluid.component
:
fluid.defaults("examples.myGrade", {
gradeNames: "fluid.component"
});
Each such grade can be built on to derive further grades/components. This derivation occurs by mentioning the name of
the original grade (e.g. examples.myGrade
) within the gradeNames section of the derived component.
Most grades you will deal with are component grades derived from fluid.component
. However, for some purposes you
may also deal with function grades which are derived from fluid.function
.
The framework's built-in component grades
The Infusion Framework already contains several predefined component grades that normally form the initial building blocks for external components and grades. The following table describes these grades and how they relate to each other.
Grade Name | Description |
---|---|
fluid.component |
A plain fluid.component is the most basic component: it supports options merging with
defaults (Components),
as well as instantiating event firers based on default framework events (onCreate ,
onDestroy , afterDestroy ) and events declared in the options
(Tutorial - Creating
Components). All Infusion components are derived from this grade, and in general all things not
derived from this grade are non-components (e.g. plain functions, or model transformation transforms,
etc.)
|
fluid.modelComponent |
A model component is a component that additionally provides supports for a component's model,
and operations on it (Tutorial - Model Components).
These operations are mediated by a machine known as a ChangeApplier
which is automatically constructed for a model component. As well as exposing a programmatic API, this
also allows for declarative constraints and relationships to be enforced by means of the
model relay system.
|
fluid.viewComponent |
A view component is a fluid.modelComponent that is bound to a DOM container node,
holds a DOM Binder and supports a view
(Tutorial - View Components).
|
fluid.resourceLoader |
A resource loader component can configure the loading of any number of asynchronously available
resources, possibly before component startup is complete. Since fluid.resourceLoader is a
mixin grade, it may be added to a fluid.modelComponent , fluid.viewComponent
or any other variety of Infusion component. A dedicated documentation page is available for
fluid.resourceLoader .
|
fluid.rendererComponent |
A renderer component is a view component that also bears a renderer.
There are additional features provided by this component grade specified on the
Useful functions and events section of the
Tutorial - Renderer Components
page.
Note: The current Infusion renderer is deprecated and
will be replaced in the Infusion 5.0 release.
|
Specifying Parent Grades
The parent grades of a newly defined grade should be specified using the gradeNames
option in the defaults block, as
shown in the examples below. If no gradeNames
are specified, the framework will not construct a component creator
function, but the grade may still function as a "mixin" grade when mentioned as the parent of another component
(or non-component). The gradeNames
option holds a String
or Array of String
.
fluid.defaults("fluid.uploader.demoRemote", {
gradeNames: ["fluid.component"]
// ...
});
fluid.defaults("cspace.messageBarImpl", {
gradeNames: ["fluid.rendererComponent"]
// ...
});
fluid.defaults("cspace.util.relationResolver", {
gradeNames: ["fluid.modelComponent"]
// ...
});
Initializing Components
The framework will automatically construct a creator function for any component which is derived (even indirectly) from
fluid.component
:
fluid.defaults("fluid.uploader.fileQueueView", {
gradeNames: ["fluid.viewComponent"]
// ...
});
// The framework has automatically generated this function since the grade is a component grade
var that = fluid.uploader.fileQueueView({
// ...
});
Combining Grades
Since the fluid.defaults
directive introduces a grade into the system, various components can be composed to create
new ones. Options, fields and methods introduced by the ancestor grades will be merged. The merging happens, firstly in
hierarchical order (grades comprising the ancestor grade are resolved before the actual component grades resolution)
and secondly in the left-to-right order (defaults from the grade on the right taking precedence over the defaults from
the grade on the left). Those interested in fine details should note that this is a very different scheme to the
C3 linearization algorithm that is commonly used for resolving
multiple inheritance. Other than preventing infinite cycles of resolution, the framework will allow the same grade to
appear any number of times in the list of grades, and each time it will be effective in overriding definitions occurring
in grades to the left in the same gradeNames
list.
Here is a simple example:
fluid.defaults("examples.componentOne", {
gradeNames: "fluid.modelComponent",
model: {
field1: false,
field2: true
},
option: "TEST1"
});
fluid.defaults("examples.componentTwo", {
gradeNames: "fluid.modelComponent",
model: {
field1: true
},
option: "TEST2"
});
fluid.defaults("examples.combinedComponent", {
gradeNames: ["examples.componentOne", "examples.componentTwo"]
// The resulting defaults for component examples.combinedComponent
// will behave as if the following had been written:
// model: {
// field1: true,
// field2: true
// },
// option: "TEST2"
});
Note: All the material from the component defaults will be merged by the framework, including records
such as events
, listeners
, members
, components
, invokers
and model
. Some of these, e.g. listeners
will receive custom merging algorithms sensitive to
their context - for example showing awareness of listener
namespaces.
Note: In the current framework, all grades derived from fluid.viewComponent
(as well as
fluid.rendererComponent
, etc.) must be listed AFTER all those that are not. This problem will be resolved in a
future framework release.
Dynamic Grades
Grades supplied as arguments to a constructing component in the gradeNames
field will be added into the grade list of
the particular component instance, as if a new fluid.defaults
block had been issued creating a new "type" in the
system - however, the main type
of the component will not change. This facility could be thought of as a form of "type
evolution" or
Schema evolution.
All dynamic grades take precedence over (that is, are merged in after) all static grades.
Delivering a dynamic gradeName as a direct argument:
There are numerous ways that these additional gradeNames could be delivered - for example, as a direct argument to a component's creator function:
var myCombinedComponent = examples.componentOne({
gradeNames: "examples.componentTwo"
});
// creates a component that behaves exactly (except for its typeName)
// as if it was created via examples.combinedComponent() above
Delivering a dynamic gradeName via a subcomponent record:
Another possibility is to supply the additional gradeNames via a subcomponent record - for example
fluid.defaults("examples.rootComponent", {
components: {
myCombinedComponent: { // This component also behaves (except for typeName)
// as if was created via examples.combinedComponent
type: "examples.componentOne",
options: {
gradeNames: "examples.componentTwo"
}
}
}
});
Delivering a dynamic gradeName via an options distribution:
Perhaps one of the most powerful possibilities is to distribute dynamic gradeNames to one or more components via a distributeOptions record:
fluid.defaults("examples.distributingRootComponent", {
distributeOptions: {
record: "examples.componentTwo",
target: "{that examples.componentOne}.options.gradeNames"
},
components: {
myCombinedComponent1: {
type: "examples.componentOne"
},
myCombinedComponent2: {
type: "examples.componentOne"
}
}
});
In the above example, every subcomponent of examples.distributingRootComponent
which had a grade content of
examples.componentOne
would automatically have mixed in a grade of
examples.componentTwo
, causing them all to behave as if they were instances of examples.combinedComponent
.
Raw Dynamic Grades
Another very powerful framework facility is the use of raw dynamic grades. In this scheme, the gradeNames list for any
component may include any standard IoC reference which may resolve to either a String
or Array of String
directly holding one or more grade names, or else a zero-arg function which can be invoked to obtain such a
value. In this way, the developer can specify additional grade names based on dynamic material (potentially not known at
the time of definition) such as a function (method or invoker) or a property in component options. Note that use of this
facility should be discouraged in favour of any of the other techniques on this page - e.g. standard dynamic grades or
context awareness - in future versions of the framework the use of raw dynamic grades may impose a big performance
penalty.
For example:
fluid.defaults("fluid.componentWithDynamicGrade", {
gradeNames: ["fluid.component", "{that}.getDynamicGradeName"],
invokers: {
getDynamicGradeName: "fluid.componentWithDynamicGrade.getDynamicGradeName"
}
});
// When resolved our fluid.componentWithDynamicGrade will have all the functionality of a fluid.modelComponent grade.
// NOTE: developers can also return an array of grade names. These grade names can be custom grade names.
// NOTE: This facility is fragile and should be used as a scheme for "last-ditch polymorphism"
fluid.componentWithDynamicGrade.getDynamicGradeName = function () {
return "fluid.modelComponent";
};