A composite component is a reusable piece of UI that can be embedded as a custom HTML element and can be composed of other composites, JET components, HTML, JavaScript, or CSS.
Packaging and Registration
Composite components should be packaged as a standalone module in a folder matching the tag name it will be registered with, e.g. 'my-chart'. An application would use a composite by requiring it as a module, e.g. 'jet-composites/my-chart/loader'. The composite module could be stored locally in the app which is the recommended approach, but could also be stored on a different server, or a CDN. Note that there are XHR restrictions when using the RequireJS text plugin which may need additional RequireJS config settings. Please see the text plugin documentation for the full set of limitations and options. By using RequireJS path mappings, the application can control where individual composites are loaded from. See below for a sample RequireJS composite path configuration. Note that the 'jet-composites/my-chart' mapping is only required if the 'my-chart' composite module maps to a folder other than 'someSubFolder/jet-composites/my-chart' using the configuration below.
requirejs.config(
{
baseUrl: 'js',
paths:
{
'jet-composites': 'someSubFolder/jet-composites',
'jet-composites/my-chart': 'https://someCDNurl',
'jet-composites/my-table': 'https://someServerUrl'
}
}
All composite modules should contain a loader.js file which will handle registering and specifying the dependencies for a composite component. We recommend using RequireJS to define your composite module with relative file dependencies. Registration is done via the oj.Composite.register API. By registering a composite component, an application links an HTML tag with provided Metadata, View, ViewModel and CSS which will be used to render the composite. These optional pieces can be provided via a descriptor object passed into the register API. See below for sample loader.js file configurations.
Note that in this example we are using require-css, a RequireJS plugin for loading css which will load the styles within our page so we do not need to pass any css into the register call. This is the recommended way to load CSS, especially for cases where the composite styles contain references to any external resources.
define(['text!./my-chart.html', './my-chart', 'text!./my-chart.json', 'css!./my-chart'],
function(view, viewModel, metadata) {
oj.Composite.register('my-chart',
{
metadata: JSON.parse(metadata),
view: view,
viewModel: viewModel
});
}
);
This example shows how to register a custom parse function which will be called to parse attribute values defined in the metadata.
define(['text!./my-chart.html', './my-chart', 'text!./my-chart.json'],
function(view, viewModel, metadata) {
var myChartParseFunction = function(value, name, meta, defaultParseFunction) {
// Custom parsing logic goes here which can also return defaultParseFunction(value) for
// values the composite wants to default to the default parsing logic for.
// This function is only called for non bound attributes.
}
oj.Composite.register('my-chart',
{
metadata: JSON.parse(metadata),
view: view,
viewModel: viewModel,
parseFunction: myChartParseFunction
});
}
);
Usage
Once registered within a page, a composite component can be used in the DOM as a custom HTML element like in the example below. A composite element will be recognized by the framework only after its module is loaded by the application. Once the element is recognized, the framework will register a busy state for the element and will begin the process of 'upgrading' the element. The element will not be ready for interaction (e.g. retrieving properties or calling methods) until the upgrade process is complete with the exception of property setters and the setProperty and setProperties methods. The application should listen to either the page-level or an element-scoped BusyContext before attempting to interact with any JET custom elements. See the BusyContext documentation on how BusyContexts can be scoped.
The upgrade of JET composite elements relies on any data binding resolving, the management of which is done by a binding provider.
The binding provider is responsible for setting and updating attribute expressions and any custom elements within its managed
subtree will not finish upgrading until it applies bindings on that subtree. By default, there is a single binding provider for a page,
but subtree specific binding providers can be added by using the data-oj-binding-provider attribute with values of
"none" and "knockout". The default binding provider is knockout, but if a page or DOM subtree does not use any expression syntax or
knockout, the application can set data-oj-binding-provider="none" on that element so its dependent JET composite custom
elements do not need to wait for bindings to be applied to finish upgrading.
<my-chart type="bubble" data="{{dataModel}}"></my-chart>
Metadata
The composite Metadata is a JSON formatted object which defines the properties, methods, slots, and events fired by the composite. The name of the composite component properties, event listeners, and methods should avoid collision with the existing HTMLElement properties, event listeners, and methods. Additionally, the composite should not re-define any global attributes or events.
The Metadata JSON object should have the following required properties: "name", "version", "jetVersion" and the following optional properties: "description", "dependencies", "icon", "displayName", "properties", "methods", "events", or "slots". See the tables below for descriptions of these properties. The Metadata can be extended by appending any extra information in an "extension" field except at the first level of the "properties", "methods", "events" or "slots" objects. Any metadata in an extension field will be ignored.
Keys defined in the "properties" top level object should map to the composite component's properties following the same naming convention described in the Property-to-Attribute mapping section. Non expression bound composite component attributes will be correctly evaluated only if they are a primitive JavaScript type (boolean, number, string) or a JSON object. Note that JSON syntax requires that strings use double quotes. Attributes evaluating to any other types must be bound via expression syntax. Boolean attributes are considered true if set to the case-insensitive attribute name, the empty string or have no value assignment. JET composite components will also evalute boolean attributes set explicitly to 'true' or 'false' to their respective boolean values. All other values are invalid.
Not all information provided in the Metadata is required at run time and those not indicated to be required at run time in the tables below can be omitted to reduce the Metadata download size. Any non run time information can be used for design time tools and property editors and could be kept in a separate JSON file which applications can use directly or merge with the run time metadata as needed, but would not be required by the loader.js file. For methods and events, only the method/event name is required at run time.
Metadata Properties
| Key | Used at Runtime | Required | Type | Description | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| name | yes | yes | {string} | The component tag name.
The component name must meet the following requirements (based upon the W3C Custom Element spec):
|
||||||||||||
| version | yes | yes | {string} | The component version. Note that changes to the metadata even for minor updates like updating the jetVersion should result in at least a minor composite version change, e.g. 1.0.0 -> 1.0.1. | ||||||||||||
| jetVersion | yes | yes | {string} | The semantic version of the supported JET version(s). Composite authors should not specify a semantic version range that includes unreleased JET major versions as major releases may contain non backwards compatible changes. Authors should instead recertify composites with each major release and update the composite metadata or release a new version that is compatible with the new release changes. | ||||||||||||
| properties | yes | no | {Object |
See Properties table below for details. | ||||||||||||
| methods | yes | no | {Object |
See Methods table below for details. | ||||||||||||
| events | yes | no | {Object |
See Events table below for details. | ||||||||||||
| slots | yes | no | {Object |
See Slots table below for details. | ||||||||||||
compositeDependencies |
no | no | {Object |
Dependency to semantic version mapping for composite dependencies.
3rd party libraries should not be included in this mapping.
{"composite1": "1.2.0", "composite2": ">=2.1.0"}
This metadata property is deprecated as of JET 5.0.0. Please use the "dependencies" metadata property instead. |
||||||||||||
dependencies |
no | no | {Object |
Dependency to semantic version mapping for composite dependencies.
3rd party libraries should not be included in this mapping.
{"composite1": "1.2.0", "composite2": ">=2.1.0"} |
||||||||||||
description |
no | no | {string} | A high-level description for the component. | ||||||||||||
displayName |
no | no | {string} | A user friendly, translatable name of the component. | ||||||||||||
extension |
no | no | {Object} | Placeholder for Extension metadata. Each section is identified by a key that specifies the downstream tool that will process this metadata.
For example:
Please consult the documentation for the downstream tool to determine what (if any) extension metadata is supported. |
||||||||||||
help |
no | no | {string} | Specifies a URL to detailed API documentation for this component. | ||||||||||||
icon |
no | no | {Object} | One or more optional images for representing the component within a design time environment's component palette. The object has the following properties:
Properties
|
||||||||||||
license |
no | no | {string} | A reference to the license under which use of the component is granted. The value can be:
|
||||||||||||
propertyLayout |
no | no | {Array<{object}>}{object}> | An optional ordered array of one or more propertyLayoutGroup objects. A propertyLayoutGroup enables a component author to order
and shape the groupings of their properties in the design time environment for their component. Each propertyLayoutGroup object is defined as follows:
Properties
Notes
ExampleA typical Property Inspector layout for the oj-input-text component might look as follows:
|
||||||||||||
styleClasses |
no | no | {Array<{object}>}{object}> | Optional array of groupings of style class names that are applicable to this component. Each grouping object has the following properties:
Properties
|
Properties
| Key | Value | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
[property name] |
Object containing the following properties:
Properties
|
Methods
| Key | Value | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
[method name] |
Object containing the following properties:
Properties
|
Events
| Key | Value | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
[event name] |
Object containing the following properties:
Properties
|
Slots
| Key | Value | |||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
[slot name] |
Object containing the following properties:
Properties
|
Example of Run Time Metadata
The JET framework will ignore "extension" fields. Extension fields cannot be defined at the first level of the "properties", "methods", "events", or "slots" objects.
{
"name": "demo-card",
"version": "1.0.2",
"jetVersion": ">=3.0.0 <5.0.0", "properties": { "currentimage" : "type": "string", "readonly": true }, "images": "array"
},
"isShown": {
"type": "boolean",
"value": true
}
},
"methods": {
"nextImage": {
"internalName": "_nextImg"
"extension": "This is where a composite can store additional data."
},
"prevImage": {}
},
"events": {
"cardclick": {}
}
}
5.0.0",>
Properties
Properties defined in provided Metadata will be made available through the $properties property of the View binding context
and through the properties property on the context passed to the provided ViewModel constructor function or lifecycle listeners.
The application can access the composite component properties by accessing them directly from the
DOM element. Using the DOM setAttribute and removeAttribute APIs will also result in property updates. Changes made to properties will
result in a [property]Changed event being fired for that property if the property was modified internally by the composite or externally
by the application if the composite has been upgraded and its busy state resolved. Early property sets before the composite has been upgraded
while allowed, will not result in [property]Changed events and will be passed to the component as part of its initial state.
Property-to-Attribute Mapping
The following rules apply when mapping property to attribute names:
- Attribute names are case insensitive. CamelCased properties are mapped to kebab-cased attribute names by inserting a dash before the uppercase letter and converting that letter to lower case, e.g. a "chartType" property will be mapped to a "chart-type" attribute.
- The reverse occurs when mapping a property name from an attribute name.
Subproperties
Subproperties can be exposed and documented by adding nested properties objects in the composite metadata. Event listeners should be added for the top level property, with applications checking the event's subproperty field to access information about the subproperty changes. Subproperties can be set declaratively using dot notation, e.g. person.first-name="{{name}}". Setting overlapping attributes, e.g. person="{{personInfo}}" person.first-name="{{name}}" will cause an error to be thrown.
Subproperties can also be set programmatically using the set/getProperty methods.
Note that while setting the subproperty using dot notation via the element's top level property is allowed, the setProperty method must be used in order
to trigger a property change event.
element.setProperty("person.firstName", Fred);
var firstName = element.getProperty("person.firstName");
Styling
Composite component styling can be done via provided css. The JET framework will add the oj-complete class to the composite DOM
element after metadata properties have been resolved. To prevent a flash of unstyled content before the composite properties have been setup,
the composite css can include the following rule to hide the composite until the oj-complete class is set on the element.
my-chart:not(.oj-complete) {
visibility: hidden;
}
Composite CSS will not be scoped to the composite component and selectors will need to be appropriately selective. We recommend scoping CSS classes and prefixing class names with the composite name as seen in the example below. Note that we do not recommend overriding JET component CSS. Composites should only update JET component styling via SASS variables.
my-chart .my-chart-text {
color: white;
}
Events
Composite components fire the following events. Any custom composite events should be created and fired by the composite's ViewModel and documented in the metadata for design and run time environments as needed. In addition to standard add/removeEventListener syntax, declarative event listeners will also be supported for custom composite events and [property]Changed events. As a result, if a composite declares an event of type "customType", applications can listen by setting the "onCustomType" property or the "on-custom-type" attribute. Similarly, property change listeners can be set via the appropriate "on[property]Changed" property or "on-[property-name]-changed" attribute. Expression syntax can be used in the on-[event-name] attributes, but we do not support executing arbitrary JavaScript like those for native event attributes like onclick.
- [property]Changed - Fired when a property is modified and contains the following fields in its event detail object:
- previousValue - The previous property value
- value - The new property value
- updatedFrom - Where the property was updated from. Supported values are:
- internal - The View or ViewModel
- external - The DOM Element either by its property setter, setAttribute, or external data binding.
- subproperty - An object containing information about the subproperty that changed with the following fields:
- path - The subproperty path that changed
- previousValue - The previous subproperty value
- value - The new subproperty value
Lifecycle
If a ViewModel is provided for a composite component, the following optional callback methods can be defined on its ViewModel and will be called at each stage of the composite component's lifecycle. The ViewModel provided at registration can either be a function which will be treated as a constructor that will be invoked to create the ViewModel instance or an object which will be treated as a singleton instance. The Object instance return type for the ViewModel is deprecated in 5.0.0.
initialize(context)
Deprecated since 5.0.0. This optional method may be implemented on the ViewModel to perform initialization tasks. This method will be invoked only if the ViewModel specified during registration is an object instance as opposed to a constructor function. If the registered ViewModel is a constructor function, the same context object will be passed to the constructor function instead. This method can return 1) nothing in which case the original model instance will be used, 2) a new model instance which will replace the original, or 3) a Promise which resolves to a new model instance which will replace the original and delay additional lifecycle phases until it is resolved.
Parameters:
| Name | Type | Description | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
context |
Object | An object with the following key-value pairs:
Properties:
|
activated(context)
This optional method may be implemented on the ViewModel and will be invoked after the ViewModel is initialized. This method can return a Promise which will delay additional lifecycle phases until it is resolved and can be used as a hook for data fetching.
Parameters:
| Name | Type | Description | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
context |
Object | An object with the following key-value pairs:
Properties:
|
attached(context)
This optional method is deprecated in 4.2.0 in favor of the connected method. This method is invoked after the View is inserted into the DOM and will only be called once. Note that if the composite needs to add/remove event listeners, we recommend using the connected/disconnected methods.
Parameters:
| Name | Type | Description | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
context |
Object | An object with the following key-value pairs:
Properties:
|
connected(context)
This optional method may be implemented on the ViewModel and will be invoked after the View is first inserted into the DOM and then each time the composite is reconnected to the DOM after being disconnected. Note that if the composite needs to add/remove event listeners, we recommend using this and the disconnected methods.
Parameters:
| Name | Type | Description | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
context |
Object | An object with the following key-value pairs:
Properties:
|
bindingsApplied(context)
This optional method may be implemented on the ViewModel and will be invoked after the bindings are applied on this View.
Parameters:
| Name | Type | Description | |||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
context |
Object | An object with the following key-value pairs:
Properties:
|
propertyChanged(context)
This optional method may be implemented on the ViewModel and will be invoked when properties are updated before the [property]Changed event is fired.
Parameters:
| Name | Type | Description | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| property | string | The property that changed. | ||||||||||||
| value | * | The current value of the property that changed. | ||||||||||||
| previousValue | * | The previous value of the property that changed. | ||||||||||||
| updatedFrom | string |
Where the property was updated from. Supported values are:
|
||||||||||||
| subproperty | Object | An object holding information about the subproperty that changed.
|
detached(element)
This method is deprecated in 4.2.0 to the renamed disconnected method with the same behavior. This optional method may be implemented on the ViewModel and will be invoked when this composite component is detached from the DOM.
Parameters:
| Name | Type | Description |
|---|---|---|
element |
Node | The composite component DOM element. |
disconnected(element)
This optional method may be implemented on the ViewModel and will be invoked when this composite component is disconnected from the DOM.
Parameters:
| Name | Type | Description |
|---|---|---|
element |
Node | The composite component DOM element. |
Data Binding and Expression Writeback
Besides string literals, composite attributes can be set using expression syntax which is currently compatible with knockout expression syntax. Applications can control expression writeback in the composite component by using {{}} syntax for two-way writable binding expressions or [[]] for one-way only expressions. In the example below, the salesData expression will not be written back to if the 'data' property is updated by the composite component's ViewModel. The 'data' property will contain the current value, but the salesData expression will not be updated. Please note that this will cause the expression and property values to be out of sync until the salesData expression is updated in the application's ViewModel. Alternatively, if the 'axisLabels' property is updated by the ViewModel, both the 'axisLabel' property and the showAxisLabels expression will contain the updated value.
<my-chart data="[[salesData]]" axis-labels={{showAxisLabels}} ... >
</my-chart>
readOnly
The composite component Metadata also defines properties to control expression writeback and property updates. If a property's readOnly option is omitted, the value is false by default, meaning the property can be updated outside of the composite component. If readOnly is true, the property can only be updated inside of the composite component by the ViewModel or View.
writeback
If a property's Metadata defines its writeback property as true (false by default), any bound attribute expressions will be updated when the property is updated unless the attribute binding was done with a one-way "[[]]"" binding syntax.
Slotting
The View can also contain slots where application provided DOM can go. Complex composite components which can contain additional composites and/or content for child facets defined in its associated View can be constructed via slotting. There are two ways to define a composite's slots, using either an oj-bind-slot or an oj-bind-template-slot element to indicate that that slot's content will be stamped using an application provided template. See the relevant slot API docs for more information.
Binding Order
The following steps will occur when processing the binding for a composite component:
- Apply bindings to children using the composite component's binding context.
- Create a slot map assigning component child nodes to View slot elements.
- At this point the component child nodes are removed from the DOM and live in the slot map.
- Insert the View and apply bindings to it with the ViewModel's binding context.
- The composite's children will be 'slotted' into their assigned View slots.
- The oj-bind-slot's slot attribute, which is "" by default, will override its assigned node's slot attribute.
- Since:
- 2.0
Methods
-
(static) getComponentMetadata(name) → {Object|null}
PREVIEW: This is a preview API. Preview APIs are production quality, but can be changed on a major version without a deprecation path.
-
Returns the composite metadata with the given name or null if the composite has not been registered.
Parameters:
Name Type Description namestring The component name, which should contain a dash '-' and not be a reserved tag name. - Since:
- 5.0.0
Returns:
- Type
- Object | null
-
(static) getMetadata(name) → {Promise|null}
-
Returns a Promise resolving with the composite metadata with the given name or null if the composite has not been registered.
Parameters:
Name Type Description namestring The component name, which should contain a dash '-' and not be a reserved tag name. Deprecated:
Since Description 5.0.0Use oj.Composite.getComponentMetadata instead. Returns:
- Type
- Promise | null
-
(static) register(name, descriptor)
-
Registers a composite component
Parameters:
Name Type Description namestring The component name, which should contain a dash '-' and not be a reserved tag name. descriptorObject The registration descriptor. The descriptor will contain keys for Metadata, View, ViewModel and CSS that are detailed below. At a minimum a composite must register Metadata and View files, but all others are optional. The composite resources should be mapped directly to each descriptor key. The support for an object with an 'inline' key mapped to the resource has been deprecated in 5.0.0. See the registration section above for a sample usage. Properties
Name Type Description metadataObject A JSON formatted object describing the composite APIs. See the metadata documentation for more info. viewstring A string, array of DOM nodes, or document fragment representing the HTML that will be used for the composite. The array of DOM nodes and document fragment types are deprecated in 5.0.0. cssstring (Deprecated) A string containing the composite CSS. Note that this key should not be used if the composite styles contain references to any external resources and is deprecated in 4.1.0. require-css, a RequireJS CSS plugin, is the current recommendation for CSS loading. viewModelObject This option is only applicable to composites hosting a Knockout template with a ViewModel and ultimately resolves to a constructor function or object instance. The Object instance return type for the ViewModel is deprecated in 5.0.0. If the initial ViewModel resolves to an object instance, the initialize lifecycle listener will be called. See the initialize documentation for more information. parseFunctionfunction(string, string, Object, function(string)) The function that will be called to parse attribute values. Note that this function is only called for non bound attributes. The parseFunction will take the following parameters: - {string} value: The value to parse.
- {string} name: The name of the property.
- {Object} meta: The metadata object for the property which can include its type, default value, and any extensions that the composite has provided on top of the required metadata.
- {function(string)} defaultParseFunction: The default parse function for the given attribute type which is used when a custom parse function isn't provided and takes as its parameters the value to parse.