About Web Components
Oracle JET Web Components are packaged as standalone modules that your application can load using RequireJS. The framework supplies APIs that you can use to register Web Components. Knockout currently provides one and two way data binding, template markup, and Web Component activation.
Web Components follow much of the W3C Web Components - Custom Elements specification which describes a mechanism for enabling authors to define and use custom DOM elements. If you are new to Web Components and would like to learn more, visit this Oracle Blogs page for a series of articles that will introduce you to important concepts: https://blogs.oracle.com/groundside/cca.
Web Component Architecture
The following image shows a high-level view of the JET Web Component architecture. In this example, an Oracle JET application is consuming the Web Component, but you can add Web Components to other JavaScript or Oracle applications where supported.
Web Components contain:
-
A custom DOM element: Named element that functions as a HTML element.
<my-web-component attribute1="value1" attribute2="value2" ...> </my-web-component>
-
Web Component binding definition: Knockout expression for setting Web Component attributes.
<my-web-component attribute1="value1" attribute2="[[value2]]" attribute3="{{value3}}"> </my-web-component>
attribute1
’s value can be a primitive JavaScript type (boolean, number, string) or a JSON object,attribute2
’s value uses one way data binding, andattribute3
‘s value uses a two way binding. One way bindings on Web Components specify that the expression will not update the application’s ViewModel if the value changes. In two way bindings, the expression will update and the value written back to the application’s ViewModel.In the following code sample, the Web Component is declared with three attributes:
type
,data
, andaxis-labels
.<my-chart type=bubble data="[[salesData]]" axis-labels={{showAxisLabels}} ... </my-chart>
Because the
salesData
expression is declared using one way binding ([[salesData]]
), it will not be written back to if thedata
property is updated by the Web Component's ViewModel. Thedata
property will contain the current value, but thesalesData
expression will not be updated. Alternatively, if theaxisLabels
property is updated by the ViewModel, both theaxisLabel
property and the{{showAxisLabels}}
expression will contain the updated value. -
Metadata: Data provided in JSON format which defines the Web Component’s required properties:
name
,version
, andjetVersion
. Metadata may also define optional properties, includingdescription
,displayName
,dependencies
,icon
,methods
,events
, andslots
.Web Components support both runtime and design time metadata. Design time metadata isn’t required at runtime and is useful for design time tools and property editors. Design time tools can define tools-specific metadata extensions to the Web Component’s metadata. For any tool-specific metadata extensions, refer to the documentation for that specific tool. For additional information about metadata properties, see oj.Composite in the API documentation.
Note:
The following sample shows some of the available metadata fields with descriptions of their content and whether they are not used at run time. Required metadata are highlighted in bold.{ "name": "The component tag name", "version": "The component version. Note that changes to the metadata even for minor updates like updating the jetVersion should result in at least a minor Web Component version change, e.g. 1.0.0 -> 1.0.1.", "jetVersion": "The semantic version of the supported JET version(s). Web Component 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 Web Components with each major release and update the component metadata or release a new version that is compatible with the new release changes.", "description": "A high-level description for the component. Not used at run time.", "displayName": "A user friendly, translatable name of the component. Not used at run time.", "properties": { "property1": { "description": "A description for the property. Not used at run time.", "displayName": "A user friendly, translatable name of the property. Not used at run time.", "readOnly": "Boolean that determines whether a property can be updated outside of the ViewModel. False by default.", "type": "The type of the property, following Google's Closure Compiler syntax.", "value": "Object containing an optional default value for a property.", "writeback": "Boolean that determines whether an expression bound to this property should be written back to. False by default.", "enumValues": "An optional array of valid enum values for a string property. An error is thrown if a property value does not match one of the provided enumValues.", "properties": "A nested properties object for complex properties. Subproperties exposed using nested properties objects in the metadata can be set using dot notation in the attribute. See the Subproperties section for more details on working with subproperties." }, "property2": { ... contents omitted } }, "methods": { "method1": { "description": "A description for the method. Not used at run time.", "displayName": "A user friendly, translatable name of the method. Not used at run time.", "internalName": "An optional ViewModel method name that is different from, but maps to this method.", "params": "An array of objects describing the method parameter . Not used at run time.", "return": "The return type of the method, following Closure Compiler syntax. Not used at run time." }, "method2": { ... contents omitted } }, "events": { "event1": { "bubbles": "Boolean that indicates whether the event bubbles up through the DOM or not. Defaults to false. Not used at run time.", "cancelable": "Boolean that Indicates whether the event is cancelable or not. Defaults to false. Not used at run time.", "description": "A description for the event. Not used at run time.", "displayName": "A user friendly, translatable name of the method. Not used at run time.", "detail": { "field name": "Describes the properties available on the event's detail property which contains data passed when initializing the event. Not used at run time." } }, "event2": { ... contents omitted } }, "slots": { "slot1": { "description": "A description for the slot. Not used at run time.", "displayName": "A user friendly, translatable name of the method. Not used at run time." } } }
HTML markup: (Required) Contains the View definition which describes how to render the Web Component.
-
JavaScript: Optional script for defining the ViewModel and custom events.
The ViewModel is also where you define callbacks for various stages of the Web Component’s lifecycle. Web Components support the following optional lifecycle methods:
activated (context)
,connected (context)
,bindingsApplied (context)
,propertyChanged (context)
, anddisconnected (element)
. For more information on lifecycle methods, see oj.Composite - Lifecycle. -
CSS: Optional styling for the Web Component.
CSS is not scoped to Web Components, and you must define styles appropriately.
-
SCSS: Optional files containing Sass variables to generate the Web Component’s CSS.
If you’re defining only a few styles for your component, then adding the CSS manually may be sufficient. However, there may be use cases where you want to use Sass variables to generate the CSS. In those cases, create and place the SCSS files in the Web Component’s folder and use the tooling to add node-sass to your application. See Step 8 - Creating Web Components.
Important:
You must add the Sass files manually to the Web Component’s folder. The tooling will compile any Sass files if they exist, but it will not create them for you.
Web Component Files
Place Web Component files in a folder with the same name as the Web Component tag. Typically, you place the folder within your application in a jet-composites
folder: application-path/jet-composites/my-web-component/
.
You can also place your Web Component in a different file location or reference a Web Component on a different server using RequireJS path mapping. For examples, see oj.Composite - Packaging and Registration.
-
my-web-component—view.html
: view template -
my-web-component—viewModel.js
: ViewModel -
component.json
: metadata -
my-web-component-styles.css
: CSS styling -
my-web-component-styles.scss
: Sass variables to generate CSS for Web Components -
loader.js
: RequireJS module defining the dependencies for its metadata, View, ViewModel, and CSS This file should also include the Web Component registration.
Web Component Slotting
Use slotting to add child components (which can also be Web Components) that get slotted into specified locations within the Web Component's View markup. The following example contains a portion of the View markup for a Web Component named demo-columns.
<div class="oj-flex oj-flex-item-pad">
<div role="group" :aria-label="[[$properties.headers[0]]]"
class="oj-flex-item demo-columns-col-a oj-flex oj-sm-flex-direction-column oj-sm-align-items-center">
<h3>
<oj-bind-text value="[[$properties.headers[0]]]"></oj-bind-text>
</h3>
<oj-bind-slot name="columnA">
</oj-bind-slot>
</div>
... content omitted
</div>
In this example, the demo-columns Web Component defines an oj-bind-slot
named columnA
. As shown below, a developer can specify a child component with a slot named columnA
when adding the demo-columns Web Component to the page.
<demo-columns id="composite-container" headers='["Sales", "Human Resources", "Support"]'>
<!-- ko foreach: sales -->
<demo-card slot="columnA" name="[[name]]" work-title="[[title]]"></demo-card>
<!-- /ko -->
... contents omitted
</demo-columns>
Web Component Template Slots
You can define placeholders in your template using template slots that can be filled with any markup fragment you want when the template element is used within a markup of your component. When you need to reuse a stamped template with varying data, you can use a template slot to expose the additional data from the component's binding context.
Template slots for Web Components are used to define additional binding contexts for a slotted content within an application. To declaratively define a template slot, use the oj-bind-template-slot
element in the Web Component's View markup for the slot that contains a stamped template DOM. The oj-bind-template-slot
element is similar to the oj-bind-slot
element, but its slotted content should be wrapped inside a template
element within the application DOM.
In the below example, the demo-list Web Component defines an oj-bind-template-slot
named item
. This template slot provides the data
attribute that exposes additional properties to the template DOM and an as
attribute that is used as an alias for the $current
variable. Note that the as
attribute for oj-bind-template-slot
element can be referenced only inside a default template.
<table>
<thead>
<tr>
<th>
<oj-bind-text value="[[$properties.header]]"></oj-bind-text>
</th>
</tr>
</thead>
<tbody>
<oj-bind-for-each data="{{$properties.data}}">
<template>
<tr>
<td>
<!-- Template slot for list items with default template and an optional alias -->
<oj-bind-template-slot name="item" data="{{ {'value': $current.data} }}" as="listItem">
<!-- Default template -->
<template>
<span>
<oj-bind-text value='[[listItem.value]]'</oj-bind-text>
</span>
</template>
</oj-bind-template-slot>
</td>
</tr>
</template>
</oj-bind-for-each>
</tbody>
... contents omitted
</table>
The oj-bind-template-slot
children are resolved when the Web Component View bindings are applied and are then resolved in the application's binding context extended with additional properties provided by the Web Component. These additional properties are available on the $current
variable in the application provided template slot. The application can use an optional data-oj-as
attribute as an alias in the template instead of the $current
variable. The following example contains a portion of the application’s markup named demo-list.
<demo-list data="{{groceryList}}" header="Groceries">
<template slot="item" data-oj-as="groceryItem">
<oj-checkboxset>
<oj-option value="bought"><oj-bind-text value='[[groceryItem.value]]'></oj-bind-text></oj-option>
</oj-checkboxset>
</template>
... contents omitted
</demo-list>
The Oracle JET Cookbook at Web Component - Template Slots includes complete examples for using template slots. oj-bind-template-slot API documentation describes the attributes and other template slot properties.
Web Component Examples
The Oracle JET Cookbook contains complete examples for creating basic and advanced Web Components. You can also find examples that use slotting and data binding. For details, see Web Component - Basic.
For additional information about Web Component fields and methods, see oj.Composite in the API documentation.