- Developing Applications with Oracle JET
- Working with Oracle JET Composite Components
- Creating Composite Components
Creating Composite Components
Use the Oracle JET command-line interface (CLI) to create a composite component template that you can populate with content. If you’re not using the tooling, you can add the composite component files and folders manually to your Oracle JET application.
The following image shows a simple Oracle JET composite component named demo-card that displays contact cards with the contact’s name and image if available. When the user selects the card, the content flips to show additional detail about the contact.
The procedure below lists the high level steps to create a composite component using this demo-card component as an example. Portions of the code are omitted for the sake of brevity, but you can find the complete example at Composite Component - Basic. You can also download the demo files by clicking the download button (
) in the cookbook.
To create a composite component:
- Determine a name for your composite component.The Web Component specification restricts custom element names as follows:
-
Names must contain a hyphen.
-
Names must start with a lowercase ASCII letter.
-
Names must not contain any uppercase ASCII letters.
-
Names should use a unique prefix to reduce the risk of a naming collision with other components.
A good pattern is to use your organization’s name as the first segment of the component name, for example, org-component-name.
-
Names must not be any of the reserved names. For the complete list, see valid custom element name.
Note:
Oracle JET also reserves theojnamespace and prefixes.
For example, use
demo-cardto duplicate the contact card example. -
- Determine where to place your composite component, using one of the following options.
-
Add the composite component to an existing Oracle JET application that you created with the Oracle JET CLI as described in Create a Web Application Using the Oracle JET Command-Line Interface or Create a Hybrid Mobile Application.
If you use this method, you’ll use the CLI to create a composite component template that contains the folders and files you’ll need to store the composite component’s content.
-
Use the Oracle JET CLI to scaffold a new composite component, based on a template, as well as a new Oracle JET application that you can use to view and test the composite component.
If you use this method, you don’t need to create a new Oracle JET application. However, you’ll still need to install the Oracle JET CLI as described in Install the Prerequisite Packages. Also, the application contains an
index.htmlfile without UI, navigation, or layout, and you’ll have to add those features manually to your application. -
Manually add the composite component to an existing Oracle JET application that doesn’t use the Oracle JET CLI.
If you use this method, you’ll create the folders and files manually to store the composite component’s content.
-
- Depending upon the choice you made in the previous step, perform one of the following tasks to create the composite component.
-
If you used the Oracle JET CLI to create your application, in the application’s top level directory, enter the following command at a terminal prompt to generate the composite component template:
ojet create component component-nameFor example, enter
ojet create component demo-cardto create a composite component nameddemo-card. The command will addjet-composites/demo-cardto the application’sjsfolder and files containing stub content for the composite component. -
If you installed the Oracle JET CLI and want to create a composite component and an Oracle JET application that you can use for testing, enter the following command at a terminal prompt in a folder of your choosing:
ojet create component component-nameFor example, enter
ojet create component demo-cardto create an application and a composite component nameddemo-card.The command will add a
demo-cardfolder containing the Oracle JET application files,jet-composites/demo-cardto the new application’sjsfolder, and files containing stub content for the composite component. -
If you’re not using the Oracle JET CLI, create a
jet-compositesfolder in your application’sjsfolder, and add folders containing the name of each composite component you will create.For the demo-card example, create the
jet-compositesfolder and add ademo-cardfolder to it. You’ll create the individual composite component files in the remaining steps.
-
- Determine the properties, methods, and events that your composite component will support and add them to the
component.jsonfile in the composite component’s root folder, creating the file if needed.Important:
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 property nameslotshould not be used. Also, you must not re-define the global attributes and events listed in the following link: Global attributes.The demo-card example defines properties for the composite component and the contact’s full name, employee image, title, work number, and email address. The required properties are highlighted in bold.
{ "name": "demo-card", "description": "A card element that can display an avatar or initials on one side and employee information on the other.", "version": "1.0.2", "displayName": "Demo Card", "jetVersion": ">=3.0.0 <5.2.0", "properties": { "name": { "description": "The employee's full name.", "type": "string" }, "avatar": { "description": "The url of the employee's image.", "type": "string" }, "workTitle": { "description": "The employee's job title.", "type": "string" }, "workNumber": { "description": "The employee's work number.", "type": "number" }, "email": { "description": "The employee's email.", "type": "string" } } }This basic demo-card example only defines properties for the composite component. You can also add metadata that defines methods and events as shown in the cookbook’s Composite Component - Events example. The metadata lists the name of the method or event and supported parameters.
{ "properties": { ... contents omitted }, "methods": { "flipCard" { "description": "Method to toggle flipping a card" }, "enableFlip" { "description": "Enables or disables the ability to flip a card.", "params": [ { "name": "bEnable", "description": "True to enable card flipping and false otherwise.", "type": "boolean" } ] }, }, "events": { "cardClick": { "description": "Triggered when a card is clicked and contains the value of the clicked card..", "bubbles": true, "detail": { "value": { "description": "The value of the card.", "type": "string" } } } } } - If your composite component contains a ViewModel, add its definition to
composite-name-viewModel.jsin the composite component’s root folder, creating the file if needed.The code sample below shows the ViewModel for the demo-card composite component. Comments describe the purpose, parameters, and return value of each function.
define(['knockout', 'ojs/ojknockout'], function(ko) { function model(context) { var self = this; self.initials = null; self.workFormatted = null; var element = context.element; /** * Formats a 10 digit number as a phone number. * @param {number} number The number to format * @return {string} The formatted phone number */ var formatPhoneNumber = function(number) { return Number(number).toString().replace(/(\d{3})(\d{3})(\d{4})/, '$1-$2-$3'); } if (context.properties.name) { var initials = context.properties.name.match(/\b\w/g); self.initials = (initials.shift() + initials.pop()).toUpperCase(); } if (context.properties.workNumber) self.workFormatted = formatPhoneNumber(context.properties.workNumber); /** * Flips a card * @param {MouseEvent} event The click event */ self.flipCard = function(event) { if (event.type === 'click' || (event.type === 'keypress' && event.keyCode === 13)) { // It's better to look for View elements using a selector // instead of by DOM node order which isn't gauranteed. $(element).children('.demo-card-flip-container').toggleClass('demo-card-flipped'); } }; } return model; } ) - In the composite component’s root folder, add the View definition to
composite-name-view.html, creating the file if needed.The View for the demo-card composite component is shown below. Any property defined in the component’s metadata is accessed using the
$propertiesproperty of the View binding context.<div tabindex="0" role="group" class="demo-card-flip-container" on-click="[[flipCard]]" on-keypress="[[flipCard]]" :aria-label="[[$properties.name + ' Press Enter for more info.']]"> <div class="demo-card-front-side"> <oj-avatar class="demo-card-avatar" role="img" size="lg" initials="[[initials]]" src="[[$properties.avatar]]" :aria-label="[['Avatar of ' + $properties.name]]"> </oj-avatar> <h2> <oj-bind-text value="[[$properties.name]]"></oj-bind-text> </h2> </div> <div class="demo-card-back-side"> <div class="demo-card-inner-back-side"> <h2> <oj-bind-text value="[[$properties.name]]"></oj-bind-text> </h2> <h5> <oj-bind-text value="[[$properties.workTitle]]"></oj-bind-text> </h5> <oj-bind-if test="[[$properties.workNumber != null]]"> <h5>Work</h5> <span class="demo-card-text"><oj-bind-text value="[[workFormatted]]"></oj-bind-text></span> </oj-bind-if> <oj-bind-if test="[[$properties.email != null]]"> <h5>Email</h5> <span class="demo-card-text"><oj-bind-text value="[[$properties.email]]"></oj-bind-text></span> </oj-bind-if> </div> </div> </div>For accessibility, the View’s role is defined as
group, witharia-labelspecified for the contact’s name. In general, follow the same accessibility guidelines for the composite View markup that you would anywhere else within the application. - If you’re not using the Oracle JET CLI, create the
loader.jsRequireJS module and place it in the composite component’s root folder.The
loader.jsmodule defines the composite dependencies and registers the composite’stagName,demo-cardin this example.define(['ojs/ojcore', 'text!./demo-card-view.html', './demo-card-viewModel', 'text!./component.json', 'css!./demo-card-styles', 'ojs/ojcomposite'], function(oj, view, viewModel, metadata) { oj.Composite.register('demo-card', { view: view, viewModel: viewModel, metadata: JSON.parse(metadata) }); } );In this example, the CSS is loaded through a RequireJS plugin (
css!./demo-card-styles), and you do not need to pass it explicitly inoj.Composite.register(). - Configure any custom styling that your composite component will use.
-
If you only have a few styles, add them to
composite-name-styles.cssfile in the composite component’s root folder, creating the file if needed.For example, the demo-card composite component defines styles for the demo card’s display, width, height, margin, padding, and more. It also defines the classes that will be used when the user clicks a contact card. A portion of the CSS is shown below. For the complete code, see
demo-card-styles.cssin the Composite Component - Basic cookbook sample./* This is to prevent the flash of unstyled content before the composite properties have been setup. */ demo-card:not(.oj-complete) { visibility: hidden; } demo-card { display: block; width: 200px; height: 200px; perspective: 800px; margin: 10px; box-sizing: border-box; cursor: pointer; } demo-card h2, demo-card h5, demo-card a, demo-card .demo-card-avatar { color: #fff; padding: 0; } ... remaining contents omitted - If you used the Oracle JET tooling to create your application and want to use Sass to generate your CSS:
-
If needed, at a terminal prompt in your application’s top level directory, type the following command to add node-sass to your application:
ojet add sass. -
Create
composite-name-styles.scssand place it in the composite component’s top level folder. -
Edit
composite-name-styles.scsswith any valid SCSS syntax and save the file.In this example, a variable defines the demo card size:$demo-card-size: 200px; /* This is to prevent the flash of unstyled content before the composite properties have been setup. */ demo-card:not(.oj-complete) { visibility: hidden; } demo-card { display: block; width: $demo-card-size; height: $demo-card-size; perspective: 800px; margin: 10px; box-sizing: border-box; cursor: pointer; } demo-card h2, demo-card h5, demo-card a, demo-card .demo-card-avatar { color: #fff; padding: 0; } ... remaining contents omitted -
To compile Sass, at a terminal prompt type
ojet buildorojet servewith the--sassflag and application-specific options.ojet build|serve [options] --sassojet build --sasswill compile your application and generatecomposite-name-styles.cssandcomposite-name-styles.css.mapfiles in the default platform’s folder. For a web application, the command will place the CSS inweb/js/js-composites/composite-name.ojet serve --sasswill also compile your application but will display the web application in a running browser withlivereloadenabled. If you save a change tocomposite-name-styles.scssin the application’ssrc/js/jet-composites/composite-namefolder, Oracle JET will compile Sass again and refresh the display.Tip:
For help withojetcommand syntax, typeojet helpat a terminal prompt.
-
-
- If you want to add documentation for your composite component, add content to
README.mdin your composite component's root folder, creating the file if needed.Your
README.mdfile should include an overview of your component with well-formatted examples. Include any additional information that you want to provide to your component’s consumers. The recommended standard forREADMEfile format is markdown. For help with markdown, refer to https://guides.github.com/features/mastering-markdown/.


