Developing Applications Oracle Jet
Developing Applications Oracle Jet
Developing Applications Oracle Jet
(Oracle JET)
Developing Applications with Oracle JET
6.0.0
E99243-01
October 2018
Oracle JavaScript Extension Toolkit (Oracle JET) Developing Applications with Oracle JET, 6.0.0
E99243-01
Copyright © 2014, 2018, Oracle and/or its affiliates. All rights reserved.
Contributing Authors: Walter Egan, Krithika Gangadhar, Vaibhav Gupta, Sandhya Hombardi
This software and related documentation are provided under a license agreement containing restrictions on
use and disclosure and are protected by intellectual property laws. Except as expressly permitted in your
license agreement or allowed by law, you may not use, copy, reproduce, translate, broadcast, modify,
license, transmit, distribute, exhibit, perform, publish, or display any part, in any form, or by any means.
Reverse engineering, disassembly, or decompilation of this software, unless required by law for
interoperability, is prohibited.
The information contained herein is subject to change without notice and is not warranted to be error-free. If
you find any errors, please report them to us in writing.
If this is software or related documentation that is delivered to the U.S. Government or anyone licensing it on
behalf of the U.S. Government, then the following notice is applicable:
U.S. GOVERNMENT END USERS: Oracle programs, including any operating system, integrated software,
any programs installed on the hardware, and/or documentation, delivered to U.S. Government end users are
"commercial computer software" pursuant to the applicable Federal Acquisition Regulation and agency-
specific supplemental regulations. As such, use, duplication, disclosure, modification, and adaptation of the
programs, including any operating system, integrated software, any programs installed on the hardware,
and/or documentation, shall be subject to license terms and license restrictions applicable to the programs.
No other rights are granted to the U.S. Government.
This software or hardware is developed for general use in a variety of information management applications.
It is not developed or intended for use in any inherently dangerous applications, including applications that
may create a risk of personal injury. If you use this software or hardware in dangerous applications, then you
shall be responsible to take all appropriate fail-safe, backup, redundancy, and other measures to ensure its
safe use. Oracle Corporation and its affiliates disclaim any liability for any damages caused by use of this
software or hardware in dangerous applications.
Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of
their respective owners.
Intel and Intel Xeon are trademarks or registered trademarks of Intel Corporation. All SPARC trademarks are
used under license and are trademarks or registered trademarks of SPARC International, Inc. AMD, Opteron,
the AMD logo, and the AMD Opteron logo are trademarks or registered trademarks of Advanced Micro
Devices. UNIX is a registered trademark of The Open Group.
This software or hardware and documentation may provide access to or information about content, products,
and services from third parties. Oracle Corporation and its affiliates are not responsible for and expressly
disclaim all warranties of any kind with respect to third-party content, products, and services unless otherwise
set forth in an applicable agreement between you and Oracle. Oracle Corporation and its affiliates will not be
responsible for any loss, costs, or damages incurred due to your access to or use of third-party content,
products, or services, except as set forth in an applicable agreement between you and Oracle.
Contents
Preface
Audience xiv
Documentation Accessibility xiv
Related Resources xiv
Conventions xv
iii
Use RequireJS to Manage Library, Link, and Script References 2-24
Create a Web Application Using the Oracle JET Starter Templates 2-25
Downloading Oracle JET with a Starter Template 2-25
Loading the Oracle JET Starter Templates 2-25
Add Oracle JET to an Existing JavaScript Application 2-29
Get Started with Oracle JET Hybrid Mobile Application Development 2-30
Install the Mobile Tooling 2-31
Install Apache Cordova 2-31
Install Android Development Tools 2-32
Install iOS Development Tools 2-37
Install Windows Development Tools 2-37
Create a Hybrid Mobile Application 2-40
Scaffold a Hybrid Mobile Application with the Oracle JET CLI 2-41
Build a Hybrid Mobile Application with the Oracle JET CLI 2-46
Customize the Hybrid Mobile Application Tooling Workflow Using Hook
Points 2-49
Serve a Hybrid Mobile Application with the Oracle JET CLI 2-52
Review Your Application Settings in the config.xml File 2-58
Change the Splash Screen and App Launcher Icon 2-59
Use Cordova Plugins to Access Mobile Device Services 2-61
About Apache Cordova and Cordova Plugins 2-61
Use a Plugin in Your App 2-63
Cordova Plugins Recommended by Oracle JET 2-63
Use a Different Web View in your JET Hybrid Mobile App 2-63
Use the Native Platform’s Date Picker UI in your JET Hybrid Mobile App 2-64
Working with the Oracle JET Starter Templates 2-64
About the Starter Templates 2-65
Modifying Starter Template Content 2-67
Optimizing Application Startup Using Oracle CDN and Oracle JET Libraries 2-70
iv
Responsive Form Layouts 3-17
Adding Responsive Design to Your Application 3-19
Using Responsive JavaScript 3-19
The Responsive JavaScript Classes 3-20
Changing a Custom Element’s Attribute Based on Screen Size 3-21
Conditionally Loading Content Based on Screen Size 3-22
Creating Responsive Images 3-23
Using the Responsive Helper Classes 3-24
Creating Responsive CSS Images 3-24
Changing Default Font Size 3-25
Changing Default Font Size Across the Application 3-25
Changing Default Font Size Based on Device Type 3-26
Controlling the Size and Generation of the CSS 3-26
v
About Binding and Control Flow 6-5
Using oj-bind-text to Bind Text Nodes 6-6
Binding HTML attributes 6-7
Using oj-bind-if to Process Conditionals 6-8
Using oj-bind-for-each to Process Loop Instructions 6-9
Binding Style Properties 6-10
Binding Event Listeners to JET and HTML Elements 6-11
Binding Classes 6-13
Adding an Oracle JET Custom Element to Your Page 6-16
Adding Animation Effects 6-17
vi
Working with Progress Indicators 7-53
Working with Tags 7-54
Working with Toolbars 7-54
Working with Trains 7-55
Working with Forms 7-56
Working with Checkbox and Radio Sets 7-57
Working with Color Pickers 7-58
Working with oj-color-palette 7-59
Working with oj-color-spectrum 7-61
Working with Comboboxes 7-62
Understanding oj-combobox-one Search 7-66
Working with Form Controls 7-67
Working with Form Layout Features 7-68
Working with oj-form-layout 7-68
Working with Input Components 7-70
Working with Labels 7-72
Working with Select 7-73
Working with Sliders 7-78
About the oj-slider Component 7-78
Creating Sliders 7-79
Formatting Tips for oj-slider 7-80
Working with Switches 7-81
Working with Validation and User Assistance 7-82
Working with Layout and Navigation 7-83
Working with Accordions 7-83
Working with Collapsibles 7-84
Working with Dialogs 7-85
Working with Masonry Layouts 7-86
Configuring Masonry Layouts 7-87
Understanding the oj-masonry-layout Layout Process 7-89
oj-masonry-layout Size Style Classes 7-89
Working with Nav Lists 7-89
Understanding Data Requirements for Nav Lists 7-90
Working with Nav Lists and Knockout Templates 7-93
Working with offCanvasUtils 7-94
Configuring an Off-Canvas Partition 7-95
Working with Panels 7-95
Working with Popups 7-96
Working with oj-popup 7-97
Working with the Oracle JET Popup Framework 7-98
Working with Tab Bars 7-101
vii
Understanding Data Requirements for Tab Bars 7-105
Working with Visualizations 7-106
Choosing a Data Visualization Component for Your Application 7-106
Using Attribute Groups With Data Visualization Components 7-112
viii
Deleting Records 9-22
ix
Using Oracle JET Messaging 11-8
Notifying an Oracle JET Editable Component of Business Validation Errors 11-8
Using the messages-custom Attribute 11-9
Using the showMessages() Method on Editable Components 11-10
Understanding the oj-validation-group Component 11-10
Tracking Validity of a Group of Editable Components Using oj-validation-
group 11-11
Creating Page Level Messaging 11-13
Create Messages with oj-message 11-15
Configuring an Editable Component's oj-label Help Attribute 11-16
Configuring an Editable Component's help.instruction Attribute 11-18
Controlling the Display of Hints, Help, and Messages 11-20
x
14 Theming Applications
Typical Workflow for Theming an Oracle JET Application 14-1
CSS Files Included With Oracle JET 14-2
DOCTYPE Requirement 14-3
ThemeUtils 14-3
Understanding the Color Palette 14-3
Setting Text Direction 14-4
Best Practices for Using CSS and Themes 14-4
Customizing Themes Using the Tooling 14-6
Sass Files, Variables, and Tools 14-8
SCSS File Organization and Naming Convention 14-8
SCSS Variables 14-9
Using Variables to Control CSS Content 14-10
Understanding Right-to-Left Behavior 14-11
Understanding Oracle JET Theming For Compatibility 14-11
SCSS Tools 14-14
Working with Images 14-14
Image Considerations 14-15
Icon Fonts 14-15
Image Files 14-16
15 Securing Applications
Typical Workflow for Securing Oracle JET Applications 15-1
About Securing Oracle JET Applications 15-1
Oracle JET Components and Security 15-2
Oracle JET Security and Developer Responsibilities 15-2
Oracle JET Security Features 15-2
Oracle JET Secure Response Headers 15-4
Content Security Policy Headers 15-6
Using oj.OAuth in Your Oracle JET Application 15-9
Initializing oj.OAuth 15-9
Verifying oj.OAuth Initialization 15-10
Obtaining the OAuth Header 15-10
Using oj.OAuth with Oracle JET Common Model 15-10
Embedding oj.OAuth in Your Application's ViewModel 15-11
Adding oj.OAuth as a Plugin in Your ViewModel 15-12
Integrating oj.OAuth with Oracle Identity Management (iDM) Server 15-12
About Securing Hybrid Mobile Applications 15-13
Managing Authentication in JET Hybrid Mobile Apps 15-14
Managing App Configuration for JET Hybrid Mobile Apps 15-14
xi
Dealing With Cross-Origin Resource Sharing (CORS) 15-14
17 Optimizing Performance
Typical Workflow for Optimizing Performance of Oracle JET Applications 17-1
About Performance and Oracle JET Applications 17-1
Adding Performance Optimization to an Oracle JET Application 17-2
Configuring the Application for Oracle CDN Optimization 17-5
Understanding the Path Mapping Script File and Configuration Options 17-7
xii
A Troubleshooting
xiii
Preface
Preface
Developing Applications with Oracle JET describes how to build responsive web and
hybrid mobile applications using Oracle JET.
Topics:
• Audience
• Documentation Accessibility
• Related Resources
• Conventions
Audience
Developing Applications with Oracle JET is intended for intermediate to advanced
JavaScript developers who want to create pure client-side, responsive web or hybrid
mobile applications based on JavaScript, HTML5, and CSS3.
Documentation Accessibility
For information about Oracle's commitment to accessibility, visit the Oracle
Accessibility Program website at http://www.oracle.com/pls/topic/lookup?
ctx=acc&id=docacc.
Related Resources
For more information, see these Oracle resources:
• Oracle JET Web Site
• JavaScript API Reference for Oracle® JavaScript Extension Toolkit (Oracle JET)
• Oracle® JavaScript Extension Toolkit (JET) Keyboard and Touch Reference
• Oracle® JavaScript Extension Toolkit (JET) Styling Reference
xiv
Preface
Conventions
The following text conventions are used in this document:
Convention Meaning
boldface Boldface type indicates graphical user interface elements associated
with an action, or terms defined in text or the glossary.
italic Italic type indicates book titles, emphasis, or placeholder variables for
which you supply particular values.
monospace Monospace type indicates commands within a paragraph, URLs, code
in examples, text that appears on the screen, or text that you enter.
xv
What’s New in This Guide for Release 6.0.0
xvi
What’s New in This Guide for Release 6.0.0
xvii
1
About Oracle JavaScript Extension Toolkit
(JET)
Oracle JET is a collection of Oracle and open source JavaScript libraries engineered
to make it as simple and efficient as possible to build client-side web and hybrid mobile
applications based on JavaScript, HTML5, and CSS.
Oracle JET is designed to meet the following application needs:
• Add interactivity to an existing page.
• Create a new end-to-end client-side web application using JavaScript, HTML5,
CSS, and best practices for responsive design.
• Create a hybrid mobile application that looks and feels like a native iOS, Android
or Windows application.
Topics:
• The Oracle JET Architecture
• What's Included in Oracle JET
You can also view videos that provide an introduction to Oracle JET in the Oracle JET
Videos collection.
1-1
Chapter 1
The Oracle JET Architecture
To support the MVVM design, Oracle JET is built upon a modular framework that
includes a collection of third-party libraries and Oracle-provided files, scripts, and
libraries.
The Oracle JET Common Model and Collection Application Programming Interface
(API) implements the model layer. The API includes the following JavaScript objects:
• oj.Model: Represents a single record data from a data service such as a RESTful
web service
• oj.Collection: Represents a set of data records and is a list of oj.Model objects of
the same type
• oj.Events: Provides methods for event handling
1-2
Chapter 1
The Oracle JET Architecture
• Management of URL and browser history using Oracle JET oj.Router and oj-
module components
• Integrated authorization through OAuth 2.0 for data models retrieved from REST
Services
• Resource management provided by RequireJS
• API compatibility with Backbone.js Model, Collection, and Events classes, except
for Backbone.js Underscore methods.
• JavaScript logging
• Popup UI handling
To support hybrid mobile development, Oracle JET includes the following features:
• Native themes that you can use with Cordova to create hybrid mobile applications
that emulate the look and feel of native iOS, Android, and Windows mobile devices
• Tooling that enables you to scaffold and build Cordova-based hybrid applications
using Oracle JET directly from the command line
• Code samples, applications, and demos that you can use to create hybrid mobile
applications using Oracle JET best practices
1-3
Chapter 1
What's Included in Oracle JET
Oracle JET visual components include the following features and standards
compliance:
• Compliance with Oracle National Language Support (NLS) standards (i18n) for
numeric, currency, and date/time formatting
• Built-in theming supporting the Oracle Alta UI style specifications
• Support for Oracle software localization standards, l10n, including:
– Lazy loading of localized resource strings at run time
– Oracle translation services formats
– Bidirectional locales (left-to-right, right-to-left)
• Web Content Accessibility Guidelines (WCAG) 2.0. In addition, components
provide support for high contrast and keyboard-only input environments.
• Gesture functionality by touch, mouse, and pointer events where appropriate
• Support for Oracle test automation tooling
• Responsive layout framework
• Hammer.js
Oracle JET components use Hammer.js internally for gesture support. Do not add
to Oracle JET components or their associated DOM nodes.
• Oracle JET dnd-polyfill HTML5 drag and drop polyfill
1-4
Chapter 1
What's Included in Oracle JET
• proj4js library
• webcomponentsjs polyfill
1-5
2
Getting Started with Oracle JET Application
Development
Use Oracle JET to create web and hybrid mobile applications. The toolkit provides
customizable Alta themes for desktop platforms and mobile devices. The toolkit also
includes Oracle JET components, Starter Templates, UI design patterns, tooling, and
a mechanism to load content from the Oracle Content Delivery Network (CDN).
Topics
• Typical Workflow for Getting Started with Oracle JET Application Development
• Prerequisites for Developing Applications with Oracle JET
• Getting Started with Oracle JET Web Application Development
• Get Started with Oracle JET Hybrid Mobile Application Development
• Working with the Oracle JET Starter Templates
• Optimizing Application Startup Using Oracle CDN and Oracle JET Libraries
2-1
Chapter 2
Prerequisites for Developing Applications with Oracle JET
Topics:
• Prerequisite Knowledge for Working with Oracle JET
• Choose a Development Environment
• Install the Prerequisite Packages
• (Optional) Develop Oracle JET Applications with TypeScript
2-2
Chapter 2
Prerequisites for Developing Applications with Oracle JET
If you will be using Oracle JET tooling to create web or hybrid mobile applications, you
should also be familiar with the following technologies.
2-3
Chapter 2
Prerequisites for Developing Applications with Oracle JET
editor for editing your application. For details about developing hybrid applications
using Oracle JET tooling, see Get Started with Oracle JET Hybrid Mobile Application
Development.
Note:
If you already have some or all of the prerequisite packages installed on your
development platform, check that you are using the minimum versions
supported by Oracle JET tooling and upgrade as needed. For the list of
minimum supported versions, see Oracle JET Support.
Install Node.js
Install Node.js on your development machine.
From a web browser, download and install one of the installers appropriate for your
OS from the Node.js download page. Oracle JET recommends that you install the
latest LTS version. Node.js is pre-installed on macOS, but is likely an old version, so
upgrade to the latest LTS version if necessary.
After you complete installation and setup, you can enter npm commands from a
command prompt to verify that your installation succeeded. For example, to configure
a proxy server, use npm config.
npm config set proxy http-proxy-server-URL:proxy-port
npm config set https-proxy https-proxy-server-URL:proxy-port
2-4
Chapter 2
Prerequisites for Developing Applications with Oracle JET
Note:
It may not be obvious that the installation succeeded. Enter npm list –g
@oracle/ojet-cli to verify that the command succeeded. If the package
is not listed, scroll through the install command output to locate the
source of the failure.
– If you receive an error related to a network failure, verify that you
have set up your proxy correctly if needed.
– If you receive an error that your version of npm is outdated, type the
following to update the version: [sudo] npm install -g npm.
Note:
For details about TypeScript support for Oracle JET, see TypeScript API
Reference for Oracle® JavaScript Extension Toolkit (Oracle JET).
To install the Oracle JET TypeScript type definition package, from the application root,
use nmp install.
npm install @type/oracle__oraclejet
The command specifies the oraclejet package scoped as oracle__ (with double
underscores).
To verify the installation, use nmp list.
npm list @type/oracle__oraclejet
Oracle JET tooling does not currently support configuring the application for
TypeScript development. To configure the application, you must set up your project’s
tsconfig.json file as described in the API documentation. The tsconfig file that you
create will guide the compiler to generate JavaScript files. For example, you may
specify compiling ES6 application source files into ES5 JavaScript code and AMD
format modules.
With a properly configured application, you can import TypeScript definition modules
for Oracle JET custom elements, as well as non-element classes, namespaces, and
interfaces. For example, in this Oracle JET application, the oj-chart import statement
supports typechecking against the element’s TypeScript API.
2-5
Chapter 2
Prerequisites for Developing Applications with Oracle JET
And, your editor can leverage the definition files for imported modules to display type
information.
2-6
Chapter 2
Getting Started with Oracle JET Web Application Development
Note:
For additional information about creating hybrid mobile applications, see
Get Started with Oracle JET Hybrid Mobile Application Development.
• Download Oracle JET with a Starter Template ready to run and display in your
local browser.
With this method, you can get up and running quickly, but it will be up to you to
package and deploy your application, using whatever method is appropriate for
your use case.
• Download the Oracle JET zip distribution which contains the Oracle JET and third-
party libraries, CSS and SCSS files, and a RequireJS bootstrap file.
With this method, you must manually create the index.html or main application file
and pull in the appropriate libraries and CSS as needed. This method is best if you
want to use your own application design pattern instead of the templates included
with the tooling or Oracle JET Starter collection.
• Add Oracle JET to an existing JavaScript application.
With this method, you overlay the pieces of Oracle JET that you need to your
existing JavaScript application and modify your main application file and scripts as
needed.
Whichever method you use to get started creating your web application, you must not
use more than one version of Oracle JET to add components to the same HTML
document of your web application. Oracle JET does not support running multiple
versions of Oracle JET components in the same web page.
Topics:
• Create a Web Application Using the Oracle JET Command-Line Interface
• Create a Web Application Using the oraclejet.zip Download
• Create a Web Application Using the Oracle JET Starter Templates
2-7
Chapter 2
Getting Started with Oracle JET Web Application Development
Topics
• Scaffold a Web Application with the Oracle JET CLI
• (Optional) Build a Web Application with the Oracle JET CLI
• Serve a Web Application with the Oracle JET CLI
Note:
You can also use the tooling to create hybrid mobile applications. For
additional information, see Get Started with Oracle JET Hybrid Mobile
Application Development
2-8
Chapter 2
Getting Started with Oracle JET Web Application Development
Note:
Do not confuse the basic template which contains no content with the Oracle
JET QuickStart Basic application available in NetBeans 8.2 examples. The
NetBeans application uses the navdrawer:web layout shown above and
contains the same sample content.
If you want your web application to look more like a mobile application, you can
scaffold your web application with a hybrid mobile version of the basic, navbar, or
navdrawer template: basic:hybrid, navbar:hybrid, or navdrawer:hybrid.
1. At a command prompt, enter ojet create with optional arguments to create the
Oracle JET application and scaffolding.
2-9
Chapter 2
Getting Started with Oracle JET Web Application Development
The following table describes the available options and provides examples for their
use.
Tip:
You can also enter ojet help at a terminal prompt to get additional help
with the Oracle JET CLI.
Option Description
directory Application location. If not specified, the application is
created in the current directory. The directory will be
created during scaffolding if it doesn’t already exist.
template Template to use for the application. Specify one of the
following:
• template-name
Predefined template. You can enter blank, basic,
navbar or navdrawer . Defaults to blank if not
specified.
Optionally, add :web or :hybrid to the template
name to specify web or hybrid mobile styling. By
default template will use the web styling, but you
can add :hybrid to change the styling to hybrid
mobile, typically basic:hybrid, navbar:hybrid or
navdrawer:hybrid.
• template-URL
URL to zip file containing the name of a zipped
application: http://path-to-app/app-name.zip.
• template-file
Path to zip file on your local file system containing
the name of a zipped application: "path-to-app/
app-name.zip". For example:
--template="C:\Users\SomeUser\app.zip"
--template="/home/users/SomeUser/app.zip"
--template="~/projects/app.zip"
help Displays a man page for the ojet create command,
including usage and options: ojet create --help.
For example, the following command will create a web application in the web-app-
navbar directory using the web version of the navbar template:
To scaffold the web application with the hybrid mobile version of the navbar
template, enter the following command:
ojet create web-app-navbar --template=navbar:hybrid
The scaffolding will take some time to complete. When successful, the terminal will
display:
2-10
Chapter 2
Getting Started with Oracle JET Web Application Development
Oracle JET: Your app is ready! Change to your new app directory web-app-navbar
and try ojet build and serve...
The new application will have a directory structure similar to the one shown in the
following image.
The application folders contain the application and configuration files that you will
modify as needed for your own application.
2-11
Chapter 2
Getting Started with Oracle JET Web Application Development
Tip:
If you selected the blank template during scaffolding, you can still follow
the same process to add cookbook samples or other content to your
application. However, it will be up to you to create the appropriate view
templates or viewModel scripts.
After scaffolding, you can perform the following optional tasks to customize your
application:
• Modify the Web Application’s File Structure
• Add Hybrid Mobile Features to Web Applications
2-12
Chapter 2
Getting Started with Oracle JET Web Application Development
"web": "web",
"hybrid": "hybrid",
"themes": "themes"
}
},
"generatorVersion": "6.0.0"
}
<!-- This is the main css file for the default Alta theme -->
<!-- injector:theme -->
<link rel="stylesheet" href="app-css/libs/oj/v6.0.0/alta/oj-alta-min.css"
type="text/css"/>
<!-- endinjector -->
<!-- This contains icon fonts used by the starter template -->
<link rel="stylesheet" href="app-css/demo-alta-site-min.css" type="text/
css"/>
<!-- This is where you would add any app specific styling -->
<link rel="stylesheet" href="app-css/override.css" type="text/css"/>
To use the new paths, in your application’s top level directory, build your application as
described in Build a Web Application with the Oracle JET CLI.
2-13
Chapter 2
Getting Started with Oracle JET Web Application Development
1. At a terminal prompt, in your application’s top level directory, enter the following
command to add hybrid mobile features to your web application:
ojet add hybrid [--platforms=android,ios,windows]
[--appid=application-id]
[--appname=application-name]
[--help]
You should specify at least one platform for the platforms value. Add additional
platforms as needed, separated by commas. If you don’t specify a platform, the
command will attempt to locate available platforms and prompt for your selection.
--platforms=android
--platforms=ios,android
The platforms, appid, appname, and help options use the same values as the ojet
create --hybrid command described in Scaffold a Hybrid Mobile Application with
the Oracle JET CLI
When you run the command, Oracle JET tooling creates the Cordova project and
two new empty source directories that you can use for platform specific content:
src-hybrid and src-web.
Note:
The file locations will vary for this step and the remaining steps in this
procedure if you modified your directory structure as described in Modify
the Web Application’s File Structure or Modify the Hybrid Mobile
Application’s File Structure.
2. To make changes to content that apply to both web and hybrid platforms, edit
content in src.
3. To make changes to content that apply only to the web or hybrid platform, add the
new content to src-web or src-hybrid as needed.
When you build your application, the content in the platform specific file takes
precedence over content in src. For example, if you create a new index.html file to
src-hybrid, content in that file will take precedence when you build the application
as a hybrid mobile application.
Change to the application’s root directory and use the ojet build command to build
your application.
ojet build [--theme=themename[:android|ios|web|windows] --themes=theme1,theme2,...
--sass]
The following table describes the available options and provides examples for their
use.
2-14
Chapter 2
Getting Started with Oracle JET Web Application Development
Tip:
You can also enter ojet help at a terminal prompt to get help for specific
Oracle JET CLI options.
Option Description
theme Theme to use for the application. The theme defaults to
alta:web, but you can specify a hybrid mobile theme
(alta:android, alta:ios, alta:windows) if you want
your application to have the look and feel of a mobile
application.
You can also enter a different themename with optional
platform for a custom theme as described in
Customizing Themes Using the Tooling.
themes Themes to include in the application, separated by
commas.
If you don’t specify the --theme flag as described above,
Oracle JET will use the first element that you specify in
--themes as the default theme. .
sass Manages Sass compilation. If you add Sass and specify
the --theme or --themes option, Sass compilation
occurs by default and you can use --sass=false or --
no-sass to turn it off.
If you add Sass and do not specify a theme option, Sass
compilation will not occur by default, and you must
specify --sass=true or --sass to turn it on. For details
about theming applications, see Typical Workflow for
Theming an Oracle JET Application.
The command will take some time to complete. If it’s successful, you’ll see the
following message:
Done.
The command will also create a web folder in your application’s root to contain the
built content.
Note:
You can also use the ojet build command with the --release option to build
a release-ready version of your application. For information, see Packaging
and Deploying Web Applications.
The Oracle JET tooling defines default build and serve behavior in node_modules/
@oracle/oraclejet-tooling/lib/defaultconfig.js. The first part of the file contains the
2-15
Chapter 2
Getting Started with Oracle JET Web Application Development
default build configuration, including entries for application paths and other options
used during the build process. To modify defaults or add your own, edit oraclejet-
build.js and remove comments from the options that you want to configure. The file
contains detailed instructions for each configurable option.
Oracle JET will merge the options with the default configuration during the build.
However, be aware that fileList options will completely replace the corresponding
option in the default configuration and will not be merged.
To customize build behavior:
1. Open scripts/config/oraclejet-build.js for editing in your favorite text editor.
2. Identify the entry that you want to change and remove the comments (//) preceding
each line.
For example, to add a custom library to the build’s staging directory, remove the
comments from the copyCustomLibsToStaging entry in oraclejet-build.js as shown
below:
/**
* # copyCustomLibsToStaging
* This task copies any custom libraries that are not provided by JET to staging
directory.
* This task supports a single option: fileList. The fileList option defines an
array of file objects.
* Each file object contains the following properties:
* cwd, current working directory
* dest, destination path
* src, array of source file patterns
* rename, function to return the full path of desired destination
* If a fileList value is specified, it completely replaces the default fileList
value defined by JET
* Example: {cwd: 'app', src: ['**', '!test.js'], dest: 'staging', rename:
function (dest, file) {return renamed path}}
*/
copyCustomLibsToStaging: {
fileList: [
{
cwd:'node_modules/oraclejet/',
src: ['*'],
dest: 'web/js/libs/oraclejet'
}
]
}
copyCustomLibsToStaging: {
fileList: [
{
cwd:'node_modules/my_custom_module/',
src: ['*'],
dest: 'web/js/libs/my_custom_module'
}
]
}
4. Repeat the previous step for each option that you want to modify.
2-16
Chapter 2
Getting Started with Oracle JET Web Application Development
Test your changes by running Build a Web Application with the Oracle JET CLI with
appropriate options.
The following table describes the available options and provides examples for their
use.
Tip:
You can also enter ojet help at a terminal prompt to get help specific to
the Oracle JET tooling commands and ojet serve --help to get
additional help with serve options.
Option Description
server-port Server port number. If not specified, defaults to 8000.
livereload-port Live reload port number. If not specified, defaults to
35729.
livereload Enable the live reload feature. Live reload is enabled by
default (--livereload=true).
Use --livereload=false or --no-livereload to disable
the live reload feature.
Disabling live reload can be helpful if you’re working in
NetBeans or other IDE and want to use that IDE’s
mechanism for loading updated applications.
sass Manages Sass compilation. If you add Sass and specify
the --theme or --themes option, Sass compilation
occurs by default and you can use --sass=false or --
no-sass to turn it off.
If you add Sass and do not specify a theme option, Sass
compilation will not occur by default, and you must
specify --sass=true or --sass to turn it on.
2-17
Chapter 2
Getting Started with Oracle JET Web Application Development
Option Description
build Build the app before you serve it. By default, an app is
built before you serve it (--build=true).
Use --build=false or --no-build to suppress the build
if you’ve already built the application and just want to
serve it.
theme Theme to use for the application. The theme defaults to
alta:web, but you can specify a hybrid mobile theme
(alta:android, alta:ios, alta:windows) if you want
your application to have the look and feel of a mobile
application.
You can also enter a different themename with optional
platform for a custom theme as described in
Customizing Themes Using the Tooling.
themes Themes to use for the application, separated by
commas.
If you don’t specify the --theme flag as described above,
Oracle JET will use the first element that you specify in
--themes as the default theme. Otherwise Oracle JET
will serve the application with the theme specified in --
theme.
server-only Serves the application, as if to a browser, but does not
launch a browser. Use this option in cloud-based
development environments so that you can attach your
browser to the app served by the development machine.
For example, the following command will launch your application in the default
web browser with live reload enabled: ojet serve.
2. Make any desired code change in the src folder, and the browser will update
automatically to reflect the change unless you set the --no-livereload flag.
While the application is running, the terminal window remains open, and the watch
task waits for any changes to the application. For example, if you change the
content in src/js/views/dashboard.html , the watch task will reflect the change in
the terminal as shown below on a Windows desktop.
Starting watcher...
Listening on port 35729...
Server ready: http://localhost:8000
Watching files....
Watcher: sass is ready...
Watcher: sourceFiles is ready...
Watcher: themes is ready...
Changed: c:\web-app-navbar\src\js\views\dashboard.html
Page reloaded resume watching...
3. To terminate the process, close the application and press Ctrl+C in the command
window.
You may need to enter Ctrl+C a few times before the process terminates.
2-18
Chapter 2
Getting Started with Oracle JET Web Application Development
Note:
You can also use the ojet serve command with the --release option to serve
a release-ready version of your application. For information, see Packaging
and Deploying Web Applications.
// sass: {
// files: [],
// commands: ['compileSass']
// },
// themes: {
// files: [],
// options: {
// livereload: true
// },
// commands: ['copyThemes']
// },
},
3. Edit the options as desired and save the file.
2-19
Chapter 2
Getting Started with Oracle JET Web Application Development
For command options, add the commands option and one or more commands,
each surrounded by single quotes and separated by commas. For example, the
following code snippet adds node --version and npm --version to the commands
option in the sourceFiles sub task:
watch: {
sourceFiles:
{
// files: [],
// options: {
// livereload: true
// },
commands: ['node --version', 'npm --version']
},
... contents omitted
},
Test your changes by running Serve a Web Application with the Oracle JET CLI with
appropriate options. When you change one of the source files, the terminal output will
display the output of the node --version and npm --version commands and reload the
page.
Therefore, customization of the build and serve processes that you enforce on Oracle
JET tooling workflow requires that you know the following details before you can write
a customization script.
• The type of application that you created: either a web application or a hybrid
mobile application.
• The Oracle JET build or serve mode that you want to customize:
– Debug — The default mode when you build or serve your application, which
produces the source code in the built application.
– Release — The mode when you build the application with the --release
option, which produces minified and bundled code in a release-ready
application.
• The appropriate hook point to trigger the customization.
• The location of the default hook script template to customize.
2-20
Chapter 2
Getting Started with Oracle JET Web Application Development
Note:
By default, the hooks system locates the scripts in the hooks subfolder using
a generated configuration file that specifies the script paths. If you change
the default location of the hooks subfolder, you must edit the configuration file
to specify the new directory path. For details, see Create a Web Application
Build or Serve Hook Script for Web Applications.
Create a Web Application Build or Serve Hook Script for Web Applications
You can create a hook point script that defines a new build or serve process step by
editing the script template associated with a specific hook point in the tooling build and
serve workflow.
The Oracle JET hooks system defines various script trigger points, also called hook
points, that allow you to customize the build and serve workflow across the various
build and serve modes. Customization relies on script files and the script code that you
want to trigger for a particular hook point. Note that the generated script templates that
you modify with your script code are named for their corresponding hook point.
To customize the workflow for the build or serve processes, you edit the generated
script template file named for a specific hook point. For example, to trigger a script at
the start of the tooling's build process, you would edit the before_build.js script named
for the hook point triggered before the build begins. That hook point is named
before_build.
The following example illustrates a build customization using the after_build hook.
This hook script is an example of adding a customize task after the build finishes.
'use strict’;
const fs = require('fs');
const archiver = require('archiver');
output.on('close', () => {
console.log('Files were successfully archived.');
resolve();
});
archive.pipe(output);
archive.directory('web', false);
archive.finalize();
2-21
Chapter 2
Getting Started with Oracle JET Web Application Development
});
};
In this example, assume the script templates reside in the default folder generated
when you created the application. The goal is to package the application into a ZIP file.
Because packaging occurs after the application build process completes, this script is
triggered for the after_build hook point. For this hook point, the modified script
template after_build.js will contain the script code to zip the application, and because
the .js file resides in the default location, no hooks system configuration changes are
required.
When the tooling reaches the hook point, it executes the corresponding script which it
locates using the configuration files in the /scripts/config application subfolders:
• <myApp_Home>/scripts/config/oraclejet-build.js - Specifies the location and hook
type of build process scripts.
• <myApp_Home>/scripts/config/oraclejet-serve.js - Specifies the location and hook
type of serve process scripts.
Note:
If your development effort requires you to relocate hook scripts to a common
location, for example to support team development, you can update the path
attributes in the hook system configuration file to specify the new target path
for the scripts. When you edit the configuration file do not change the type
attributes. The hook system relies on type attribute to execute the script for a
specific build/serve hook point.
The following table identifies the hook points and the workflow customizations they
support in the Oracle JET build and serve processes.
2-22
Chapter 2
Getting Started with Oracle JET Web Application Development
Tip:
OJET tooling reports when hook points are executed in the message log for
the build and serve process. You can examine the log in the console to
understand the tooling workflow and determine exactly when the tooling
triggers a hook point script.
Topics
• Download the Oracle JET Zip File
• Include References to CSS Files in Your Oracle JET Application
• Use RequireJS to Manage Library, Link, and Script References
2-23
Chapter 2
Getting Started with Oracle JET Web Application Development
If needed, download Oracle JET as described in Download the Oracle JET Zip File.
1. Create a new index.html file in the project root.
2. In the application’s main page, index.html, add the HTML link element and point it
to the CSS theme that you will use.
For example, to use the Oracle JET Alta web theme:
<!-- Oracle JET CSS files -->
<link rel="stylesheet" href="css/libs/oj/version/alta/oj-alta-min.css"
type="text/css"/>
For additional information about Oracle JET and the themes available for your use,
see Theming Applications.
3. If you will be using the CSS included with Oracle JET, add the following line to the
top of your application’s main HTML page:
<!DOCTYPE html>
This line is needed for the Oracle JET CSS to function as expected.
2-24
Chapter 2
Getting Started with Oracle JET Web Application Development
Topics:
• Downloading Oracle JET with a Starter Template
• Loading the Oracle JET Starter Templates
Tip:
If you use the Oracle JET CLI to scaffold a web application with a navdrawer
template as described in Scaffold a Web Application with the Oracle JET
CLI, then you already have a Starter Template and can skip this step.
Also, if you use NetBeans IDE version 8.2, it includes the Oracle JET
QuickStart Basic project which contains the same content as the Web Nav
Drawer Starter Template.
You can also load the Oracle JET Starter Templates in the NetBeans IDE or the
development environment of your choice.
Topics:
• Create an Application in the NetBeans IDE with a Site Template
• Run the Oracle JET Starter Templates in NetBeans
2-25
Chapter 2
Getting Started with Oracle JET Web Application Development
To create an Oracle JET application in the NetBeans IDE using a site template:
1. If needed, download and install NetBeans. Select a download bundle that includes
support for HTML5. For additional detail, see https://netbeans.org/downloads/
index.html
2. In the NetBeans IDE, click New Project.
3. In the New Project dialog, create a new HTML5 application.
a. In the Categories area, click HTML5/JavaScript.
b. In the Projects area, click HTML5/JS Application.
In this example, the HTML5 & PHP download bundle is used for the initial
NetBeans 8.2 download, and the categories are limited to HTML5 and PHP. If
you downloaded a different NetBeans download bundle or are using a
different version, the dialog may include additional or different categories.
c. Click Next.
4. In the New HTML5/JS Application dialog, configure the project name and project
location.
a. In the Project Name field, enter a name for your project.
b. In the Project Location field, enter a location to store the project files.
2-26
Chapter 2
Getting Started with Oracle JET Web Application Development
In this example, the project is named OracleJETSample, and the project files will
be stored in the C:\NetBeansProject directory.
c. Click Next.
5. In the New HTML5/JS Application dialog, specify the name of the site template.
a. Choose Select Template.
b. In the Template field, choose Browse and select the location containing the
download bundle.
c. Click Next.
2-27
Chapter 2
Getting Started with Oracle JET Web Application Development
6. Optionally, in the New HTML5 Application Dialog, remove the check mark from
any tools your application won’t need. By default, all tools are checked.
Tip:
If you decide you want to use one of the tools after you create your
application, you can add it in the NetBeans IDE. Choose Tools, then
Options, and HTML/JS. Select the tab containing the tool you wish to
add, and select the option to install the package.
Note:
To take full advantage of the NetBeans integrated tools, the NetBeans team
recommends that you install the NetBeans Connector extension for the
Chrome browser. For additional info, see https://netbeans.org/kb/docs/
webclient/html5-gettingstarted.html.
2-28
Chapter 2
Getting Started with Oracle JET Web Application Development
The graphic below shows the Oracle JET Web Nav Drawer Starter Template displayed
in the Chrome browser at run time. In this example, the NetBeans Connector is
enabled.
2-29
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
The css folder contains the themes included with Oracle JET, and the scss folder
contains the SASS source files for the Oracle JET themes. For additional
information about Oracle JET theming, see Theming Applications.
The js folder contains the Oracle JET libraries and third party libraries, and the
buildnum and revnum files identify the Oracle JET build number and version. For
additional information about the libraries included in Oracle JET, see What's
Included in Oracle JET.
2. If you want to use one of the themes included with Oracle JET, add the
appropriate link to the CSS. For details, see Include References to CSS Files in
Your Oracle JET Application.
3. If you want to use the Oracle JET libraries, you can use RequireJS to manage the
Oracle JET library, link, and script references.
• For information about configuring your application to use RequireJS, see Use
RequireJS to Manage Library, Link, and Script References.
Topics
• Install the Mobile Tooling
2-30
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
Tip:
If you’re strictly interested in developing web applications that run in desktop
and mobile browsers, you don’t need to install the mobile tooling. For
information on developing web applications, see Getting Started with Oracle
JET Web Application Development
2-31
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
When you have completed development and want to sign and package the Android
app for distribution, see Packaging a Hybrid Mobile App on Android.
2-32
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
If this is the first time that you use Android Studio, click the Start a new Android
Studio project link on the Welcome screen of Android Studio. Once you create the
project, you’ll have access to the aforementioned menu options.
Make sure that the update site for the emulator accelerator that you want to download
is selected in the SDK Update Sites tab as shown. Once downloaded, execute the
installer. See https://developer.android.com/studio/run/emulator-
acceleration.html#accel-vm.
2-33
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
4. In the System Image screen, click Download for one of the recommended system
images. Agree to the terms to complete the download.
5. After the download completes, select the system image from the list and click
Next.
6. On the next screen, leave all the configuration settings unchanged and click
Finish.
7. In the Your Virtual Devices screen, select the device you just created and click
Launch this AVD in the emulator.
Set Up Your Android Device to Install an App from Your Development Machine
You can install your app directly from your development machine to your Android
device by configuring the Android device and connecting it to your development
machine using a USB cable.
To set up your Android device:
1. Connect your device to your development machine with a USB cable. If you are
developing on Windows, you might need to install the appropriate USB driver for
your device. For help installing drivers, see the OEM USB Drivers document.
2. Enable USB debugging on your device by going to Settings > Developer
options.
Note:
On Android 4.2 and newer, Developer options is hidden by default. To
make it available, go to Settings > About phone and tap Build number
seven times. Return to the previous screen to find Developer options.
If you are a Windows user, you locate your ~/.gradle directory by navigating to
%USERPROFILE%\.gradle. If you are a macOS or Linux user, navigate to ~/.grade.
2-34
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
The process to set the environment variables depends upon your development
environment:
• Windows
Set the variables permanently in Control Panel > System and Security >
System > Advanced System Settings > Environment Variables.
For example, to include the Android SDK's tools and platform-tools directories in
your PATH environment variable, modify the entry that the Environment Variables
dialog displays to include entries similar to the following:
Path=C:\Users\JET\AppData\Local\Android\sdk\tools;C:\Users\JET\AppData\Local
\Android\sdk\platform-tools;C:\Users\JET\AppData\Roaming\npm
To set the JAVA_HOME and ANDROID_HOME environment variables, click New in the
Environment Variables dialog and set the values of the variables to the location of
the JDK and Android SDK locations, as shown in the following image.
2-35
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
If you have opened a command window, close it and open a new one for the
change to take effect.
• Macintosh and Linux
On Mac and Linux systems, the settings depend upon your default shell. For
example, on a Mac system using the Bash shell, add the following lines in
~/.bash_profile:
export JAVA_HOME=/Development/jdkInstallDir/
export ANDROID_HOME=/Development/android-sdk/
To update your PATH environment variable, add a line resembling the following
where you substitute the paths with your local Android SDK installation's location:
export PATH=${PATH}:/Development/android-sdk/platform-tools:/Development/android-
sdk/tools
Reload your terminal to see this change reflected or run the following command:
$ source ~/.bash_profile
On a Linux system using C Shell, you would use the following format:
2-36
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
setenv JAVA_HOME=/Development/jdkInstallDir/
setenv ANDROID_HOME=/Development/android-sdk/
Reload your terminal to see this change reflected or run the following command:
~/.cshrc
These steps are sufficient for developing iOS applications and testing on iOS
simulators. If, however, you want to use an actual iOS device for testing or to publish
your application to the App Store, you must join the Apple iOS Developer program and
create an appropriate provisioning profile. For additional information, see Manage
profiles in Apple’s documentation. For details about using the Oracle JET tooling to
package and deploy your hybrid mobile application, see Packaging a Hybrid Mobile
App on iOS.
In addition you must install a tool that is used by the Oracle JET CLI to deploy apps to
iOS devices by executing the following command in a terminal window:
sudo npm install -g ios-deploy --unsafe-perm=true --allow-root
Once you have installed the iOS development tools, you can create a hybrid mobile
application, as described in Create a Hybrid Mobile Application.
Note:
If your device is owned by an organization, some options might be disabled
by your organization. Contact your system administrator to ensure you have
the options enabled that you require.
2-37
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
For information about Developer mode, and other settings that configure development
on Windows 10 computers, see https://docs.microsoft.com/en-us/windows/uwp/get-
started/enable-your-device-for-development.
Run Visual Studio after you install it to execute any required setup tasks. This prompts
you to enable Developer mode, if you have not already enabled this mode on your
computer.
2-38
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
b. Use the MakeCert tool to create certificate files with the specified parameters.
For example:
makecert.exe -sv C:\aDirectory\doc.pvk -n "CN=Doc
Example,OU=JET,O=Oracle,C=US" -r -h 0 -eku
"1.3.6.1.5.5.7.3.3,1.3.6.1.4.1.311.10.3.13" C:\aDirectory
\doc.cer
2-39
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
c. Package the resulting certificate files into a PFX file. For example:
pvk2pfx.exe -pvk C:\aDirectory\doc.pvk -spc C:\aDirectory
\doc.cer -pfx C:\aDirectory\doc.pfx
3. To do this, double-click the PFX file to invoke the Certificate Import Wizard,
choose the appropriate store location (for example, Current User) and click Next
to proceed to the page where you choose the Certificate Store, as shown by the
following figure where the Trusted People certificate store is selected.
Note:
The file created in our previous example is not password-protected, so
leave the Password input field blank in the Private key protection page of
the wizard.
4. Click Finish to complete the installation of the certificate in the certificate store and
repeat the process to install the PFX file in the remaining certificate stores.
2-40
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
2. (Optional) Build a Hybrid Mobile Application with the Oracle JET CLI
3. Serve a Hybrid Mobile Application with the Oracle JET CLI
4. Review Your Application Settings in the config.xml File
5. Change the Splash Screen and App Launcher Icon
Note:
You can add hybrid platforms (Android, iOS, and Windows) to an existing
web application. See Add Hybrid Mobile Features to Web Applications.
2-41
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
The following table describes the available options and provides examples for their
use.
Tip:
You can also enter ojet help create at a terminal prompt to get help with
the Oracle JET CLI create arguments.
Option Description
directory Application location. If not specified, the application is created in the current
directory.
appid Application ID entered in reverse domain style: com.mydomain.myappname.
The appid is equivalent to Package Name on Android and Bundle ID on
iOS.
If not specified, the appid defaults to org.oraclejet.directory, using the
directory you specified in the scaffolding command.
For example, if you specified MyApp for the directory, the default appid will
be org.oraclejet.MyApp.
appname Application name displayed on device. To include spaces in the title, use
quotes: --appname="My Sample Application". If not specified, the appid
defaults to the directory you specified in the scaffolding command.
template Template to use for the application. Specify one of the following:
• template-name
Predefined template. You can specify basic, blank, navbar , or
navdrawer. If you don’t specify a template, your application will be
configured with the blank template.
• template-URL
URL to zip file containing the name of the zipped application: http://
path-to-app/app-name.zip.
• template-file
Path to zip file on your local file system containing the name of a
zipped application: "path-to-app/app-name.zip". For example:
--template="C:\Users\SomeUser\app.zip"
--template="/home/users/SomeUser/app.zip"
--template="~/projects/app.zip"
platforms Comma-separated list of platform names. You can specify one or more of
the following: ios, android, or windows without spaces. For example:
ios,android.
If you don’t specify a platform, the command will prompt you for your choice
of platform after verifying that the necessary prerequisites are available.
platform Platform name. You can specify one of the following: ios, android, or
windows.
If you don’t specify a platform, the command will prompt you for your choice
of platform after verifying that the necessary prerequisites are available.
For example, the following command creates a hybrid mobile application named
Sample NavBar in the MySampleApp directory using the navbar template and targeted
for Android devices:
ojet create MySampleApp --hybrid --appname="Sample NavBar" --template=navbar --
platform=android
2-42
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
The scaffolding will take some time to complete. When successful, the terminal will
display: Your app is ready! Change to your new app directory
MySampleApp...
The new application will have a directory structure similar to the one shown in the
following image.
The application folders contain the application and configuration files that you will
modify as needed for your own application. See Hybrid Mobile Directory Structure.
2-43
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
Commit the config.xml file to your source control system, if you use one.
For more information about the config.xml file, see Review Your
Application Settings in the config.xml File.
scripts Contains the oraclejet-build.js and oraclejet-serve.js files that you
can modify to customize your application’s build and serve steps. See
Customize the Web Application’s Build Steps and Customize the Web
Application’s Serve Steps.
Also contains template hook scripts that you can modify to define new build
and serve steps for your application. See Customize the Hybrid Mobile
Application Tooling Workflow Using Hook Points.
node_modules Contains the Node.js modules used by the tooling.
src Site root for your application. Contains the application files that you can
modify as needed for your own application and should be committed to
source control.
The content will vary, depending upon your choice of template. Each
template, even the blank one, will contain an index.html file and a
main.js RequireJS bootstrap file.
Other templates may contain view templates and viewModel scripts pre-
populated with content. For example, if you specified the navbar template
during creation, the js/views and js/viewModels folders will contain the
templates and scripts for a hybrid mobile application that uses a nav bar for
navigation.
.gitignore Defines rules for application folders to ignore when using a GIT repository
to check in application source. Users who do not use a GIT repository can
use ojet strip to avoid checking in content that Oracle JET always
regenerates. Note this file must not be deleted since the ojet strip
command depends on it.
Gruntfile.js Task configuration file for Grunt based tasks. This file is not used by the
Oracle JET command-line interface (CLI) and the direct use of Grunt has
been deprecated since Oracle JET release 5.0.0. The tooling workflow now
relies on native build and serve scripts and tasks no longer require Grunt to
run.
oraclejetconfig.json Contains the default source and staging file paths that you can modify if
you need to change your application's file structure.
package.json Defines npm dependencies and project metadata.
2-44
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
<!-- This is the main css file for the default Alta theme -->
<!-- injector:theme -->
<link rel="stylesheet" href="app-css/libs/oj/v6.0.0/alta/oj-alta-min.css"
type="text/css"/>
<!-- endinjector -->
<!-- This contains icon fonts used by the starter template -->
<link rel="stylesheet" href="app-css/demo-alta-site-min.css" type="text/
css"/>
2-45
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
<!-- This is where you would add any app specific styling -->
<link rel="stylesheet" href="app-css/override.css" type="text/css"/>
To use the new paths, in your application’s top level directory, build your application as
described in Build a Hybrid Mobile Application with the Oracle JET CLI.
The following table describes the commonly-used options and provides examples for
their use.
Tip:
You can also enter ojet help at a terminal prompt to get help for specific
Oracle JET CLI options.
Option Description
[android|ios|windows] Desired platform. Enter android, ios, or windows.
build-config Path to buildConfig.json. The buildConfig.json file contains
details that Cordova can use to sign the application.
You do not need this file when building a debug version of your
app for Android or Windows, or if you are building your app for
deployment to an iOS simulator. However, you must configure one
for testing on an iOS device and when you’re ready to release
your Android, iOS, or Windows application. For additional
information, see Packaging and Publishing Hybrid Mobile
Applications.
destination Required for iOS applications. Specify one of the following:
• emulator: builds your application for deployment to the iOS
Simulator.
• device: builds your application for deployment to an iOS
device. Be aware, though, if you want to deploy your
application to an iOS device, you must take additional steps
as described in Packaging a Hybrid Mobile App on iOS.
Typically, you can develop and test your iOS application in
the simulator or web browser until you’re ready to build your
release.
device Equivalent to destination=device.
emulator Equivalent to destination=emulator.
2-46
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
Option Description
theme Theme to use for the application. The theme defaults to
alta:platform, but you can specify an alternate theme, such as a
custom theme as described in Customizing Themes Using the
Tooling.
themes Themes to include in the application, separated by commas
without spaces.
If you don’t specify the --theme flag as described above, Oracle
JET will use the first element that you specify in --themes as the
default theme.
sass Manages Sass compilation. If you add Sass and specify the --
theme or --themes option, Sass compilation occurs by default and
you can use --sass=false or --no-sass to turn it off. If you add
Sass and do not specify a theme option, Sass compilation will not
occur by default, and you must specify --sass=true or --sass to
turn it on. For more information, see Customizing Themes Using
the Tooling.
platform-options Platform-specific options that will pass verbatim to the Cordova
CLI.
This option is typically required for Windows device deployments
or if a Cordova plugin contains multiple static libraries for different
CPUs. If the value passed contains a quoted string, the quotation
marks must be escaped.
For example, you can use platform-options to specify Windows
architectures. By default, the architecture defaults to anycpu.
To specify a single architecture, use the --arch option and specify
arm, x86, x64, or anycpu.
--platform-options="--arch arm|x86|x64|anycpu"
For example, the following command will build the sample Android application shown
in Scaffold a Hybrid Mobile Application with the Oracle JET CLI
ojet build android
The command will take some time to complete. If it’s successful, you’ll see the
following message: Done. The command will also output the name and location of the
built application in hybrid/platforms/android , hybrid/platforms/ios, or hybrid/
platforms/windows.
By default ojet build creates a debug version of your application. You can also use
the ojet build command with the –-release option to build a release-ready version of
your application. For information, see Packaging and Publishing Hybrid Mobile
Applications.
2-47
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
WARNING:
If you use a proxy server and specify the Android platform, the build
command will fail the first time you issue it. To resolve this, create a
gradle.properties file in your HOME/.gradle directory and rerun the build
command. The file should contain the following:
systemProp.http.proxyHost=proxy-server-URL
systemProp.http.proxyPort=80
systemProp.https.proxyHost=proxy-server-URL
systemProp.https.proxyPort=80
The Oracle JET tooling defines default build and serve behavior in node_modules/
@oracle/oraclejet-tooling/lib/defaultconfig.js. The first part of the file contains the
default build configuration, including entries for application paths and other options
used during the build process. To modify defaults or add your own, edit oraclejet-
build.js and remove comments from the options that you want to configure. The file
contains detailed instructions for each configurable option.
Oracle JET will merge the options with the default configuration during the build.
However, be aware that fileList options will completely replace the corresponding
option in the default configuration and will not be merged.
To customize build behavior:
1. Open scripts/config/oraclejet-build.js for editing in your favorite text editor.
2. Identify the entry that you want to change and remove the comments (//) preceding
each line.
For example, to add a custom library to the build’s staging directory for hybrid
applications, remove the comments from the following section in oraclejet-
build.js as shown below:
/**
* This is the hybrid specific configuration. You can specify configurations
targeted only hybrid apps.
* The hybrid specific configurations will override the general configuration.
*/
hybrid: {
copyCustomLibsToStaging: {
fileList: [
{
cwd:'node_modules/oraclejet/',
src: ['*'],
dest: 'hybrid/www/js/libs/oraclejet'
}
]
}
}
2-48
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
hybrid: {
copyCustomLibsToStaging: {
fileList: [
{
cwd:'node_modules/my_custom_module/',
src: ['*'],
dest: 'hybrid/js/libs/my_custom_module'
}
]
}
}
4. Repeat the previous step for each option that you want to modify.
Test your changes by running Build a Hybrid Mobile Application with the Oracle JET
CLI with appropriate options.
Customize the Hybrid Mobile Application Tooling Workflow Using Hook Points
Hook points that Oracle JET tooling defines let you customize the behavior of the build
and serve processes when you want to define new steps to execute during the tooling
workflow using script code that you write.
When you create a hybrid mobile application, Oracle JET tooling generates script
templates in the /scripts/hooks application subfolder. To customize the Oracle JET
tooling workflow you can edit the generated templates with the script code that you
want the tooling to execute for specific hook points during the build and serve
processes. If you do not create a custom hook point script, Oracle JET tooling ignores
the script templates and follows the default workflow for the build and serve processes.
To customize the workflow for the build or serve processes, you edit the generated
script template file named for a specific hook point. For example, to trigger a script at
the start of the tooling's build process, you would edit the before_hybrid_build.js script
named for the hook point triggered before the build begins. That hook point is named
before_hybrid_build.
Therefore, customization of the build and serve processes that you enforce on Oracle
JET tooling workflow requires that you know the following details before you can write
a customization script.
• The type of application that you created: either a web application or a hybrid
mobile application.
• The Oracle JET build or serve mode that you want to customize:
– Debug — The default mode when you build or serve your application, which
produces the source code in the built application.
– Release — The mode when you build the application with the --release
option, which produces minified and bundled code in a release-ready
application.
• The appropriate hook point to trigger the customization.
• The location of the default hook script template to customize.
2-49
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
Note:
By default, the hooks system locates the scripts in the hooks subfolder using
a generated configuration file that specifies the script paths. If you change
the default location of the hooks subfolder, you must edit the configuration file
to specify the new directory path. For details, see Create a Web Application
Build or Serve Hook Script for Hybrid Mobile Applications.
Create a Web Application Build or Serve Hook Script for Hybrid Mobile Applications
You can create a hook point script that defines a new build or serve process step by
editing the script template associated with a specific hook point in the tooling build and
serve workflow.
The Oracle JET hooks system defines various script trigger points, also called hook
points, that allow you to customize the build and serve workflow across the various
build and serve modes. Customization relies on script files and the script code that you
want to trigger for a particular hook point. Note that the generated script templates that
you modify with your script code are named for their corresponding hook point.
To customize the workflow for the build or serve processes, you edit the generated
script template file named for a specific hook point. For example, to trigger a script at
the start of the tooling's build process, you would edit the before_hybrid_build.js script
named for the hook point triggered before the build begins. That hook point is named
before_hybrid_build.
The following example illustrates a build customization using the after_build hook.
This hook script is an example of adding a customize task after the build finishes.
'use strict’;
const fs = require('fs');
const archiver = require('archiver');
output.on('close', () => {
console.log('Files were successfully archived.');
resolve();
});
archive.pipe(output);
archive.directory('web', false);
archive.finalize();
2-50
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
});
};
In this example, assume the script templates reside in the default folder generated
when you created the application. The goal is to package the application into a ZIP file.
Because packaging occurs after the application build process completes, this script is
triggered for the after_build hook point. For this hook point, the modified script
template after_build.js will contain the script code to zip the application, and because
the .js file resides in the default location, no hooks system configuration changes are
required.
When the tooling reaches the hook point, it executes the corresponding script which it
locates using the configuration files in the /scripts/config application subfolders:
• <myApp_Home>/scripts/config/oraclejet-build.js - Specifies the location and hook
type of build process scripts.
• <myApp_Home>/scripts/config/oraclejet-serve.js - Specifies the location and hook
type of serve process scripts.
Note:
If your development effort requires you to relocate hook scripts to a common
location, for example to support team development, you can update the path
attributes in the hook system configuration file to specify the new target path
for the scripts. When you edit the configuration file do not change the type
attributes. The hook system relies on type attribute to execute the script for a
specific build/serve hook point.
The following table identifies the hook points and the workflow customizations they
support in Oracle JET build and serve processes.
2-51
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
Tip:
OJET tooling reports when hook points are executed in the message log for
the build and serve process. You can examine the log in the console to
understand the tooling workflow and determine exactly when the tooling
triggers a hook point script.
The following table describes the commonly-used options and provides examples for
their use.
Tip:
You can also enter ojet help at a terminal prompt to get help for specific
Oracle JET CLI commands.
2-52
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
Option Description
[ios|android|windows] Desired platform. Enter android, ios, or windows.
build-config Specify the path to buildConfig.json. The buildConfig.json file
contains details that Cordova can use to sign the application.
You do not need this file when building a debug version of your
application for Android or Windows, or if you are building your app
for deployment to an iOS simulator. However, you must configure
one for testing on an iOS device and for pre-release testing of
your Android, iOS or Windows application. For additional
information, see Packaging and Publishing Hybrid Mobile
Applications.
server-port Server port number. If not specified, defaults to 8000.
livereload-port Live reload port number. If not specified, defaults to 35729.
2-53
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
Option Description
destination Specify one of the following:
• emulator: Displays your application in the default Android
AVD, iOS Simulator, or Windows Emulator.
To use a different emulator, append its name to the emulator
option: --destination=emulator:emulator-name.
Tip:
You can view the list of available emulators for
each platform by invoking the following from your
app's top-level folder:
hybrid/platforms/{platform}/cordova/lib/
list-emulator-images
where {platform} is one of android, ios, or
windows.
--destination=browser:firefox|edge|ie|opera|safari|
chrome
Tip:
To change your application’s default browser from
Chrome, open oraclejetconfig.json in the
application's top level directory and change the
name of the browser in defaultBrowser. For
example, to change the default browser to Firefox,
edit oraclejetconfig.json as shown below.
"defaultBrowser": "firefox"
Note:
If you want to send your application to an iOS
device, you must take additional steps as
described in Packaging a Hybrid Mobile App on
iOS.
2-54
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
Option Description
browser[=browser-name] Equivalent to destination=browser[:browser-name].
emulator[=emulator-name] Equivalent to destination=emulator[:emulator-name].
device[=device-name] Equivalent to destination=device[:device-name].
server-only Equivalent to destination=server-only.
livereload Enable the live reload feature. Live reload is enabled by default
(--livereload=true).
Use --livereload=false or --no-livereload to disable the live
reload feature.
Disabling live reload can be helpful if you’re working in NetBeans
or another IDE and want to use that IDE’s mechanism for loading
updated applications.
build Build the app before you serve it. By default, an app is built before
you serve it (--build=true).
Use --build=false or --no-build to suppress the build if you’ve
already built the application and just want to serve it.
theme Theme to use for the application. The theme defaults to
alta:platform, but you can specify an alternate theme. You can
also enter a different themename with optional platform for a
custom theme as described in Customizing Themes Using the
Tooling .
themes Themes to use for the application, separated by commas.
If you don’t specify the --theme flag as described above, Oracle
JET will use the first element that you specify in --themes as the
default theme. Otherwise Oracle JET will build the application with
the theme specified in --theme.
sass Manages Sass compilation. If you add Sass and specify the --
theme or --themes option, Sass compilation occurs by default and
you can use --sass=false or --no-sass to turn it off. If you add
Sass and do not specify a theme option, Sass compilation will not
occur by default, and you must specify --sass=true or --sass to
turn it on. For more information, see Customizing Themes Using
the Tooling .
Note:
The option that you choose controls
both Sass compilation in the build
step and Sass watch in the serve
step.
2-55
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
Option Description
platform-options Platform-specific options that will pass verbatim to the Cordova
CLI.
This option is typically required for Windows device deployments
or if a Cordova plugin contains multiple static libraries for different
CPUs. If the value passed contains a quoted string, the quotation
marks must be escaped.
For example, you can use platform-options to specify Windows
architectures. By default, the architecture defaults to anycpu.
To specify a single architecture, use the --arch option and specify
arm, x86, x64, or anycpu.
--platform-options="--arch arm"
Command Description
ojet serve windows -–browser=firefox Launches a Windows version of the
application in the Firefox browser.
ojet serve ios Launches the application in the iOS Simulator
using the Alta iOS theme.
ojet serve android --destination=emulator:MyEmulator Launches the application in the Android
emulator using the AVD named “MyEmulator”.
The emulator name is case-sensitive.
ojet serve android --device Launches the application on the attached
Android mobile device.
The terminal will also output the names of the files as they are loaded. If your
application contains multiple views, the output will reflect the names of the views and
associated files as you navigate through the application.
WARNING:
If you specify the Android platform, use a proxy server and skipped the ojet
build step, the serve command will fail the first time you issue it. To resolve
this, create a gradle.properties file in your HOME/.gradle directory and rerun
the serve command. The file should contain the following:
systemProp.http.proxyHost=proxy-server-URL
systemProp.http.proxyPort=80
systemProp.https.proxyHost=proxy-server-URL
systemProp.https.proxyPort=80
2-56
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
name of the changed file, and the browser or emulator/simulator updates with the
change. Live reload is disabled when you serve an application to a device.
To terminate the batch job when using live reload, press Ctrl+C in the command
window and then enter y if prompted to terminate the batch job.
Note:
You can also use the ojet serve command with the –-release option to serve
a release-ready version of your application. For information, see Packaging
and Publishing Hybrid Mobile Applications.
// sass: {
// files: [],
// commands: ['compileSass']
// },
// themes: {
// files: [],
// options: {
2-57
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
// livereload: true
// },
// commands: ['copyThemes']
// },
}
3. Edit the options as desired and save the file.
For command options, add one or more commands, each surrounded by single
quotes and separated by commas. For example, the following code snippet adds
node --version and npm --version to the commands option in the sourceFiles sub
task. Add a comma to the closing braces.
watch: {
sourceFiles:
{
// files: [],
// options: {
// livereload: true
// },
commands: ['node --version', 'npm --version']
},
... contents omitted
},
Test your changes by running Serve a Hybrid Mobile Application with the Oracle JET
CLI with appropriate options. When you change one of the source files, the terminal
output will display the output of the node --version and npm --version commands and
reload the page.
The value of the id attribute determines the unique identifier for this app. By
default, JET apps use a combination of reverse domain notation with the
oraclejet.org domain name as input and the app’s short name. Change this to
use, for example, the reverse of your company’s domain name.
• The value of the version attribute identifies the version number of your app to end
users. It appears, for example, on the App Info screen on Android devices.
Change it to an appropriate value.
• Value for the name element. This is the name of the app displayed on the
springboard of your user's mobile device and in the app stores.
Apart from these generic settings that affect your app irrespective of the platform
where it is deployed (Android, iOS, or Windows), you can configure a range of other
entries in the config.xml file that set preferences for your app on specific platforms.
2-58
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
2-59
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
JET provides a set of splash screen images for each platform that can be used in your
app. These images are stored in the following sub-directories of your app’s hybrid
directory.
AppRootDirectory/hybrid/res/screen
+---android
| splash-land-hdpi.9.png
| ...
| splash-port-xxxhdpi.9.png
|
+---ios
| Default-568h@2x~iphone.png
| ...
| Default~iphone.png
|
\---windows
SplashScreen.scale-100.png
SplashScreenPhone.scale-240.png
You can replace the JET-provided splash screen images with your own images,
matching the names and sizes. You can change the behavior of the splash screen by
configuring your app’s AppRootDir/hybrid/config.xml file. The following example shows
how you display the splash screen for 4000 milliseconds.
<preference name="SplashScreenDelay" value="4000" />
For more information about the plugin, including how to configure its behavior plus
platform-specific information, see https://cordova.apache.org/docs/en/latest/reference/
cordova-plugin-splashscreen/.
JET provides a set of app launcher icons for each platform that can be used in your
app. To use an alternative app launcher icon to the JET-provided icons, replace the
images in the following directories.
AppRootDir/hybrid/res/icon
+---android
| icon-hdpi.png
| ...
| icon-xxxhdpi.png
|
+---ios
| icon-40.png
| ...
| [email protected]
|
\---windows
Square150x150Logo.scale-100.png
...
Wide310x150Logo.scale-240.png
Both the splash screen and app launcher icons that your app uses are referenced from
your app’s AppRootDir/hybrid/config.xml file, as shown by the following example
excerpts.
2-60
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
<platform name="windows">
...
<icon src="res/icon/windows/Square30x30Logo.scale-100.png" width="30"
height="30"/>
...
<splash src="res/screen/windows/SplashScreen.scale-100.png" width="620"
height="300"/>
<platform name="android">
...
<icon src="res/icon/android/icon-ldpi.png" width="36" height="36"/>
...
<splash src="res/screen/android/splash-land-ldpi.9.png" density="land-ldpi"/>
...
<platform name="ios">
...
<icon src="res/icon/ios/icon-small.png" width="29" height="29"/>
...
<splash src="res/screen/ios/Default@2x~iphone.png" width="640" height="960"/>
...
For more information about icons and their entries in the config.xml file, see https://
cordova.apache.org/docs/en/latest/config_ref/images.html.
Topics
• About Apache Cordova and Cordova Plugins
• Use a Plugin in Your App
• Cordova Plugins Recommended by Oracle JET
• Use a Different Web View in your JET Hybrid Mobile App
• Use the Native Platform’s Date Picker UI in your JET Hybrid Mobile App
2-61
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
JET uses Apache Cordova to develop web technology-based applications for some of
the mobile platforms supported by Apache Cordova. So, one could say that the JET
hybrid mobile application that you develop is an Apache Cordova application where
JET provides features such as components and themes that determine the look and
feel of your application.
A plugin is a package of code that allows the Cordova web view within which your
application renders to communicate with the native platform on which it runs. The
plugin does this by providing a JavaScript interface to native components that allows
your application to use native device capabilities, such as camera, geolocation, and so
on.
So, assume for example, that you want your application to use a camera. In that case,
you look for an existing plugin to provide access to the camera on the platform(s)
where your application will be installed as there is a good possibility that someone has
already developed a plugin to address this requirement.
If you cannot find a plugin that meets your requirements, you can develop your own
plugin. Although this blog post makes reference to another mobile development
framework (MAF), it provides a suitable introduction to creating a Cordova plugin that
could be used in a JET application or any Cordova-based application.
To find a plugin to use in your application, go to the Plugins page at https://
cordova.apache.org/plugins/ that provides a registry of core and third-party Cordova
plugins. Core plugins are plugins provided by Apache Cordova while third-party
plugins are those developed by independent developers. Core plugins display a blue
vertical strip to the left of the card that describes the plugin, as shown in the following
figure, where two core plugins (cordova-plugin-media and cordova-plugin-contacts)
appear after a third-party plugin named cordova-plugin-ble-peripheral.
The Plugins page categorizes plugins according to the platform on which the plugins
will run. Some plugins run on a wide variety of mobile platforms (Android, iOS,
Windows, and so on) while other plugins may only support one platform. Ensure that
the plugins you install in your application support the platforms where your application
will run.
2-62
Chapter 2
Get Started with Oracle JET Hybrid Mobile Application Development
Each entry generally provides information such as a brief description of the plugin’s
functionality, the platforms it supports, and the number of days since it was updated. It
then links to a web page where you can access more detail, such as how to install the
plugin in your application and use it once installed.
Once you have added the plugin to your app, you need to write code in your app to
use the plugin. The following code excerpts illustrate how you might use the phonegap-
plugin-barcodescanner plugin by exposing a button in the appRootDir/src/js/views/
incidents.html page of a JET app built using the navbar template that invokes the
barcode scanner on the device.
...
<oj-button id='b1Scan' on-oj-action='{{buttonClick}}' label='Scan'></oj-button>
...
The following code excerpt shows the entries to add to the appRootDir/src/js/
viewModels/incidents.js file which invokes the barcode scanner when an end user
clicks the button that the incidents.html page renders.
self.buttonClick = function(data, event){
cordova.plugins.barcodeScanner.scan(
function (result) {
alert("We got a barcode\n" +
"Result: " + result.text + "\n" +
"Format: " + result.format + "\n" +
"Cancelled: " + result.cancelled);
},
function (error) {
alert("Scanning failed: " + error);
}
);
}
2-63
Chapter 2
Working with the Oracle JET Starter Templates
On the Android platform, this is the Android platform’s WebView. A number of Cordova
plugins exist that enable you to use a different web view in your hybrid mobile app.
One example for the Android platform is the cordova-plugin-crosswalk-webview plugin
that configures your app to use the Crosswalk web view to bring performance
improvements and compatibility improvements across older Android versions. Install
this plugin in your app using the following command:
ojet add plugin cordova-plugin-crosswalk-webview
Use the Native Platform’s Date Picker UI in your JET Hybrid Mobile App
Include the cordova-plugin-datepicker plugin in your JET hybrid mobile app if you
want to present end users with the date picker UI of the underlying platform (Android,
iOS, or Windows) when your app renders JET date or time components such as oj-
input-date or oj-input-date-time.
JET hybrid mobile apps that do not include the cordova-plugin-datepicker plugin
render the aforementioned JET components using the default UI provided by Oracle
JET.
Install the cordova-plugin-datepicker plugin in your app using the following command:
ojet add plugin cordova-plugin-datepicker
Topics:
• About the Starter Templates
• Modifying Starter Template Content
You can also view a video that shows how to work with the Oracle JET Starter
Templates in the Oracle JET Videos collection.
2-64
Chapter 2
Working with the Oracle JET Starter Templates
Instead of storing all the application markup in the index.html file, the application uses
the oj-module component to bind either a view template containing the HTML markup
for the section or both the view template and JavaScript file that contains the
viewModel for any components defined in the section.
The following code shows a portion of the index.html file in the Web Nav Drawer
Starter Template that highlights the oj-module component definition. For the sake of
brevity, most of the code and comments are omitted. Comments describe the purpose
of each section, and you should review the full source code for accessibility and usage
tips.
<!DOCTYPE html>
<html lang="en-us">
<head>
<title>Oracle JET Starter Template - Web Nav Drawer</title>
... contents omitted
</head>
<body class="oj-web-applayout-body">
The main page’s content area uses the Oracle JET oj-web-applayout-* CSS classes to
manage the responsive layout. The main page’s content uses the HTML oj-module
element with its role defined as main (role="main") for accessibility.
The oj-module component’s config.view attribute tells Oracle JET that the section is
only defining a view template, and the view will be bound to the existing viewModel.
When the oj-module element’s config.view-model attribute is defined, the application
will load both the viewModel and view template with the name corresponding to the
value of the config.view-model attribute.
When the oj-module element’s view and view-model attributes are missing, as in this
example, the behavior will depend on the parameter specified in the config attribute’s
definition.
• If the parameter is an Oracle JET router’s moduleConfig object as in the above
example, then oj-module will automatically load and render the content of the
viewModel script and view template corresponding to the router’s current state.
The Web Nav Drawer Starter Template uses oj.Router to manage navigation
when the user clicks one of the application’s navigation items. The router states
2-65
Chapter 2
Working with the Oracle JET Starter Templates
include dashboard, incidents. customers, , and about. If the user clicks Incidents, for
example, the main content area changes to display the content in the incidents
view template.
2-66
Chapter 2
Working with the Oracle JET Starter Templates
For additional information about working with single page applications, oj-module,
oj.Router, and Knockout templates, see Creating Single-Page Applications.
For details about the oj-web-applayout-* CSS classes, see Web Application Patterns.
For additional information about working with responsive design, see Designing
Responsive Applications.
If you add content to a section that changes its role, then be sure to change the role
associated with that section. Oracle JET uses the role definitions for accessibility,
specifically WAI-ARIA landmarks. For additional information about Oracle JET and
accessibility, see Developing Accessible Applications.
Before you begin:
1. Create your Oracle JET application using one of the following methods:
• Create a Web Application Using the Oracle JET Command-Line Interface
• Create a Hybrid Mobile Application
• Create a Web Application Using the Oracle JET Starter Templates
2. Load the starter template into your favorite IDE, or extract the zip file into a
development folder.
The example in the procedure below uses the Web Nav Drawer Starter Template,
but you can use the same process on any of the Starter Templates.
2-67
Chapter 2
Working with the Oracle JET Starter Templates
Tip:
If you used the command line tooling to scaffold your application, you
can still use an IDE like NetBeans for editing. For example, in the
NetBeans IDE, choose File –> Open Project and select the folder
containing the application you created. Edit your application as needed,
but use the tooling commands in a terminal window to build and serve
your application.
The return value of [[moduleConfig]] is set to the current state of the ojRouter
object. The ojRouter object is defined with an initial value of dashboard in the
application's appController.js script, shown below.
// Router setup
self.router = oj.Router.rootInstance;
self.router.configure({
'dashboard': {label: 'Dashboard', isDefault: true},
'incidents': {label: 'Incidents'},
'customers': {label: 'Customers'},
'about': {label: 'About'}
});
oj.Router.defaults['urlAdapter'] = new oj.Router.urlParamAdapter();
To modify the Dashboard Content Area, for example, you will modify both
dashboard.html and dashboard.js.
2. To modify the view template, remove unneeded content, and add the new content
to the view template file.
For example, if you are working with an Oracle JET Cookbook sample, you can
copy the markup into the view template you identified in the previous step
(dashboard.html). Replace everything after the <h1>Dashboard Content Area</h1>
markup in the template with the markup from the sample.
The following code shows the modified markup if you replace the existing content
with a portion of the content from the Date and Time Pickers demo.
<div id="div1">
<oj-label for="dateTime">Default</oj-label>
<oj-input-date-time id="dateTime" value='{{value}}'>
</oj-input-date-time>
<br/><br/>
2-68
Chapter 2
Working with the Oracle JET Starter Templates
</div>
3. To modify the ViewModel, remove unneeded content, and add the new content as
needed. Include any additional RequireJS modules that your new content may
need.
The application's main.js file contains the list of Require modules currently
included in the application. Compare the list of libraries with the list you need for
your application, and add any missing modules to your define() function in the
ViewModel script. For example, to use the oj-input-date-time elements shown in
the demo, add ojs/ojdatetimepicker to the dashboard.js ViewModel script since it's
not already defined in dashboard.js.
The sample below shows a portion of the modified dashboard.js file, with the
changes highlighted in bold.
define(['ojs/ojcore' ,'knockout', 'ojs/ojdatetimepicker'
], function(oj, ko) {
/**
* The view model for the main content view template
*/
function DashboardViewModel() {
var self = this;
self.value = ko.observable(oj.IntlConverterUtils.dateToLocalIso(new
Date(2013, 0, 1)));
}
return DashboardViewModel;
});
Important:
Notice that with this example, you are not copying the entire code
section. The Cookbook uses a require() call to load and use the needed
libraries in a single bootstrap file. The Starter Template that you are
pasting uses define() to create a RequireJS module that can be used by
other parts of your application.
If you're not using a Cookbook example and are not sure which RequireJS module
to include, see the table at Oracle JET Module Organization.
4. If you want to add, change, or delete modules or templates to the application,
modify the main.js RequireJS bootstrap file and appController.js file as needed.
The appController.js file also contains the event handler that responds when a
user clicks one of the navigation items. Depending upon your modifications, you
may need to update this method as well.
5. Verify the changes in your favorite browser.
The following image shows the runtime view of the Web Nav Drawer Starter
Template with the new Dashboard Content Area content showing oj-input-date-
time with its current value.
2-69
Chapter 2
Optimizing Application Startup Using Oracle CDN and Oracle JET Libraries
2-70
Chapter 2
Optimizing Application Startup Using Oracle CDN and Oracle JET Libraries
Configuring the application to load from CDN offers these advantages over loading
from the application src folder:
• Once loaded from a CDN distribution server, the required libraries and module will
be available to other applications that the user may run in the same browser
session.
• Enables the option to load bundled libraries and module using a bundles
configuration file that Oracle maintains on CDN. The bundles configuration file
groups the most commonly accessed libraries and modules into content packages
that are specific to the release and makes them available for delivery to the
application as a bundle.
Tip:
Configuring your application to reference the bundles configuration on Oracle
CDN is recommended because Oracle maintains the configuration for each
release. By pointing your application to the current bundles configuration,
you will ensure that your application runs with the latest supported library and
module versions. For information about how to enable this bundle loading
optimization, see Configuring the Application for Oracle CDN Optimization.
2-71
3
Designing Responsive Applications
Oracle JET includes classes that support a flexbox–based layout, 12-column
responsive grid system, design patterns, responsive form layout, and responsive
JavaScript that you can use to design responsive web and hybrid mobile applications.
Topics:
• Typical Workflow for Designing Responsive Applications in Oracle JET
• Oracle JET and Responsive Design
• Media Queries
• Oracle JET Flex, Grid, Form, and Responsive Helper Class Naming Convention
• Oracle JET Flex Layouts
• Oracle JET Grids
• Responsive Layout and Content Design Patterns
• Responsive Form Layouts
• Adding Responsive Design to Your Application
• Using Responsive JavaScript
• Using the Responsive Helper Classes
• Creating Responsive CSS Images
• Changing Default Font Size
• Controlling the Size and Generation of the CSS
3-1
Chapter 3
Oracle JET and Responsive Design
Media Queries
CSS3 media queries use the @media at-rule, media type, and expressions that evaluate
to true or false to define the cases for which the corresponding style rules will be
applied. Media queries form the basis for Oracle JET’s responsive classes.
3-2
Chapter 3
Media Queries
<style>
@media media_types (expression){
/* media-specific rules */
}
</style>
The CSS3 specification defines several media types, but specific browsers may not
implement all media types. The media type is optional and applies to all types if not
specified. The following media query will display a sidebar only when the screen is
wider than 767 pixels.
@media (max-width: 767px){
.facet_sidebar {
display: none;
}
}
Oracle JET defines CSS3 media queries for the screen widths listed in the following
table.
Note:
For printing, Oracle JET uses the large screen layout for printing in
landscape mode and the medium screen layout for printing in portrait mode.
Oracle JET's size defaults and media queries are defined in the Sass variables
contained in site_root/scss/oj/6.0.0/common/_oj.common.variables.scss\ and are used
in the grid, form, and responsive helper style classes. The following code sample
shows the responsive screen width variables and a subset of the responsive media
queries. In most cases the defaults are sufficient, but be sure to check the file for
additional comments that show how you might modify the variables for your application
if needed.
// responsive screen widths
$screenSmallRange: 0, 767px !default;
$screenMediumRange: 768px, 1023px !default;
$screenLargeRange: 1024px, 1280px !default;
$screenXlargeRange: 1281px, null !default;
3-3
Chapter 3
Oracle JET Flex, Grid, Form, and Responsive Helper Class Naming Convention
bound($screenMediumRange)})" !default;
$responsiveQueryMediumDown: "print and (orientation: portrait), screen and (max-
width: #{upper-bound($screenMediumRange)})" !default;
Size can be one of sm, md, lg, xl, and print and are based on the media queries
described in Media Queries. Oracle JET will apply the style to the size specified and
any larger sizes unless function is defined as only. For example:
3-4
Chapter 3
Oracle JET Flex Layouts
The following image shows an example of a default flex layout using the Oracle JET
flex box styles. The sample contains two flex containers, each with three children. As
the screen size widens, the flex container allocates unused space to each of the
children. As the screen size shrinks below the width of one of the flex items, the flex
container will wrap the content in that item as needed to no wider than the maximum
display width. In this example, this has the effect of causing the F child to wrap to the
next row.
The markup for this flex layout is shown below, with the flex layout classes highlighted
in bold. The demo-flex-display class sets the color, font weight, height, and border
around each flex item in the layout.
<div id="container">
<div class="demo-flex-display">
<div class="oj-flex">
<div class="oj-flex-item">A</div>
<div class="oj-flex-item">B</div>
<div class="oj-flex-item">C</div>
</div>
<div class="oj-flex">
<div class="oj-flex-item">D</div>
<div class="oj-flex-item">E</div>
<div class="oj-flex-item">F - This child has more text to show the effect of
unequal content.</div>
</div>
3-5
Chapter 3
Oracle JET Flex Layouts
</div>
</div>
You can customize the flex layout using styles detailed in Flex Layout Styling and
described below.
You can achieve the same effect by adding the oj-sm-flex-items-initial class to the
flex container to set the flex property to initial for all child flex items, or add the oj-
sm-flex-initial class to an individual flex item to set its property to initial. The
following image shows the effect.
The code sample below shows the markup. In this example, padding is also added to
the content using the oj-flex-items-pad class on the parent container.
<div id="container">
<div class="demo-flex-display oj-flex-items-pad">
<div class="oj-flex oj-sm-flex-items-initial">
<div class="oj-flex-item">A</div>
<div class="oj-flex-item">B</div>
<div class="oj-flex-item">C</div>
</div>
<div class="oj-flex">
<div class="oj-flex-item">A</div>
<div class="oj-sm-flex-initial oj-flex-item">B</div>
<div class="oj-flex-item">C</div>
</div>
</div>
</div>
You can also override the default auto flex property by using the oj-size-flex-items-1
class on the flex container. This class sets the flex property to 1, and all flex items in
the flex container with a screen size of size or higher will have the same width,
regardless of the items’ content.
3-6
Chapter 3
Oracle JET Flex Layouts
To set the flex property to 1 on an individual flex item, add oj-sm-flex-1 to the flex
item. The Flex Layouts section in the Oracle JET Cookbook includes the examples
used in this section that you can use and modify to observe the flex layout’s
responsive behavior.
Wrapping Content
By default, Oracle JET sets the CSS flex-wrap property to wrap, which sets the flex
container to multi-line. Child flex items will wrap content to additional lines when the
screen width shrinks to less than the width of the flex item’s content. However, the
CSS model sets the flex-wrap property to nowrap, which sets the flex container to
single-line. When a child item’s content is too wide to fit on the screen, the content will
wrap within the child.
You can set the flex-wrap property to nowrap by adding oj-sm-flex-nowrap to the oj-
flex container. The following image shows the effect of changing the flex-wrap
property to nowrap.
• align-items
• align-self
• justify-content
• order
3-7
Chapter 3
Oracle JET Grids
The Oracle JET Cookbook includes examples for customizing your flex layout at: Flex
Layouts.
Topics:
• About the Grid System
• The Grid System and Printing
• Grid Convenience Classes
The Responsive Grids section in the Oracle JET Cookbook provides several examples
and recipes for using the Oracle JET grid system, and you should review them to get
accustomed to the grid system.
When the defaults are not sufficient, you can specify relative widths for the flex items
when the screen size changes. In the following image, the flex layout is using grid
classes to define different widths when the screen size changes to medium, large, and
extra large.
3-8
Chapter 3
Oracle JET Grids
The grid classes follow the Oracle JET Flex, Grid, Form, and Responsive Helper Class
Naming Convention. Use oj-size-numberofcolumns to set the width to the specified
numberofcolumns when the screen is the specified size or larger. For example:
• oj-sm-6 works on all screen sizes and sets the width to 6 columns.
• oj-sm-6 and oj-lg-3 on the same flex item sets the width to 6 columns wide on
small and medium screens and 3 columns wide on large and extra-large screens.
Design for the smallest screen size first and then customize for larger screens as
needed. You can further customize the grid by adding one of the Grid Convenience
Classes or by using one of the responsive helper classes described in Using the
Responsive Helper Classes.
The following code sample shows the markup for the modified Flex Auto Layout
display, with grid classes defined for medium, large, and extra-large screens.
<div class="oj-flex">
<div class="oj-md-6 oj-lg-2 oj-xl-8 oj-flex-item">A</div>
<div class="oj-md-3 oj-lg-4 oj-xl-2 oj-flex-item">B</div>
<div class="oj-md-3 oj-lg-6 oj-xl-2 oj-flex-item">C</div>
</div>
When the screen size is small, the flex layout default styles are used, and each item
uses the same amount of space. When the screen size is medium, the A flex item will
use 6 columns, and the B and C flex items will each use 3 columns. When the screen
size is large, The A flex item will use 2 columns, the B flex item will use 4 columns,
and the C flex item will use 6 columns. Finally, when the screen size is extra large, the
A flex item will use 8 columns, and the B and C flex items will each use 2 columns.
For a complete example that illustrates working with the grid system, see Responsive
Grids.
3-9
Chapter 3
Oracle JET Grids
As shown in the following print preview, when you print this grid in landscape mode,
the oj-lg-4 style classes will be applied on Row 3 and Row 4. When you print the grid
in portrait mode, the oj-md-* style classes apply on Row 2 and Row 4.
If you want to change the printing default, you can set the Sass $responsiveQueryPrint
variable to print in a custom settings file. After you enable the print classes, you can
add the oj-print-numberofcolumns style class to the column definition. This has the
effect of changing the column sizes for printing purposes only. In the following
example, Row 1 includes the oj-print-6 class for each column in the row.
<div class="oj-flex oj-flex-items-pad">
<div class="oj-sm-9 oj-print-6 oj-flex-item"></div>
<div class="oj-sm-3 oj-print-6 oj-flex-item"></div>
</div>
In normal mode, Row 1 contains two columns, one column with a size of 9 and one
column with a size of 3, regardless of screen size. If you do a print preview, however,
you'll see that Row 1 will print with two columns in portrait and landscape mode, both
with a size of 6.
3-10
Chapter 3
Oracle JET Grids
For information about setting Sass variables in a custom settings file, see Customizing
Themes Using the Tooling.
The code sample below shows the grid configuration used to render the figure.
The example also sets oj-sm-odd-cols-12 which will set the odd column width to 12
on small screens, displaying col2 on a new row.
<div class="oj-md-odd-cols-4 oj-flex-items-pad">
<div class="oj-flex">
<div class="oj-flex-item">col 1</div>
<div class="oj-flex-item">col 2</div>
</div>
<div class="oj-flex">
<div class="oj-flex-item">col 1</div>
<div class="oj-flex-item">col 2</div>
</div>
<div class="oj-flex">
<div class="oj-flex-item">col 1</div>
<div class="oj-flex-item">col 2</div>
</div>
</div>
You could achieve the same effect by defining oj-md-4 for the first column's width
and oj-md-8 for the second column's width on each flex item.
3-11
Chapter 3
Oracle JET Grids
<div class="oj-flex-items-pad"
<div class="oj-flex">
<div class="oj-sm-12 oj-md-4 oj-flex-item">col 1</div>
<div class="oj-sm-12 oj-md-8 oj-flex-item">col 2</div>
</div>
<div class="oj-flex">
<div class="oj-sm-12 oj-md-4 oj-flex-item">col 1</div>
<div class="oj-sm-12 oj-md-8 oj-flex-item">col 2</div>
</div>
<div class="oj-flex">
<div class="oj-sm-12 oj-md-4 oj-flex-item">col 1</div>
<div class="oj-sm-12 oj-md-8 oj-flex-item">col 2</div>
</div>
</div>
The code sample below shows the grid configuration used to render the figure.
<div class="oj-sm-odd-cols-12 oj-md-odd-cols-2 oj-md-even-cols-4 oj-flex-items-
pad">
<div class="oj-flex">
<div class="oj-flex-item">col 1</div>
<div class="oj-flex-item">col 2</div>
<div class="oj-flex-item">col 3</div>
<div class="oj-flex-item">col 4</div>
</div>
<div class="oj-flex">
<div class="oj-flex-item">col 1</div>
<div class="oj-flex-item">col 2</div>
<div class="oj-flex-item">col 3</div>
<div class="oj-flex-item">col 4</div>
</div>
</div>
If you don't use the convenience classes, you must define the size classes on
every column in every row as shown below.
<div class="oj-flex-items-pad">
<div class="oj-flex">
<div class="oj-sm-odd-cols-12 oj-md-2 oj-flex-item">col 1</div>
<div class="oj-sm-odd-cols-12 oj-md-4 oj-flex-item">col 2</div>
<div class="oj-sm-odd-cols-12 oj-md-2 oj-flex-item">col 3</div>
<div class="oj-sm-odd-cols-12 oj-md-4 oj-flex-item">col 4</div>
</div>
<div class="oj-flex">
<div class="oj-sm-odd-cols-12 oj-md-2 oj-flex-item">col 1</div>
<div class="oj-sm-odd-cols-12 oj-md-4 oj-flex-item">col 2</div>
<div class="oj-sm-odd-cols-12 oj-md-2 oj-flex-item">col 3</div>
3-12
Chapter 3
Responsive Layout and Content Design Patterns
3-13
Chapter 3
Responsive Layout and Content Design Patterns
• Column Drop: Columns drop below each other for smaller screens. At the widest
breakpoint, content displays in columns. As the display size is reduced, columns
drop from the rightmost side of the display and add as rows below the remaining
column(s).
• Layout Shift: Column layout changes for smaller screens. Layout shape may differ
at each breakpoint as content is repositioned. Rather than simply adding or
removing columns, for example, the leftmost column could transition to a row at
the top of the display.
3-14
Chapter 3
Responsive Layout and Content Design Patterns
The difference between the two design patterns is subtle, and you can get a better
feel for the difference by looking at the Oracle JET Cookbook examples at differing
widths or on different devices. In the figure below, the column drop and layout shift
patterns are shown at three different screen widths on a desktop display. In this
example, the difference is noticeable in the middle figure for each pattern.
• List - View Switcher The View Switcher allows you to switch between list view and
grid view.
The image below shows the View Switcher web layout displayed in a desktop
browser. The View Switcher switches between list view and grid view as follows.
3-15
Chapter 3
Responsive Layout and Content Design Patterns
The image below shows the list view and grid view patterns of the View Switcher
hybrid layout on a hybrid mobile application with a fixed header.
• Off-Canvas Push and Off-Canvas Overlay: You can use Oracle JET’s
oj.OffcanvasUtils to make an off-canvas partition that responsively change its
position from off-screen to fixed inside the viewport when the browser width is
changed.
In the image below, when the screen size is extra large, the off-canvas design
shows three columns: the off-canvas partition on the start edge labeled A, the main
content labeled B, and the off-canvas partition on the end edge labeled C. When
the screen size is large, the off-canvas position on the end edge (C) is hidden off-
screen, and the launch button indicates its availability. For medium screens and
smaller, both the start edge and end edge partitions are hidden off-screen, and
two launch buttons indicate their availability.
3-16
Chapter 3
Responsive Form Layouts
3-17
Chapter 3
Responsive Form Layouts
When you specify oj-form-cols, Oracle JET calculates the width of the field to
determine if another column will fit in the display. When you specify oj-form-cols-
labels-inline, Oracle JET totals the field and inline label width to determine if
another column will fit in the display.
Tip:
If you want to restrict the display to two columns, add the oj-form-cols-
max2 class to the same element where you placed oj-form-cols or oj-
form-cols-label-inline.
Note:
The CSS multi-column layout technique is not supported by all browsers.
If the browser doesn't support the technique, the display will default to
single column for all screen sizes.
3-18
Chapter 3
Adding Responsive Design to Your Application
Topics:
• The Responsive JavaScript Classes
3-19
Chapter 3
Using Responsive JavaScript
oj.ResponsiveKnockoutUtils.createMediaQueryObservable(
'(min-width:
400px)');
self.screenRange =
oj.ResponsiveKnockoutUtils.createScreenRangeObservabl
e();
3-20
Chapter 3
Using Responsive JavaScript
In this example, the oj-button element’s display attribute is defined for icons. The
code sample below shows the markup for the button.
<div id="optioncontainer">
<oj-button display="[[large() ? 'all' : 'icons']]">
<span slot='startIcon' class="oj-fwk-icon oj-fwk-icon-calendar"></span>
calendar
</oj-button>
</div>
The code sample also sets the oj-button display attribute to all, which displays both
the label and icon when the large() method returns true, and icons only when the
large() method returns false.
The code sample below shows the code that sets the value for large() and completes
the knockout binding. In this example, lgQuery is set to the LG_UP framework query key
which applies when the screen size is large or up. self.large is initially set to true as
the result of the call to
oj.ResponsiveKnockoutUtils.createMediaQueryObservable(lgQuery). When the screen
changes to a smaller size, the self.large value changes to false, and the display
attribute value becomes icons.
require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'ojs/ojbutton'],
function(oj, ko, $)
{
function MyModel(){
var self = this;
self.large = oj.ResponsiveKnockoutUtils.createMediaQueryObservable(lgQuery);
$(function() {
ko.applyBindings(new MyModel(), document.getElementById('optioncontainer'));
});
});
The Oracle JET Cookbook contains the complete code for this example which includes
a demo that shows a computed observable used to change the button label's text
3-21
Chapter 3
Using Responsive JavaScript
depending on the screen size. You can also find examples that show how to use
custom media queries and Knockout computed observables. For details, see
Responsive JavaScript Framework Queries.
For additional information about working with oj-button, see Working with Buttons.
In this example, the HTML content is defined in Knockout templates. The markup uses
Knockout's data-bind utility to display a template whose name depends on the value
returned by the large() call. If the screen is small or medium, the application will use
the sm_md_template. If the screen is large or larger, the application will use the
lg_xl_template.
<div id="sample_container">
The code that sets the value for large() is identical to the code used for setting
component option changes. For details, see Changing a Custom Element’s Attribute
Based on Screen Size.
3-22
Chapter 3
Using Responsive JavaScript
For the complete code used in this example, see the Responsive Loading with
JavaScript demo in the Oracle JET Cookbook.
In this example, the image is defined in a HTML img element. The markup uses Oracle
JET’s attribute binding to display a larger image when the large() call returns true.
<div id="container">
<p>current width:
<strong>
<span>
<oj-bind-text value="[[large() ? 'large' : 'not large']]"></oj-bind-text>
</span>
</strong>
</p>
The code that set the value for large() is identical to the code used for setting
component option changes. For details, see Changing a Custom Element’s Attribute
Based on Screen Size.
Note:
The image will not begin to load until the JavaScript is loaded. This could be
an issue on devices with slower connections. If performance is an issue, you
can use responsive CSS as described in Creating Responsive CSS Images.
You could also use the HTML picture element which supports responsive
images without CSS or JavaScript. However, browser support is limited and
may not be an option for your environment.
For the complete code used in this example, see the Oracle JET Cookbook
Responsive Images demos.
3-23
Chapter 3
Using the Responsive Helper Classes
3-24
Chapter 3
Changing Default Font Size
The code below shows the markup that defines the image. In this example, bulletlist
is a CSS class generated from the Sass responsive variables and media queries.
<div role="img" class="oj-icon bulletlist" title="bulleted list image"></div>
The following image shows the bulletlist CSS class. When the screen is small or
medium size, the icon_small.png image loads. When the screen increases to large or
larger, or to print, the icon.png loads instead.
The Oracle JET Cookbook includes the Sass variables and queries used to generate
the bulletlist CSS class. You can also find a Sass mixin that makes generating the
CSS easier, but you are not required to use SCSS to create responsive CSS images.
In addition, the Oracle JET Cookbook includes examples that show high resolution
images, sprites, button images, and more. For details, see Responsive CSS Images.
Note:
You can also use responsive JavaScript to change the images based on
screen size. For details, see Creating Responsive Images.
Topics
• Changing Default Font Size Across the Application
• Changing Default Font Size Based on Device Type
3-25
Chapter 3
Controlling the Size and Generation of the CSS
For details about customizing Oracle JET themes, see Customizing Themes Using
the Tooling.
• In your application-specific CSS, override the font-size setting for the html class.
For example, to set the browser's default font size to 12 pixels, add the following to
your application-specific CSS:
html {
font-size: 12px;
}
Note:
Be sure to perform this step before initializing components since some
Oracle JET components measure themselves.
3-26
Chapter 3
Controlling the Size and Generation of the CSS
– $includeAppLayoutWebClasses
– $includeFlexClasses
– $includeGridClasses
– $includeFormLayoutClasses
– $includeResponsiveHelperClasses
For additional information about using the $include* variables, see Using Variables
to Control CSS Content.
• Stop generation of a particular responsive helper class.
For finer-grained control, there are additional variables that you can set to false to
generation of a particular type of class.
Variable Description
$responsiveGenerateH Generate hide classes like .oj-md-hide.
ide
$responsiveGenerateT Generate text-align end classes like .oj-md-text-align-end.
extAlignEnd
$responsiveGenerateF Generate float start classes like .oj-md-float-start.
loatStart
$responsiveGenerateF Generate float end classes like .oj-md-float-end.
loatEnd
3-27
4
Using RequireJS for Modular Development
Oracle JET includes RequireJS, a third party JavaScript library that you can use in
your application to load only the Oracle JET libraries you need. Using RequireJS, you
can also implement lazy loading of modules or create JavaScript partitions that contain
more than one module.
Topics:
• Typical Workflow for Using RequireJS
• Oracle JET and RequireJS
• Using RequireJS in an Oracle JET Application
• Adding Third-Party Tools or Libraries to Your Oracle JET Application
• Troubleshooting RequireJS in an Oracle JET Application
• Using JavaScript Partitions and RequireJS in an Oracle JET Application
4-1
Chapter 4
Oracle JET and RequireJS
Note:
Oracle JET reserves the oj namespace, and you may not use oj as a
namespace in your Oracle JET application.
You do not have to use RequireJS to reference Oracle JET libraries, but it is required if
you plan to use Oracle JET's internationalization or data visualization components in
your application. The Oracle JET download includes the RequireJS library, and it is
used by default in the Oracle JET Starter Templates and Cookbook examples.
For more information about RequireJS, see http://requirejs.org.
4-2
Chapter 4
Oracle JET and RequireJS
4-3
Chapter 4
Oracle JET and RequireJS
4-4
Chapter 4
Using RequireJS in an Oracle JET Application
Note:
Oracle JET includes several modules that aren't listed in this table because
they are dependencies of other modules. When you load an Oracle JET
component module, the module's dependencies are also loaded by default,
and you do not need to include them explicitly. For example, oj-dialog
requires the ojs/ojcomponentcore module, and the ojs/ojcomponentcore
module will load automatically when you specify the ojs/ojdialog module in
your RequireJS bootstrap file.
// This section configures the i18n plugin. It is merging the Oracle JET built-in
translation
// resources with a custom translation file.
// Any resource file added, must be placed under a directory named "nls". You can
use a path mapping or you can define
// a path that is relative to the location of this main.js file.
config: {
ojL10n: {
merge: {
//'ojtranslations/nls/ojtranslations': 'resources/nls/
myTranslations'
}
}
}
});
4-5
Chapter 4
Using RequireJS in an Oracle JET Application
/**
* A top-level require call executed by the Application.
* Although 'ojcore' and 'knockout' would be loaded in any case (they are specified
as dependencies
* by the modules themselves), we are listing them explicitly to get the references
to the 'oj' and 'ko'
* objects in the callback
*
* For a listing of which JET component modules are required for each component,
* see the specific component demo pages in the JET cookbook.
*/
require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'ojs/ojbutton',
'ojs/ojtoolbar', 'ojs/ojmenu'],
// add additional JET component libraries as needed
function(oj, ko, $) // this callback gets executed when all required modules
are loaded
{
// add any startup code that you want here
}
);
4-6
Chapter 4
Adding Third-Party Tools or Libraries to Your Oracle JET Application
Note:
You can also use RequireJS with ojModule to define view templates and
viewModels for page sections in a single-page application. For example,
the Oracle JET Starter Templates use the ojModule binding with
RequireJS to use a different view and viewModel when the user clicks
one of the navigation buttons.
For additional information about the Oracle JET Starter Templates, see
Create a Web Application Using the Oracle JET Starter Templates. For
more information about using ojModule and templates, see Creating
Single-Page Applications.
config: {
ojL10n: {
merge: {
'ojtranslations/nls/ojtranslations': 'resources/nls/myTranslations'
}
}
}
4-7
Chapter 4
Adding Third-Party Tools or Libraries to Your Oracle JET Application
Note:
This process is provided as a convenience for Oracle JET developers.
Oracle JET will not support the additional tools or libraries and cannot
guarantee that they will work correctly with other Oracle JET components or
toolkit features.
To add a third-party tool or library to your Oracle JET application, do one of the
following:
• If you created your application with command-line tooling, perform the following
steps.
1. In your main project directory, enter the following command in a terminal
window to install the library using npm:
npm install library-name --save
For example, enter the following command to install a library named my-
library:
"cdns": {
"jet": "https://static.oracle.com/cdn/jet/v6.0.0/default/js",
"3rdparty": "https://static.oracle.com/cdn/jet/v6.0.0/3rdparty"
},
"libs": {
"knockout": {
"cdn": "3rdparty",
"cwd": "node_modules/knockout/build/output",
"debug": {
"src": "knockout-latest.debug.js",
"path": "libs/knockout/knockout-#{version}.debug.js",
"cdnPath": "knockout/knockout-3.4.2"
},
"release": {
"src": "knockout-latest.js",
"path": "libs/knockout/knockout-#{version}.js",
"cdnPath": "knockout/knockout-3.4.2"
}
},
4-8
Chapter 4
Adding Third-Party Tools or Libraries to Your Oracle JET Application
"my-library": {
"cdn": "3rdparty",
"cwd": "node_modules/my-library/dist",
"debug": {
"src": "my-library.debug.js",
"path": "libs/my-library/my-library.debug.js",
"cdnPath": ""
},
"release": {
"src": "my-library.js",
"path": "libs/my-library/my-library.js",
"cdnPath": ""
}
},
...
In this example, cwd points to the location where npm installed the library,
src points to a path or array of paths containing the files that will be copied
during a build, and path points to the destination that will contain the built
version.
Note:
If you use a CDN, add the URL to the CDN in the entry for
cdnPath.
• If you didn’t use the tooling to create your application, perform the following steps
to add the tool or library.
1. In the application’s js/libs directory, create a new directory and add the new
library and any accompanying files to it.
For example, for a library named my-library, create the my-library directory
and add the my-library.js file and any needed files to it. Be sure to add the
minified version if available.
2. In your RequireJS bootstrap file, typically main.js, add a link to the new file in
the path mapping section and include the new library in the require()
definition.
For example, add the highlighted code below to your bootstrap file to use a
library named my-library.
requirejs.config({
// Path mappings for the logical module names
paths:
{
'knockout': 'libs/knockout/knockout-3.4.2',
'jquery': 'libs/jquery/jquery-3.3.1.min',
... contents omitted
'text': 'libs/require/text',
'my-library': 'libs/my-library/my-library
},
4-9
Chapter 4
Troubleshooting RequireJS in an Oracle JET Application
In this example, two partition bundles are defined: commonComponents and tabs.
RequireJS ships with its own Optimizer tool for creating partitions and minifying
JavaScript code. The tool is designed to be used at build time with a complete project
that is already configured to use RequireJS. It analyzes all static dependencies and
creates partitions out of modules that are always loaded together. The Oracle JET
team recommends that you use an optimizer to minimize the number of HTTP
requests needed to download the modules.
For additional information about the RequireJS Optimizer tool, see http://requirejs.org/
docs/optimization.html.
4-10
Chapter 4
Using JavaScript Partitions and RequireJS in an Oracle JET Application
4-11
5
Creating Single-Page Applications
Oracle JET includes the oj-module component and oj.Router framework class that you
can use to create single-page applications that simulate the look and feel of desktop
applications.
The oj-module component can work in conjunction with oj.Router , which provides
routing support for Oracle JET applications.
Topics:
• Typical Workflow for Creating Single-Page Applications in Oracle JET
• Designing Single-Page Applications Using Oracle JET
• Using ojModule Binding
To create a single-page application (SPA) in Oracle JET, refer to the typical workflow
described in the following table:
Topics:
• Understanding Oracle JET Support for Single-Page Applications
• Creating a Single-Page Application in Oracle JET
5-1
Chapter 5
Designing Single-Page Applications Using Oracle JET
When the user selects a toolbar item such as the Customers button, the new content
displays, and the URL changes to reflect the user's current location on the page.
If the user chooses several chapters to display, the browser remembers the previous
selections. When the user selects the browser's back function, the URL and page
content change to reflect the user's previous selection.
The router comes with two URL adapters. Each adapter defines how the URL is
formatted to represent the application state:
• urlPathAdapter formats the URL in path segments. Each segment is the
possessive router’s current state id separated by a '/' , like /customers.
• urlParamAdapter formats the URL using query parameters. Each parameter is the
router name and its current state id like ?root=customers.
5-2
Chapter 5
Designing Single-Page Applications Using Oracle JET
• Access to a server
• URL rewrite rules configured properly on the server
• Commented out the line in the main.js file: //oj.Router.defaults['urlAdapter'] =
new oj.Router.urlParamAdapter();
The default adapter for the router is the urlPathAdapter adapter. However, the Oracle
JET SPA-ojModule-ojRouter application uses the urlParamAdapter to ensure that it
can run in any server environment without modification to the server.
When routing a single-page application, the page doesn't reload from scratch but the
content of the page changes dynamically. In order to be part of the browser history
and provide bookmarkable content, the Oracle JET router emulates the act of
navigating using the HTML5 history push state feature. The router also controls the
URL to look like traditional page URLs. However, there are no resources at those
URLs, and you must set up the HTML server. This is done using a simple rule for a
rewrite engine, like mod rewrite module for Apache HTTP server or a rewrite filter like
UrlRewriteFilter for servlets.
In general, use query parameters when your application contains only a few views that
the user will bookmark and that are not associated with a complex state. Use path
segments to display simpler URLs, especially for nested paths such as customers/
cust/orders.
The Oracle JET Cookbook and Oracle JET sample applications use the Oracle JET
oj-module feature to manage the Knockout binding. With oj-module, you can store your
HTML content for a page section in an HTML fragment or template file and the
JavaScript functions that contain your viewModel in a viewModel file.
When oj-module and oj.Router are used in conjunction, you can configure an oj-module
object where the module name is the router state. When the router changes state, oj-
module will automatically load and render the content of the module name specified in
the value of the current RouterState object.
5-3
Chapter 5
Designing Single-Page Applications Using Oracle JET
examples that use routing with the oj-module component. Regardless of the routing
method you use, the process to create the application is similar.
To create a single-page application in Oracle JET:
If needed, create the application that will house your main HTML5 page and
supporting JavaScript. For additional information, see Getting Started with Oracle JET
Web Application Development or Get Started with Oracle JET Hybrid Mobile
Application Development.
1. Design the application's structure.
Identify the templates and ViewModels that your application will require. For
example, the SPA-ojModule-ojRouter single-page sample application defines the
HTML template for header and footer navigation items and uses oj-module in
conjunction with oj.Router to navigate through the content. The code sample
below shows the index.html file, with the router definition highlighted.
<!DOCTYPE html>
<!--
Copyright (c) 2014, 2018, Oracle and/or its affiliates.
The Universal Permissive License (UPL), Version 1.0
-->
<html lang="en-us">
<head>
<title>Oracle JET Samples - Home</title>
<meta http-equiv="x-ua-compatible" content="IE=edge"/>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- base href="/" /-->
<!-- This is the main css file for the default Alta theme -->
<link rel="stylesheet" href="../../jet/css/libs/oj/v6.0.0/alta/oj-alta-
min.css" type="text/css"/>
<!-- These style classes are for demonstration purposes only. -->
<link rel="stylesheet" href="css/demo-alta-patterns-min.css"/>
<!-- This is where you would add any app specific styling -->
<link rel="stylesheet" href="css/override.css" type="text/css"/>
</head>
<body class="oj-web-applayout-body">
<!-- Template for rendering navigation items shared between nav bar and nav
list -->
<script type="text/html" id="navTemplate">
<li><a href="#">
<span :class="[[$data.iconClass]]"></span>
<span><oj-bind-text value="[[$data.name]]"></oj-bind-text></span>
</a></li>
</script>
<div id="globalBody" class="oj-offcanvas-outer-wrapper oj-offcanvas-page">
<!--
** Oracle JET V6.0.0 web application navigation drawer pattern.
** Please see the demos under Cookbook/Patterns/App Shell: Web
5-4
Chapter 5
Designing Single-Page Applications Using Oracle JET
item.renderer="[[oj.KnockoutTemplateUtils.getRenderer('navTemplate', true)]]"
on-click="[[toggleDrawer]]"
selection="{{router.stateId}}">
</oj-navigation-list>
</div>
<div id="pageContent" class="oj-web-applayout-page">
<!--
** Oracle JET V6.0.0 web application header pattern.
** Please see the demos under Cookbook/Patterns/App Shell: Web
** and the CSS documentation under Support/API Docs/Non-Component
Styling
** on the JET website for more information on how to use this pattern.
-->
item.renderer="[[oj.KnockoutTemplateUtils.getRenderer('navTemplate', true)]]"
5-5
Chapter 5
Designing Single-Page Applications Using Oracle JET
on-oj-before-select="[[selectHandler]]"
selection="{{router.stateId}}">
</oj-navigation-list>
</div>
</header>
In this example, oj-module manages content replacement for the header, main
content, and footer sections of the body, and oj.Router provides routing support.
For additional information about ojModule, see Using ojModule Binding.
The application also contains content that displays when the user clicks
Customers and Admin in the application header. To manage the content for
those sections, the application includes the customers.html and admin.html view
templates with corresponding customers.js and admin.js ViewModels.
The image below shows a portion of the directory structure for the SPA-ojModule-
ojRouter single-page sample application. The ViewModel definitions are contained
in JavaScript files in the js/viewModels folder, and the view templates are
contained in HTML files in the js/views folder.
5-6
Chapter 5
Designing Single-Page Applications Using Oracle JET
2. Add code to your application's main script that defines the states that the router
can take, and add the ojs/ojrouter module to your require() list.
For example, in the SPA-ojModule-ojRouter sample application, the router can be
in the home, customers, or admin states. The code sample below shows the code that
configures the router.
require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojmodule-element-utils', 'ojs/
ojmodule-element',
'ojs/ojknockout', 'ojs/ojbutton', 'ojs/ojmenu', 'ojs/ojtoolbar', 'ojs/
ojnavigationlist',
'ojs/ojoffcanvas', 'ojs/ojarraytabledatasource', 'ojs/ojmodule', 'ojs/ojrouter',
'text', 'ojs/ojcheckboxset', 'ojs/ojswitch'],
function (oj, ko, $, moduleUtils)
5-7
Chapter 5
Designing Single-Page Applications Using Oracle JET
{
'use strict';
// Set debug mode and log level
// oj.Assert.forceDebug();
// oj.Logger.option('level', oj.Logger.LEVEL_INFO);
// Router setup
oj.Router.defaults['urlAdapter'] = new oj.Router.urlParamAdapter();
var router = oj.Router.rootInstance;
router.configure({
'home': {label: 'Home',
isDefault: true,
enter: function(){
console.log('entered Home');
}},
'customers': {label: 'Customers',
exit: function(){
console.log('exited customers');
}},
'admin': {label: 'Admin',
canEnter: function(){
return
ko.dataFor(document.getElementById('switch')).isAdmin();
}}
});
function ViewModel()
{
var self = this;
self.router = router;
//initialize oj-module config
self.moduleConfig = ko.observable({'view':[], 'viewModel':null});
//update module config every time as router.moduleConfig mutates
ko.computed(function() {
var name = self.router.moduleConfig.name();
var viewPath = 'views/' + name + '.html';
var modelPath = 'viewModels/' + name;
var masterPromise = Promise.all([
moduleUtils.createView({'viewPath':viewPath}),
moduleUtils.createViewModel({'viewModelPath':modelPath})
]);
masterPromise.then(
function(values){
var viewModel = typeof values[1] === 'function' ? new values[1]
(self.router) : values[1];
self.moduleConfig({'view':values[0],'viewModel':viewModel});
}
);
5-8
Chapter 5
Designing Single-Page Applications Using Oracle JET
});
// Navigation setup
var navData = [
{name: 'Home', id: 'home',
iconClass: 'oj-navigationlist-item-icon demo-icon-font-24 demo-
chart-icon-24'},
{name: 'Customers', id: 'customers',
iconClass: 'oj-navigationlist-item-icon demo-icon-font-24 demo-
people-icon-24'},
{name: 'Admin', id: 'admin',
iconClass: 'oj-navigationlist-item-icon demo-icon-font-24 demo-info-
icon-24'}
];
self.navDataSource = new oj.ArrayTableDataSource(navData,
{idAttribute: 'id'});
// Drawer
// Close offcanvas on medium and larger screens
self.mdScreen.subscribe(function()
{oj.OffcanvasUtils.close(self.drawerParams);});
self.drawerParams = {
displayMode: 'push',
selector: '#navDrawer',
content: '#pageContent'
};
// Called by navigation drawer toggle button and after selection of
nav drawer item
self.toggleDrawer = function() {
return oj.OffcanvasUtils.toggle(self.drawerParams);
}
// Add a close listener so we can move focus back to the toggle
button when the drawer closes
$("#navDrawer").on("ojclose", function() { $
('#drawerToggleButton').focus(); });
// Header
// Application Name used in Branding Area
self.appName = ko.observable("Router Demo");
// User Info used in Global Navigation area
self.userLogin = ko.observable("[email protected]");
self.isAdmin = ko.observable(false);
// Footer
function footerLink(name, id, linkTarget) {
this.name = name;
this.linkId = id;
this.linkTarget = linkTarget;
}
self.footerLinks = ko.observableArray([
5-9
Chapter 5
Designing Single-Page Applications Using Oracle JET
oj.Router.sync().then(
function ()
{
ko.applyBindings(new ViewModel(),
document.getElementById('globalBody'));
},
function (error)
{
oj.Logger.error('Error when starting router: ' +
error.message);
}
);
}
);
In this example, the router is configured for query parameters. To configure the
router for path segments, comment out the following line as shown below:
//oj.Router.defaults['urlAdapter'] = new oj.Router.urlParamAdapter();
To configure the router for path segments, you must also set the baseUrl property
of the requirejs.config. This provides the router with a base to calculate which
part belongs to the router state and which part is the existing URL.
requirejs.config({
// Need to set baseUrl or nested view won't work because module location
relative to current url.
// Change to the correct baseUrl when deployed to site like: http://host/myApp
baseUrl: window.location.href.split('#')[0].substring(0,
window.location.href.split('#')[0].lastIndexOf('/')) + '/js',
// Path mappings for the logical module names
paths: {
... contents omitted
});
5-10
Chapter 5
Designing Single-Page Applications Using Oracle JET
Note:
Routing with path segments also requires that the web server recognizes
the paths as existing resources and routes the requests to the root page.
If you're using the Apache web server, you can use the mod_rewrite
module to rewrite requested URLs on demand. For other web servers,
you can create a servlet filter or use one of the publicly available URL
rewrite filters. In addition to rewriting the URL, the base URL needs to be
set so that the router can retrieve the part of the URL representing the
state. If your application start at the context root and is using index.html,
nothing needs to be done because the default value of baseUrl is '/'. If
your application is nested in a directory named myApp, then the base URL
should be changed:
oj.Router.defaults['baseUrl'] = '/myApp/';
If the <base href> tag is set in your starting page, it is usually the same
value used for the router base URL.
3. Add code to the markup that triggers the state transition and displays the content
of the current state.
When the user clicks one of the buttons in the header, the content is loaded
according to the router's current state.
For additional information about creating templates and ViewModels, see Using
ojModule Binding.
4. To manage routing within a module, add a child router using
oj.Router.createChildRouter().
For example, when the user clicks Customers , the page displays a list of
customer names with IDs that the user can select. The user can move back and
forth between the customer details, and the child router maintains the state. The
HTML markup is stored in the Customers.html template and is shown below.
<oj-module id="customers" config="[[moduleConfig]]"></oj-module>
The template defines the Oracle JET oj-navigation-list component and uses the
router's state IDs as an argument to the oj-navigation-list’s selection attribute.
To display the customer list, the template iterates through each RouterState object
in the router.states property.
The code sample below shows the customer.js file that defines the child router in
this code sample to display customers (either a list or details of a single customer)
and to display orders of a specific customer. custRouter is a child of the parent
router (params). The state of the child router, the parameters passed with it, along
with the parent router determines the content of the page region.
define(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojmodule-element-utils', 'ojs/
ojmodule-element','ojs/ojknockout', 'ojs/ojrouter'],
function(oj, ko, $, moduleUtils)
{
'use strict';
var custArray = [
{id:7369, name: 'Smith Brothers', market: 'Dairy', sales: 800,
deptno: 20, orders: [
{id:100, name:'A'},
5-11
Chapter 5
Designing Single-Page Applications Using Oracle JET
{id:110, name:'B'},
{id:120, name:'C'}] },
{id:7499, name: 'Allen Furniture', market: 'Home Furnishings',
sales: 1600, deptno: 30, orders: [
{id:101, name:'A'},
{id:102, name:'C'}] },
{id:7521, name: 'Ward and Roebuck', market: 'Home Appliances',
sales: 1250, deptno: 30, orders: [
{id:120, name:'A'},
{id:121, name:'F'},
{id:122, name:'G'}] },
{id:7566, name: 'Jones Brothers', market: 'Reality', sales: 2975,
deptno: 20, orders: [
{id:130, name:'B'},
{id:131, name:'C'},
{id:132, name:'D'}] },
{id:7654, name: 'Martin Marina', market: 'Water Sports', sales:
1250, deptno: 30, orders: [
{id:140, name:'C'},
{id:141, name:'B'}] },
{id:7698, name: 'Blake and Sons', market: 'Accounting', sales: 2850,
deptno: 30, orders: [
{id:150, name:'E'},
{id:151, name:'G'}] },
{id:7782, name: 'Clark Candies', market: 'Confectionaries', sales:
2450, deptno: 10, orders: [
{id:160, name:'B'},
{id:161, name:'E'}] },
{id:7788, name: 'Scott Lawn Service', market: 'Gardening Supplies',
sales: 3000, deptno: 20, orders: [
{id:170, name:'C'},
{id:171, name:'D'}] },
{id:7839, name: 'King Power', market: 'Champion Builders', sales:
5000, deptno: 10, orders: [
{id:180, name:'A'},
{id:181, name:'F'},
{id:182, name:'C'}] }];
function viewModel(parentRouter)
{
var routerConfig =
{
'list': { isDefault: true }
};
// Populate the router config object with all the items from the table
custArray.forEach(function(item)
{
var id = item.id.toString();
// Configure the router state with a state parameter, in the form of
// "1234/{orderId}"
// "orderId" is the name we'll use to retrieve the value passed to
// custRouter further in our moduleConfig.
routerConfig[id+'/{orderId}'] = { label: item.name, value: item };
});
5-12
Chapter 5
Designing Single-Page Applications Using Oracle JET
// This is the main logic to switch the module based on both router states.
ko.computed(function() {
// create subscription to changes in states for both routers
var customerState = self.custRouter.currentState();
var orderId = customerState && customerState.parameters.orderId;
if(initial) {
// nothing to load on inital call
initial = false;
}
else {
// update module config for oj-module on customers page
var moduleName, viewModelParams = {};
if (orderId) {
viewModelParams['parentRouter'] = self.custRouter;
viewModelParams['data'] = orderId === 'orders' ?
self.custRouter.currentValue.peek().orders : orderId;
moduleName = orderId === 'orders' ? "listOrder" : "viewOrder";
}
else if (customerState && customerState.id === 'list') {
moduleName = self.custRouter.moduleConfig.name();
viewModelParams['data'] = custArray; // Pass a reference of custArray
to the child module to render the list
viewModelParams['parentRouter'] = self.custRouter;
}
else {
moduleName = "view";
viewModelParams['data'] = self.custRouter.currentValue.peek() || {};
viewModelParams['parentRouter'] = self.custRouter;
}
// load view and view model and update the moduleConfig for 'customers'
var viewPath = 'views/' + moduleName + '.html';
var modelPath = 'viewModels/' + moduleName;
var masterPromise = Promise.all([
moduleUtils.createView({'viewPath':viewPath}),
moduleUtils.createViewModel({'viewModelPath':modelPath})
]);
masterPromise.then(
function(values){
var viewModel = new values[1](viewModelParams);
self.moduleConfig({'view':values[0],'viewModel':viewModel});
}
);
}
});
self.connected = function() {
oj.Router.sync().then(
null,
function(error) {
oj.Logger.error('Error during refresh: ' + error.message);
}
);
};
self.disconnected = function() {
// Every router is destroyed on dispose.
self.custRouter.dispose();
5-13
Chapter 5
Using the oj-module Element
};
}
Note:
In this example, the oj-module connected lifecycle callback plays an
important role in making the page work. oj.Router.sync() synchronizes
the new router state with the current URL. The synchronization gives the
custRouter child router a chance to transition to its default state, which is
list in this example.
5-14
Chapter 5
Using the oj-module Element
Note:
The Customers tab also uses templates to display details of a single
customer, list of customers, orders for a customer, and details of a specific
order.
The home view template is shown below. In the highlighted code, the first oj-bind-text
element sets the h2 element's content to a text node containing the value of title. The
oj-bind-for-each element iterates through the provided data, which contains the list
items defined in the second oj-bind-text.
<div class="oj-flex oj-flex-items-pad">
<div class="oj-xl-9 oj-lg-9 oj-md-9 oj-sm-12 oj-flex-item">
<div role="main" class="oj-panel demo-page-content-area">
<h2><oj-bind-text value="[[title]]"></oj-bind-text></h2>
<oj-bind-for-each data="[[description]]">
<template>
<p><oj-bind-text value="[[$current.data.line]]"></oj-bind-text></p>
</template>
</oj-bind-for-each>
</div>
</div>
<div class="oj-xl-3 oj-lg-3 oj-md-3 oj-sm-12 oj-flex-item">
<div class="oj-panel oj-panel-alt2 demo-page-content-area">
<p>Click on the navigation buttons to switch which component is displayed in
this page. </p>
<p>To enter the Admin page, you must turn on the switch that says you are the
Admin. This is an example of the canEnter method and is defined in the router
configuration.</p>
</div>
</div>
</div>
5-15
Chapter 5
Using the oj-module Element
The code that defines the title and line content is contained in the corresponding
home.js viewModel file.
define(['knockout'],
function(ko){
var viewModel = {
title: 'Responsive Web Application with router',
description:[{
line: 'This application demonstrates the Responsive Web Application pattern
using the \
JET router for navigation.'
},{
line: 'This text is from the home module.'
}]
};
return viewModel;
});
If needed, create the application that will house your main HTML5 page and
supporting JavaScript. See Getting Started with Oracle JET Web Application
Development. Oracle JET applications are built with default views and viewModels
folders under application_folder/src/js.
1. Optionally, you can modify the default location of the application’s view templates
and viewModel scripts. Add code to the main script that defines the locations and
suffix for the view templates. If you modify this, you will need to create the
appropriate folders.
The following code sample shows the loadModule method from the
appController.js file that defines the paths and locations used in the app.
self.loadModule = function() {
ko.computed(function() {
var name = self.router.moduleConfig.name();
var viewPath = 'views/' + name + '.html';
var modelPath = 'viewModels/' + name;
var masterPromise = Promise.all([
moduleUtils.createView({'viewPath':viewPath}),
moduleUtils.createViewModel({'viewModelPath':modelPath})
]);
masterPromise.then(
function(values){
self.moduleConfig({'view':values[0],'viewModel':values[1]});
},
function(reason){}
);
});
};
2. In your RequireJS bootstrap file (typically main.js) add ojs/ojmodule-element to the
list of RequireJS modules, along with any other modules that your application
uses.
require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojmodule-element-utils', 'ojs/
ojmodule-element',
'ojs/ojknockout', 'ojs/ojbutton', 'ojs/ojmenu', 'ojs/ojtoolbar', 'ojs/
ojnavigationlist',
'ojs/ojoffcanvas', 'ojs/ojarraytabledatasource', 'ojs/ojmodule', 'ojs/ojrouter',
'text', 'ojs/ojcheckboxset', 'ojs/ojswitch']
5-16
Chapter 5
Using the oj-module Element
3. Create your view templates and add them to the viewPath folder.
4. Create your viewModel scripts and add them to the modelPath folder.
5. Add code to the application's HTML page to reference the view template or
viewModel in the oj-module element. Use the config attribute to set a Knockout
observable with the module configurations
<oj-module role="main" class="oj-panel" style="padding-bottom:30px"
config="[[moduleConfig]]"></oj-module>
For more information about oj.Router and oj-module, see the Oracle JET oj.Router and
oj-module API documentation.
Tip:
oj-module is not specific to single-page applications, and you can also use it
to reuse content in multi-page applications. However, if you plan to reuse or
share your content across multiple applications, consider creating Oracle
JET Web Components instead. Web Components are reusable components
that follow the HTML5 Web Component specification. They have the
following benefits:
• Web Components have a contract. The API for a Web Component is
well defined by its component.json, which describes its supported
properties, methods, and events in a standard, universal, and self-
documenting way. Providing a standardized contract makes it easier for
external tools or other applications to consume these components.
• Web Components include version and dependency metadata, making it
clear which versions of Oracle JET they support and what other
components they may require for operation.
• Web Components are self-contained. A Web Component definition can
contain all the libraries, styles, images, and translations that it needs to
work.
To learn more about Web Component features, see Working with Oracle JET
Web Components.
5-17
Chapter 5
Using the oj-module Element
You can also find stub methods for using the oj-module lifecycle methods in some of
the Oracle JET templates. For example, the navbar template, available as a template
when you Scaffold a Web Application with the Oracle JET CLI, defines stub methods
for connected(), disconnected(), and transitionCompleted(). Comments describe the
expected parameters and use cases.
function DashboardViewModel() {
var self = this;
// Below are a set of the ViewModel methods invoked by the oj-module component.
// Please reference the oj-module jsDoc for additional information.
/**
* Optional ViewModel method invoked after the View is inserted into the
* document DOM. The application can put logic that requires the DOM being
* attached here.
* This method might be called multiple times - after the View is created
* and inserted into the DOM and after the View is reconnected
* after being disconnected.
*/
self.connected = function() {
// Implement if needed
};
/**
* Optional ViewModel method invoked after the View is disconnected from the
DOM.
*/
self.disconnected = function() {
// Implement if needed
};
/**
* Optional ViewModel method invoked after transition to the new View is
5-18
Chapter 5
Using ojModule Binding
complete.
* That includes any possible animation between the old and the new View.
*/
self.transitionCompleted = function() {
// Implement if needed
};
}
/*
* Returns a constructor for the ViewModel so that the ViewModel is constructed
* each time the view is displayed. Return an instance of the ViewModel if
* only one instance of the ViewModel is needed.
*/
return new DashboardViewModel();
}
Note:
The Customers tab also uses templates to display details of a single
customer, list of customers, orders for a customer, and details of a specific
order.
The home view template is shown below. In the highlighted code, the first data-bind
definition uses Knockout's text binding to set the h2 element's content to a text node
5-19
Chapter 5
Using ojModule Binding
containing the value of title. The second data-bind definition uses Knockout's foreach
binding to iterate through the content, and the list items display the text defined in line.
<div class="oj-flex oj-flex-items-pad">
<div class="oj-xl-9 oj-lg-9 oj-md-9 oj-sm-12 oj-flex-item">
<div role="main" class="oj-panel demo-page-content-area">
<h2 data-bind="text: title"></h2>
<!-- ko foreach: description -->
<p data-bind="text: line"></p>
<!-- /ko -->
</div>
</div>
<div class="oj-xl-3 oj-lg-3 oj-md-3 oj-sm-12 oj-flex-item">
<div class="oj-panel oj-panel-alt2 demo-page-content-area">
<p>Click on the navigation buttons to switch which component is displayed in
this page.</p>
<p>To enter the Admin page, you must turn on the switch that says you are
the Admin.
This is an example of the canEnter method and is defined in the router
configuration.</p>
</div>
</div>
</div>
The code that defines the title and line is contained in the viewModel for the home
section's content, stored in the home.js viewModel.
/* Home content module, example of singleton view model object. */
define(['knockout'], function(ko) {
var viewModel = {
title: 'Responsive Web Application with router',
description:[{
line: 'This application demonstrates the Responsive Web Application
pattern using the \
JET router for navigation.'
},{
line: 'This text is from the home module.'
}]
};
return viewModel;
});
If needed, create the application that will house your main HTML5 page and
supporting JavaScript. For additional information, see Getting Started with Oracle JET
Web Application Development.
1. To modify the default location of the application’s view templates and viewModel
scripts, add code to the application's main script that initializes the location of the
viewModels and view templates and defines the suffix for the view templates.
The ojModule binding namespace contains the default location and names of
managed view templates and viewModels.
Property Description
viewPath Defines the path to the view templates. Defaults to text!views/
viewSuffix Defines the suffix for view templates. Defaults to .html.
5-20
Chapter 5
Using ojModule Binding
Property Description
modelPath Defines the location of the viewModel scripts. Defaults to
viewModels/
6. As needed, create your viewModel scripts and add them to the modelPath folder.
Use the same name for the module that you used for the template in Step 4. For
example, if your template is named customers.html, use customers.js for the
viewModel's name.
7. Add code to the application's HTML page to reference the view template or
viewModel in the ojModule binding.
If the page section contains only a view template, use ojModule with the viewName
option:
<header id="headerWrapper" role="banner" data-bind="ojModule: { viewName:
'sampleView'}"></div>
To reference a viewModel with a view template, use ojModule with the name option:
<header id="headerWrapper" role="banner" data-bind="ojModule: { name:
'sampleViewModel'}"></div>
For more information about oj.Router and ojModule, see the Oracle JET oj.Router and
ojModule API documentation.
5-21
Chapter 5
Using ojModule Binding
Tip:
ojModule is not specific to single-page applications, and you can also use it to
reuse content in multi-page applications. However, if you plan to reuse or
share your content across multiple applications, consider creating Oracle
JET Web Components instead of using ojModule. Oracle JET Web
Components also contain reusable pieces of user interface code, and they
offer the following additional features to facilitate sharing and reuse.
• Web Components have a contract. The API for a Web Component is
well defined by its component.json, which describes its supported
properties, methods, and events in a standard, universal, and self-
documenting way. Providing a standardized contract makes it easier for
external tools or other applications to consume Web Components.
• Web Components include version and dependency metadata, making it
clear which versions of Oracle JET they support and what other
components they may require for operation.
• Web Components are self-contained. A Web Component definition can
contain all the libraries, styles, images, and translations that it needs to
work.
To learn more about Web Component features, see Working with Oracle JET
Web Components.
5-22
Chapter 5
Using ojModule Binding
You can also find stub methods for using the ojModule lifecycle methods in some of the
Oracle JET templates. For example, the navbar template, available as a template
when you Scaffold a Hybrid Mobile Application with the Oracle JET CLI, defines stub
methods for handleActivated(), handleAttached(), handleBindingsApplied(), and
handleDetached(). Comments describe the expected parameters and use cases.
function DashboardViewModel() {
var self = this;
// Below are a subset of the ViewModel methods invoked by the ojModule binding
// Please reference the ojModule jsDoc for additional available methods.
/**
* Optional ViewModel method invoked when this ViewModel is about to be
* used for the View transition. The application can put data fetch logic
* here that can return a Promise which will delay the handleAttached function
* call below until the Promise is resolved.
* @param {Object} info - An object with the following key-value pairs:
* @param {Node} info.element - DOM element or where the binding is attached. This
may be a 'virtual' element (comment node).
* @param {Function} info.valueAccessor - The binding's value accessor.
* @return {Promise|undefined} - If the callback returns a Promise, the next phase
(attaching DOM) will be delayed until
* the promise is resolved
*/
self.handleActivated = function(info) {
// Implement if needed
};
/**
* Optional ViewModel method invoked after the View is inserted into the
* document DOM. The application can put logic that requires the DOM being
* attached here.
* @param {Object} info - An object with the following key-value pairs:
* @param {Node} info.element - DOM element or where the binding is attached. This
may be a 'virtual' element (comment node).
* @param {Function} info.valueAccessor - The binding's value accessor.
* @param {boolean} info.fromCache - A boolean indicating whether the module was
retrieved from cache.
*/
self.handleAttached = function(info) {
// Implement if needed
};
/**
* Optional ViewModel method invoked after the bindings are applied on this View.
* If the current View is retrieved from cache, the bindings will not be re-applied
* and this callback will not be invoked.
* @param {Object} info - An object with the following key-value pairs:
* @param {Node} info.element - DOM element or where the binding is attached. This
may be a 'virtual' element (comment node).
* @param {Function} info.valueAccessor - The binding's value accessor.
5-23
Chapter 5
Using ojModule Binding
*/
self.handleBindingsApplied = function(info) {
// Implement if needed
};
/*
* Optional ViewModel method invoked after the View is removed from the
* document DOM.
* @param {Object} info - An object with the following key-value pairs:
* @param {Node} info.element - DOM element or where the binding is attached. This
may be a 'virtual' element (comment node).
* @param {Function} info.valueAccessor - The binding's value accessor.
* @param {Array} info.cachedNodes - An Array containing cached nodes for the View
if the cache is enabled.
*/
self.handleDetached = function(info) {
// Implement if needed
};
}
5-24
6
Understanding Oracle JET User Interface
Basics
Oracle JET User Interface (UI) components extend the htmlElement prototype to
implement the World Wide Web Consortium (W3C) web component specification for
custom elements. Custom elements provide a more declarative way of working with
Oracle JET components and allow you to access properties and methods directly on
the DOM layer.
Topics:
• Typical Workflow for Working with the Oracle JET User Interface
• About the Oracle JET User Interface
• About Binding and Control Flow
• Adding an Oracle JET Custom Element to Your Page
6-1
Chapter 6
About the Oracle JET User Interface
Topics:
• Identifying Oracle JET UI Components, Patterns, and Utilities
• Common Functionality in Oracle JET Components
• Oracle JET Reserved Namespaces and Prefixes
6-2
Chapter 6
About the Oracle JET User Interface
The {{...}} syntax indicates that the element’s value property will be updated in
the element’s ViewModel if it’s changed. To prevent the attribute’s value from
updating the corresponding ViewModel, use the [[...]] syntax.
• Methods: Supported methods
Each custom element’s supported method is documented in its API.
• Events: Supported events
Events specific to the custom element are documented in its API. Define the
listener’s method in the element’s ViewModel.
var listener = function( event )
{
// Check if this is the end of "inline-open" animation for inline message
if (event.detail.action == "inline-open") {
// Add any processing here
}
};
Reference the listener using the custom element’s DOM attribute, JavaScript
property, or the addEventListener().
– Use the DOM attribute.
Declare a listener on the custom element using on-event-name syntax.
<oj-input-date-time on-oj-animate-start='[[listener]]'</oj-input-date-time>
Note that in this example the listener is declared using the [[...]] syntax
since its value is not expected to change.
– Use the JavaScript property.
Specify a listener in your ViewModel for the .onEventName property.
myInputDateTime.onOjAnimateEnd = listener
Note that the JavaScript property uses camelCase for the onOjAnimateEnd
property. The camelCased properties are mapped to attribute names by
inserting a dash before the uppercase letter and converting that letter to lower
case, for example, on-oj-animate-end.
– Use the addEventListener() API.
myInputDateTime.addEventListener('ojAnimateEnd', listener);
By default, JET components will also fire propertyChanged custom events whenever
a property is updated, for example, valueChanged. You can define and add a
listener using any of the three methods above. When referencing a
propertyChanged event declaratively, use on-property-changed syntax.
6-3
Chapter 6
About the Oracle JET User Interface
<oj-table>
<div slot='bottom'<oj-paging-control></oj-paging-control></div>
</oj-table>
– Any child element lacking a slot attribute will be moved to the default slot, also
known as a regular child.
A custom element will be recognized only after its module is loaded by the application.
Once the element is recognized, Oracle JET will register a busy state for the element
and will begin the process of upgrading the element from a normal element to a
custom element. The element will not be ready for interaction until the upgrade
process is complete. The application should listen to either the page-level or element-
scoped oj.BusyContext before attempting to interact with any JET custom elements.
However, property setting (but not property getting) is allowed before the
oj.BusyContext is initialized. See the oj.BusyContext API documentation on how
BusyContexts can be scoped.
The upgrade of custom elements relies on a binding provider which manages the data
binding. The binding provider is responsible for setting and updating attribute
expressions. Any custom elements within its managed subtree will not finish upgrading
until the provider 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 that its dependent JET custom elements do not
wait for bindings to be applied to finish upgrading.
Oracle JET custom elements also have the following functionality in common:
• Context menus
Custom elements support the slot attribute to add context menus to Oracle JET
custom elements, described in each element’s API documentation.
<oj-some-element>
<-- use the contextMenu slot to designate this as the context menu for this
component -->
<oj-menu slot="contextMenu" style="display:none" aria-label="Some element's
context menu"
...
</oj-menu>
</oj-some-element>
• Keyboard navigation and other accessibility features
Oracle JET components that support keyboard navigation list the end user
information in their API documentation. For additional information about Oracle
JET components and accessibility, see Developing Accessible Applications.
• Drag and drop
Oracle JET includes support for standard HTML5 drag and drop and provides the
dnd-polyfill library to extend HTML5 drag and drop behavior to supported mobile
and desktop browsers. In addition, some Oracle JET custom elements such as oj-
table support drag and drop behavior through the dnd attribute. For specific details,
see the component’s API documentation and cookbook examples. To learn more
6-4
Chapter 6
About Binding and Control Flow
Add the deferred content to the application’s view and ViewModel, content.html
and content.js, as specified in the oj-module definition. For the complete code
example, see Collapsibles - Deferred Rendering.
For a list of custom elements that support oj-defer, see oj-defer.
Topics
• Using oj-bind-text to Bind Text Nodes
• Binding HTML attributes
• Using oj-bind-if to Process Conditionals
• Using oj-bind-for-each to Process Loop Instructions
• Binding Style Properties
• Binding Event Listeners to JET and HTML Elements
6-5
Chapter 6
About Binding and Control Flow
The oj-bind-text element is removed from the DOM after binding is applied. For
example, the following code sample shows an oj-input-text and an oj-button with a
text node that are both bound to the buttonLabel variable. When the input text is
updated, the button text is automatically updated as well.
<div id='button-container'>
<oj-button id='button1'>
<span><oj-bind-text value="[[buttonLabel]]"></oj-bind-text></span>
</oj-button>
<br><br>
<oj-label for="text-input">Update Button Label:</oj-label>
<oj-input-text id="text-input" value="{{buttonLabel}}"></oj-input-text>
</div>
The script to create the view model for this example is shown below.
require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'ojs/ojbutton', 'ojs/
ojinputtext', 'ojs/ojlabel'],
function(oj, ko, $)
{
function ButtonModel() {
this.buttonLabel = ko.observable("My Button");
}
$(function(){
ko.applyBindings(new ButtonModel(), document.getElementById('button-container'));
});
});
The figure below shows the output for the code sample.
6-6
Chapter 6
About Binding and Control Flow
The Oracle JET Cookbook contains the complete example used in this section. See
Text Binding.
To use an HTML attribute in an HTML or JET element, prefix a colon to it. JET
component-specific attributes do not need the prefix.
The following code sample shows two JET elements and two HTML elements that use
both the prefixed and non-prefixed syntax. Since the label and input elements are
native HTML elements, all of their data bound attributes should use the colon prefixing.
The oj-label and oj-input-text use the prefix only for native HTML element attributes
and the non-prefixed syntax for component-specific attributes.
<div id="demo-container">
<oj-label for="[[inputId1]]">oj-input-text element</oj-label>
<oj-input-text :id="[[inputId1]]" value="{{value}}"></oj-input-text>
<br><br>
<label :for="[[inputId2]]">HTML input element</label>
<br>
<input :id="[[inputId2]]" :value="[[value]]" style="width:100%;max-width:18em"/>
</div>
The script to create the view model for this example is shown below.
require(['jquery', 'knockout', 'ojs/ojinputtext', 'ojs/ojlabel', 'ojs/ojknockout'],
function($, ko)
{
function ViewModel()
{
this.inputId1 = 'text-input1';
this.inputId2 = 'text-input2';
this.value = "This text value is bound.";
}
$(function()
{
ko.applyBindings(new ViewModel(), document.getElementById('demo-container'));
});
});
The figure below shows the output for the code sample.
6-7
Chapter 6
About Binding and Control Flow
The Oracle JET Cookbook contains the complete example used in this section. See
Attribute Binding.
The oj-bind-if element is removed from the DOM after binding is applied, and must
be wrapped in another element such as a div if it is used for slotting. The slot attribute
has to be applied to the wrapper since oj-bind-if does not support it. For example, the
following code sample shows an image that is conditionally rendered based on the
option chosen in an oj-buttonset.
<div id="demo-container">
<oj-buttonset-one class="oj-buttonset-width-auto" value="{{buttonValue}}">
<oj-option id="onOption" value="on">On</oj-option>
<oj-option id="offOption" value="off">Off</oj-option>
</oj-buttonset-one>
<br><br>
<div>Image will be rendered if the button is on:</div>
<oj-bind-if test="[[buttonValue() === 'on']]">
<oj-avatar role="img" aria-label="Avatar of Amy Bartlet" size="md" initials="AB"
src="images/composites/avatar-image.jpg" class="oj-avatar-image">
</oj-avatar>
</oj-bind-if>
</div>
In the above example, the oj-avatar element is an icon which can display a custom or
placeholder image. See oj-avatar.
The script to create the view model for this example is shown below.
require(['jquery', 'knockout', 'ojs/ojknockout', 'ojs/ojbutton', 'ojs/ojavatar'],
function($, ko)
{
function ViewModel()
{
this.buttonValue = ko.observable("off");
}
$(function()
{
ko.applyBindings(new ViewModel(), document.getElementById('demo-container'));
});
});
The figure below shows the output for the code sample. When the oj-buttonset is set
to 'on', the oj-avatar element is rendered and displayed.
6-8
Chapter 6
About Binding and Control Flow
The Oracle JET Cookbook contains the complete example used in this section. See If
Binding.
The oj-bind-for-each element only accepts a single template element as its direct
child. Any markup to be duplicated, such as li tags, must be placed inside the
template tag. For example, the following code sample shows an unordered list nested
inside another unordered list. The list items are created using an oj-bind-text tag
inside nested oj-bind-for-each elements.
<div id="form-container">
<ul>
<oj-bind-for-each data="[[categories]]" as="category">
<template><li>
<ul>
<oj-bind-for-each data="[[category.data.items]]" as="item">
<template><li>
<oj-bind-text value="[[category.data.name + ' : ' +
item.data]]"></oj-bind-text>
</li></template>
</oj-bind-for-each>
</ul>
</li></template>
</oj-bind-for-each>
</ul>
</div>
In the above example, the as attribute provides an alias for the bound data. This alias
is referenced in the nested oj-bind-for-each and oj-bind-text elements.
The script to create the view model for this example is shown below.
6-9
Chapter 6
About Binding and Control Flow
The figure below shows the output for the code sample.
The Oracle JET Cookbook contains the complete example used in this section. See
Foreach Binding.
The script to create the view model for this example is shown below. The style object
referenced above is highlighted below.
6-10
Chapter 6
About Binding and Control Flow
$(function()
{
ko.applyBindings(new ViewModel(), document.getElementById('demo-container'));
});
});
The figure below shows the output for the code sample.
The Oracle JET Cookbook contains the complete example used in this section. See
Style Binding.
• bindingContext: The entire data binding context (or scope) that is applied to the
element.
Second, they have access to the model state and can access functions defined in the
ViewModel using the data and bindingContext parameters.
Note:
The this context is not directly available in the event listeners. This is the
same behavior as native HTML event listeners.
For example, the following code sample shows an oj-button that uses the on-oj-
action event attribute and an HTML button that uses the on-click event attribute to
access custom functions defined in the ViewModel shown below.
6-11
Chapter 6
About Binding and Control Flow
<div id="demo-container">
<oj-label for="button1">oj-button element</oj-label>
<oj-button id="button1" on-oj-action="[[clickListener1]]">Click me!</oj-button>
<br><br>
<label for="button2">HTML button element</label>
<br>
<button id="button2" on-click="[[clickListener2]]">Click me!</button>
<br><br>
<div style="font-weight:bold;color:#ea5b3f;">
<oj-bind-text value="[[message]]"></oj-bind-text>
</div>
</div>
Note:
HTML events use the prefix “on”, such as onclick and onload. JET events
use the prefix “on-”, such as on-click and on-load.
The script to create the view model for this example is shown below. Note the usage of
the data attribute to access the message parameter.
require(['jquery', 'knockout', 'ojs/ojbutton', 'ojs/ojlabel', 'ojs/ojknockout'],
function($, ko)
{
function ViewModel()
{
this.message = ko.observable();
$(function()
{
ko.applyBindings(new ViewModel(), document.getElementById('demo-container'));
});
});
The figure below shows the output for the code sample.
6-12
Chapter 6
About Binding and Control Flow
The Oracle JET Cookbook contains the complete example used in this section. See
Event Binding.
Binding Classes
The Oracle JET attribute binding syntax has enhanced support for the class attribute,
and can accept in addition to a string, an object or an array. This can be used to set
classes on components. The ojknockout module must be imported.
The :class attribute binding can support expressions that resolve to a space delimited
string, an Array of class names, or an Object of class to a boolean value or expression
for toggling the class in the DOM. Object values can be used to toggle classes on and
off. Array and string values can be used only to set classes.
For example, the following code sample shows an oj-input-text and an HTML input
that both use :class.
<oj-input-text id="input1"
:class="[[{'oj-form-control-text-align-right': alignRight}]]"
value="Text Content"></oj-input-text>
<oj-button id="button2" on-oj-action="[[clickListener2]]">Toggle Alignment</oj-
button>
The script to create the view model for this example is shown below.
6-13
Chapter 6
About Binding and Control Flow
this.clickListener1 = function(event) {
var newClass = classList.pop();
self.classArrayObs.push(newClass);
this.clickListener2 = function(event) {
self.alignRight(!self.alignRight());
};
}
$(function()
{
ko.applyBindings(new ViewModel(), document.getElementById('demo-container'));
});
});
The CSS class styles used by the classList variable above are shown below.
.bold {
font-weight: bold;
}
.italic {
font-style: italic;
}
.pink {
color: #ff69b4;
}
The figure below shows the first of the two outputs for the code sample. The button
acts as a toggle to switch on and off the oj-form-control-text-align-right class
property and hence change the alignment of the text.
6-14
Chapter 6
About Binding and Control Flow
The figure below shows the second of the two outputs for the code sample. The button
calls a function to take a pre-defined array of classes and add them to the input
element. Each class has CSS modifications that come into effect when the class is
added.
6-15
Chapter 6
Adding an Oracle JET Custom Element to Your Page
The Oracle JET Cookbook contains the complete example used in this section. See
Class Binding.
<div id="div1">
<oj-label for="dateTime">Default</oj-label>
<oj-input-date-time id="dateTime" value='{{value}}'>
</oj-input-date-time>
<br/><br/>
</div>
In this example, the oj-input-date-time element is declared with its value attribute
using {{...}} expression syntax, which indicates that changes to the value will
also update the corresponding value in the ViewModel. Each Oracle JET custom
element includes additional attributes that are defined in the custom element's API
documentation.
3. Use the Oracle JET Cookbook for example scripts and the syntax to use for
adding the custom element’s Require module and ViewModel to your RequireJS
bootstrap file or module.
For example, the basic demo for oj-input-date-time includes the following script
that you can use in your application.
require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'ojs/
ojdatetimepicker'],
function (oj, ko, $)
{
function SimpleModel()
{
this.value = ko.observable(oj.IntlConverterUtils.dateToLocalIso(new
Date(2013, 0, 1)));
6-16
Chapter 6
Adding an Oracle JET Custom Element to Your Page
$(document).ready(function ()
{
ko.applyBindings(new SimpleModel(), document.getElementById('div1'));
});
});
Important:
If you’re working with a Starter Template or define modules for different
sections of your page, you can’t simply copy and paste the cookbook
example. The Cookbook uses a require() call to load and use the
needed libraries in a single bootstrap file. The starter file that you are
pasting into is a RequireJS module which uses define() instead to
create a module that can be used by other parts of your application. For
additional details, see Modifying Starter Template Content.
If you already have a RequireJS bootstrap file or module, compare your file with
the Cookbook sample and merge in the differences. For details about working with
RequireJS, see Using RequireJS for Modular Development.
6-17
Chapter 6
Adding an Oracle JET Custom Element to Your Page
• pushRight: The new view pushes the old view out to the right.
• revealDown: The old view slides down to reveal the new view.
• revealEnd: The new view slides left or right to reveal the new view, depending on
the locale.
• zoomIn: The new view zooms in.
For examples that illustrate how to add animation with the oj-module component, see
Animation Effects with Module Component.
• fadeIn and fadeOut: Use for fading the element into and out of view.
• flipIn and flipOut: Use for rotating the element in and out of view.
• slideIn and slideOut: Use for sliding the element into and out of view.
• zoomIn and zoomOut: Using for zooming the element into and out of view.
Depending on the method’s options, you can configure properties like delay, duration,
and direction. For examples that illustrate how to configure animation using the
oj.AnimationUtils namespace, see Animation Effects.
6-18
7
Working with Oracle JET User Interface
Components
Oracle JET provides a variety of user interface (UI) components that you can configure
for use in your application. The Oracle JET Cookbook includes examples for working
with collections, controls, forms, visualizations, and other features. You may also find
this collection of tips and tricks helpful.
Topics
• Typical Workflow for Working with Oracle JET UI Components
• Working with Collections
• Working with Controls
• Working with Forms
• Working with Layout and Navigation
• Working with Visualizations
7-1
Chapter 7
Working with Collections
Topics:
• Choosing a Table, Data Grid, or List View
• Working with Data Grids
• Working with List Views
• Working with Pagination
• Working with Row Expanders
• Working with Tables
• Working with Tree Views
Note:
In past releases, Oracle JET collection-based components, such as oj-Table
and oj-List-View, relied exclusively on the DataSource API for fetching data.
Beginning with release 4.1.0, a new data fetching implementation based on
Oracle JET DataProvider APIs has replaced the DataSource API for certain
components. Applications that rely on a component's DataSource
implementation will continue to work without change. However, developers
who want to work with the component-specific DataProvider implementations
will find additional built-in support for data operations, specific to the
component. In this guide, samples will be updated to use DataProvider APIs
as they become available.
7-2
Chapter 7
Working with Collections
7-3
Chapter 7
Working with Collections
Note:
True virtual scrolling is available as a feature of oj-data-grid. Modern design
principles should be considered and implemented before implementing
virtual scrolling. It is much more desirable to present the end user with a
filtered list of data that will be more useful to them, than to display thousands
of rows of data and expect them to scroll through it all. True virtual scrolling
means that you can perform scrolling in both horizontal and vertical
directions with data being added and removed from the underlying DOM.
High water mark scrolling (with lazy loading) is the preferred method of
scrolling, and you should use it as a first approach.
7-4
Chapter 7
Working with Collections
accessibility. You can configure the oj-data-grid for cell selection with row and column
headers or for row selection with column headers.
Note:
When you configure the data grid for row selection, the grid has a look and
feel that is similar to the oj-table component. However, there are distinct
differences in functionality between the oj-data-grid and oj-table
components. For additional information, see Choosing a Table, Data Grid, or
List View.
To create the data grid, define an oj-data-grid element in your HTML file and assign a
meaningful id, width, and height. Also, specify the data property.
<oj-data-grid id="datagrid"
style="width:100%;max-width:234px;height:130px"
aria-label="Data Grid Cell Based Grid Demo"
data="[[dataSource]]">
</oj-data-grid>
In this example, the aria-label is added to the oj-data-grid element for accessibility.
oj-data-grid implicitly defines an Aria role as application, and you must add the aria-
label to distinguish it from other elements defined with the Aria application role. For
additional information about Oracle JET and accessibility, see Developing Accessible
Applications.
The data is defined in the dataSource object and can be one of the following:
The array can be a single array where each item in the array represents a row in
the data grid, or a two dimensional array where each item represents a cell in the
grid. For the data grid shown in this section, the data is defined as a two
dimensional array.
7-5
Chapter 7
Working with Collections
$(
function()
{
ko.applyBindings(new viewModel(),
document.getElementById('datagrid'));
}
);
});
Note:
Only data grid components that use a custom data source support the
use of nested row or column headers.
The Oracle JET Cookbook includes the complete example for the data grid used in
this section at Data Grids. The cookbook also includes examples that show row-based
data grids, editable data grids, and data grids with visualizations, end headers, custom
cell renderers, merged cells, and custom data sources.
7-6
Chapter 7
Working with Collections
The data grid uses JSON data for its data source. The code sample below shows a
portion of the JSON array.
[
{
"index": 0,
"units": 80,
"sales": 535,
"tax": 0.0234,
"year": "2014",
"gender": "Male",
"product": "Coupe",
"city": "New York",
"drivetrain": "FWD",
"color": "White"
},
{
"index": 1,
"units": 95,
7-7
Chapter 7
Working with Collections
"sales": 610,
"tax": 0.0721,
"year": "2015",
"gender": "Male",
"product": "Coupe",
"city": "New York",
"drivetrain": "FWD",
"color": "White"
},
{
"index": 2,
"units": 27,
"sales": 354,
"tax": 0.0988,
"year": "2014",
"gender": "Female",
"product": "Coupe",
"city": "New York",
"drivetrain": "FWD",
"color": "White"
},
The data also contains a column for the gender of the buyer which isn't included in the
display. The totals displayed in the grid come from applying the aggregation across
any JSON rows that match up on type, color, drivetrain, year, and city. For the data in
this example, this has the effect of grouping the Male and Female values and applying
the aggregation. For example, the units shown in the grid for the New York sales in
2014 of white FWD coupes comes from totaling the highlighted values:
80 + 27 = 107 units
The following code sample shows the markup for the data grid.
<oj-data-grid id="datagrid"
style="width:100%;height:400px;max-width:851px;"
aria-label="Cubic Data Source Grid Demo"
data="[[dataSource]]"
cell.renderer="[[cellRenderer]]"
></oj-data-grid>
7-8
Chapter 7
Working with Collections
{attribute:'sales'},
{attribute:'tax',aggregation:oj.CubeAggType['AVERAGE']}]);
};
collection.fetch({success:function() {
dataArr = collection.map(function(model) {
return model.attributes;
});
cube = generateCube(dataArr, axes);
vm.currentColor(topLevelItems[0].value);
vm.dataSource(new oj.CubeDataGridDataSource(cube));
}});
var axes = [
{axis: 0, levels: [
{attribute: 'city'},
{attribute: 'year'},
{dataValue: true}]},
{axis: 1, levels:[
{attribute: 'product'}
{attribute: 'color'},
{attribute: 'drivetrain'}]}];
this.dataSource = ko.observable();
$(function() {
ko.applyBindings(new dataGridModel(),
document.getElementById('wrapper'));
});
});
In this example, the JSON data is mapped to the dataArr object which is defined
as an oj.Collection object.
var collection = new oj.Collection(null, {
url: 'cookbook/dataCollections/dataGrid/cubeGrid/cubedata.json'
});
• layout: An array of objects that contains the axis number and levels to use for the
aggregation
The axis number indicates where you want the aggregated data displayed: 0 for
column headers, and 1 for row headers. The levels tell the cube which values to
aggregate and the order to display them. The code sample below shows the layout
used in this example. The dataValue property indicates which level to display for
the units, sales, and tax aggregated values.
var axes = [
{axis: 0, levels: [
7-9
Chapter 7
Working with Collections
{attribute: 'city'},
{attribute: 'year'},
{dataValue: true}]},
{axis: 1, levels: [
{attribute: 'product'},
{attribute: 'color'},
{attribute: 'drivetrain'}]}];
You can configure an additional axis that you can use to filter or otherwise restrict
the display of data. For example, you can add a third axis which includes the color
and drivetrain attributes, as shown in the following code sample:
var axes = [
{axis: 0, levels: [
{attribute: 'city'},
{attribute: 'year'},
{dataValue: true}]},
{axis: 1, levels: [
{attribute: 'product'}]},
{axis: 2, levels:[
{attribute: 'color'},
{attribute: 'drivetrain'}]}];
When you add a third axis to the page, the axis will not be visible. However, you
can use the setPage() method to use the axis as a page axis to filter the display.
For example, setting color and drivetrain on the third axis has the effect of
restricting the display to aggregate only those values that match both the color
and drivetrain attributes.
In this example, you have six page combinations: White FWD, White AWD, Black
FWD, Black AWD, Red FWD, and Red AWD products. If you set the color
attribute to White and the drivetrain attribute to 4WD as shown in the following
image, when you render the page only the values for White, 4WD products
(Coupe, Sedan, Wagon, SUV, Van, and Truck) display on the screen.
7-10
Chapter 7
Working with Collections
• dataValues: An array of objects that contains the name of the attribute in the row
set that represents the data, an optional label, and the aggregation type. The
aggregation type is also optional and defaults to SUM. You can also set it to one of
the aggregation types shown in the following table.
The code sample below shows the function that defines the dataValues for the
cube used in this section. The cube will sum the units and sales data and will
average the sales tax data.
function generateCube(dataArr, axes) {
return new oj.DataValueAttributeCube(dataArr, axes,
[{attribute:'units',aggregation:oj.CubeAggType['SUM']},
{attribute:'sales'},
{attribute:'tax',aggregation:oj.CubeAggType['AVERAGE']}]);
};
The Oracle JET Cookbook demos at Data Grids (Cubic Data Source) contain the
complete code for the example used in this section, including the complete code for
setting the page axis and rendering the cell content. The oj.CubeDataGridDataSource
API documentation also contains additional detail and methods that you can use for
working with the cube.
Topics:
• Understanding Data Requirements for List Views
• Working with List Views and Inline Templates
7-11
Chapter 7
Working with Collections
<oj-list-view id="listview"
aria-label="list with hierarchical data"
selection-mode="multiple"
expanded="[[expanded]]"
item.selectable="[[itemOnly]]">
<ul>
<li id="a">
<span>A</span>
<ul>
<li id="amybartlet">
<span class="avatar" style="background-image: url('images/dvt/1.png')"></
span>
<div class="container">
<div>
<span class="name">Amy Bartlet</span>
</div>
<div>
<span class="oj-text-xs oj-text-secondary-color">Vice President</
span>
</div>
</div>
</li>
...contents omitted
</oj-list-view>
In your application’s viewModel, define the key set. The following example sets the
ListView to expand the cookbook item on initial display. Note that you must also add
the ojkeyset module and keySet function to the require definition.
require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojkeyset', 'ojs/ojknockout', 'ojs/
ojlistview'],
function(oj, ko, $, keySet)
{
function viewModel()
{
this.itemOnly = function(context)
{
return context['leaf'];
};
this.expanded = new keySet.ExpandedKeySet(['a', 'b', 'c']);
} ...contents omitted );
You can use ExpandAllKeySet class to set all the list items in a ListView to be expanded
on initial display as follows:
this.expanded = new keySet.ExpandAllKeySet();
For the complete example, see List View Using Expanded Option using expanded
attribute.
7-12
Chapter 7
Working with Collections
The following code sample shows a portion of the markup used to create the list
view with hierarchical static content using the oj-list-view element. The element
allows multiple selection and expands both list item groups A and B upon initial
display.
<oj-list-view id="listview" aria-label="list with hierarchical data" selection-
mode="multiple" item.selectable="[[itemOnly]]">
<ul>
<li id="a">
<span>A</span>
<ul>
<li id="amybartlet">
<span class="avatar" style="background-image: url('images/nBox/
1.png')"></span>
<div class="container">
<div>
<span class="name">Amy Bartlet</span>
</div>
<div>
<span class="oj-text-xs oj-text-secondary-color">Vice
President</span>
</div>
</div>
</li>
... contents omitted
</ul>
7-13
Chapter 7
Working with Collections
</li>
<li id="b">
<span>B</span>
<ul>
... contents omitted
</ul>
</li>
</ul>
</oj-list-view>
$(
function()
{
ko.applyBindings(new viewModel(),
document.getElementById('listview'));
}
);
});
See List View Using Static Data for the complete example to create oj-list-view
components using static data and static hierarchical data.
• Oracle JET TreeDataSource, including oj.JsonTreeDataSource and
oj.CollectionTreeDataSource.
To use a TreeDataSource, you specify the method that returns the tree data in the
data attribute for the component.
The Oracle JET Cookbook contains the complete example for creating a list view
with an oj.JsonTreeDataSource at List View Using Hierarchical JSON Data. You can
also find an example for creating a list view with an oj.JsonTreeDataSource that also
contains an index at Indexer.
• Oracle JET TableDataSource, including oj.CollectionTableDataSource and
oj.PagingTableDataSource
Use the TableDataSource data attribute when oj.Collection is the model for the
underlying data, or you want to add scrolling or pagination to your oj-list-view.
The following image shows a list view using an oj.CollectionTableDataSource
object for its data.
7-14
Chapter 7
Working with Collections
In this example, high water mark scrolling is enabled through the component
attribute’s scroll-policy.
<oj-list-view id="listview" aria-label="list using collection" style="width:
100%;height:300px;overflow-x:hidden"
data="[[dataSource]]"
item.renderer="[[oj.KnockoutTemplateUtils.getRenderer('tweet_template')]]"
selection-mode="single"
scroll-policy="loadMoreOnScroll" scroll-policy-options.fetch-size="15">
</oj-list-view>
For the complete example, including the script that creates the
oj.CollectionTableDataSource object, see List View Using oj.Collection.
You can also find cookbook examples that add Pull to Refresh and Swipe to
Reveal touch capability to an oj-list-view created with the
oj.CollectionTableDataSource object.
For the complete example, see List View Using and oj.ArrayDataProvider .
7-15
Chapter 7
Working with Collections
Note:
If you do not specify a data source in the list view component's data attribute,
Oracle JET will examine the child elements inside the root element and use it
for static content. If the root element has no children, Oracle JET will render
an empty list.
– depth (available when hierarchical data is provided) - the depth of the current
item being rendered. The depth of the first level children under the invisible
root is 1
– leaf (available when hierarchical data is provided) - whether the current item
is a leaf node or not
– parentKey (available when hierarchical data is provided) - the key of the parent
item. The parent key is null for root nodes
• alias - if as attribute is specified, the value will be used to provide an application-
named alias for $current value.
In the following image, an inline template specifies the content to be rendered within
the list created using oj-list-view.
7-16
Chapter 7
Working with Collections
The HTML code sample below shows a portion of the markup for a list view using an
inline template.
<oj-list-view id="listview" aria-label="list using observable array"
data="[[dataProvider]]" selection-mode="multiple"
selection="{{selectedItems}}">
<template slot="itemTemplate">
<span>
<oj-bind-text value='[[$current.data.item]]'></oj-bind-text>
</span>
</template>
</oj-list-view>
The selection attribute monitors the current selection value in the array.
In the following JavaScript sample code, array data in a variable, allItems, is passed
to a variable, dataProvider, using the oj.ArrayDataProvider object:
this.itemToAdd = ko.observable("");
this.allItems = ko.observableArray([{"id": 1, "item": "Milk"},
{"id": 2, "item": "Flour"},
{"id": 3, "item": "Sugar"},
{"id": 4, "item": "Vanilla Extract"}
]);
this.selectedItems = ko.observableArray([]);
var lastItemId = this.allItems().length;
See List View Using Inline Template for the complete example to create a list view
using an inline template.
7-17
Chapter 7
Working with Collections
The script that populates the pagingDatasource with data and completes the Knockout
binding is shown below. In this example, the table's data is defined in an
ArrayDataProvider object, and the pagingDatasource defines the PagingTableDataSource
as a new ArrayDataProvider object.
require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'ojs/ojcheckboxset',
'ojs/ojtable', 'ojs/ojpagingcontrol',
'ojs/ojpagingtabledatasource', 'ojs/ojarraydataprovider'],
function(oj, ko, $)
{
function viewModel()
{
var self = this;
7-18
Chapter 7
Working with Collections
ManagerId: 300},
{DepartmentId: 20, DepartmentName: 'Marketing', LocationId: 200, ManagerId:
300},
...contents omitted
{DepartmentId: 13022, DepartmentName: 'Human Resources15', LocationId: 200,
ManagerId: 300}];
self.pagingDatasource = new oj.PagingTableDataSource(new
oj.ArrayDataProvider(deptArray, {idAttribute: 'DepartmentId'}));
}
$(document).ready
(
function()
{
ko.applyBindings(vm, document.getElementById('pagingControlDemo'));
}
);
});
The Oracle JET Cookbook contains complete examples for adding pagination to oj-
table, oj-data-grid, and HTML lists at Pagination. You can also find the link to the oj-
paging-control API documentation as well as examples that show different options for
customizing the paging display.
For additional information about working with the oj-table component, see Working
with Tables. For more information about working with the oj-data-grid component, see
Working with Data Grids.
7-19
Chapter 7
Working with Collections
<oj-data-grid
id="datagrid"
style="width:100%;max-width:502px;height:400px"
aria-label="Data Grid with Row Expander"
data="[[dataSource]]"
selection-mode.cell="single"
header.column.renderer="[[oj.KnockoutTemplateUtils.getRenderer('column_header_templat
e')]]"
header.column.style="width:100px;"
header.column.resizable.width="enable"
header.row.renderer="[[oj.KnockoutTemplateUtils.getRenderer('row_header_template')]]"
header.row.style="width:200px;"
cell.class-name="oj-helper-justify-content-flex-start"
></oj-data-grid>
7-20
Chapter 7
Working with Collections
</oj-bind-if>
<oj-bind-if test="[[$context.key == 'end']]">
<span><oj-bind-text value="End Date"></oj-bind-text></span>
</oj-bind-if>
</script>
The data for the oj.FlattenedTreeDataGridDataSource object can come from local or
fetched JSON, or an oj.Collection object. In the example in this section, the data is
read from a JSON file. The code sample below shows a portion of the JSON.
[
{"attr": {"id": "t1",
"name": "Task 1",
"resource": "Chadwick",
"start": "1/1/2014",
"end": "10/1/2014"
},
"children": [
{"attr": {"id": "t1:1",
"name": "Task 1-1",
"resource": "Chris",
"start": "1/1/2014",
"end": "3/1/2014"
},
"children": [
{"attr": {"id": "t1:1:1",
"name": "Task 1-1-1",
"resource": "Henry",
"start": "1/1/2014",
"end": "2/1/2014"
}
},
{"attr": {"id": "t1:1:2",
"name": "Task 1-1-2",
"resource": "Victor",
"start": "2/1/2014",
"end": "3/1/2014"
}
}
]
},
...contents omitted
]
},
... contents omitted
{"attr": {"id": "t4",
"name": "Task 4",
"resource": "Victor",
"start": "11/1/2014",
"end": "12/1/2014"
}
}
]
7-21
Chapter 7
Working with Collections
The script that reads the JSON file and defines the datasource object as an
oj.FlattenedTreeDataGridDataSource is shown below.
var options = {
'rowHeader': 'name',
'columns': ['resource', 'start', 'end']
};
self.dataSource = ko.observable();
$.getJSON( "cookbook/dataCollections/rowExpander/dataGridRowExpander/
projectData.json",
function(data)
{
self.dataSource(new oj.FlattenedTreeDataGridDataSource(new
oj.JsonTreeDataSource(data), options));
}
);
}
$(
function()
{
ko.applyBindings(new viewModel(), document.getElementById('datagrid'));
}
);
});
To use the row expander with oj-table, add the oj-row-expander element to the HTML
markup, and specify the table’s data in an oj.FlattenedTreeTableDataSource object.
The Oracle JET Cookbook at Row Expanders contains an example that uses the row
expander with oj-table. The cookbook also contains the complete code for the
example in this section and a link to the API documentation for oj-row-expander. In
addition, you can find examples that use an oj.Collection object for the table's data
and initialize the row expander with one or more rows expanded.
For additional information about working with the oj-data-grid component, see
Working with Data Grids. For more information about working with the oj-table
component, see Working with Tables.
7-22
Chapter 7
Working with Collections
The Tables demos in the Oracle JET Cookbook include the complete example for this
table and a link to the oj-table API documentation. The cookbook also includes an
example that creates the oj-table component using data defined in an
oj.CollectionTableDataSource object and examples that show tables with custom row
and cell templates, selection, sorting, reordering, scrolling, custom cell renderers, and
drag and drop support.
The data source for the oj-table component can be one of the following:
7-23
Chapter 7
Working with Collections
In the following JavaScript sample code, data from an external data source is
passed to a variable, DeptCollection, using the oj.CollectionTableDataSource
object:
self.DeptCollection = oj.Collection.extend({url: self.serviceURL + "?
limit=50",model: self.myDept});
self.DeptCol(new self.DeptCollection());
$.getJSON("cookbook/dataCollections/table/ojCollectionTable/departments.json",
function (data) {
new MockRESTServer(data, {id:"DepartmentId",
url:/^http:\/\/mockrest\/stable\/rest\/Departments(\?
limit=([\d]*)?)$/i,
idUrl:/^http:\/\/mockrest\/stable\/rest\/Departments\/([\d]+)(?:\
\?limit=([\\d]*))$/i});
7-24
Chapter 7
Working with Collections
self.datasource(new oj.CollectionTableDataSource(self.DeptCol()));
});
<oj-table
id="table" aria-label="Tasks Table"
data="[[datasource]]"
row-renderer='[[oj.KnockoutTemplateUtils.getRenderer("row_template", true)]]'
columns='[{"headerText": "Task Name", "sortProperty": "name"},
{"headerText": "Resource", "sortProperty": "resource"},
{"headerText": "Start Date", "sortProperty": "start"},
{"headerText": "End Date", "sortProperty": "end"}]'>
</oj-table>
<script type="text/html" id="row_template">
<tr>
<td>
<oj-row-expander context="[[$context.rowContext]]"></oj-row-expander>
<span><oj-bind-text value="[[$context.data.name]]"></oj-bind-text></span>
</td>
<td>
<span><oj-bind-text value="[[$context.data.resource]]"></oj-bind-text></
span>
</td>
<td>
<span><oj-bind-text value="[[$context.data.start]]"></oj-bind-text></
span>
</td>
<td>
<span><oj-bind-text value="[[$context.data.end]]"></oj-bind-text></span>
</td>
</tr>
</script>
7-25
Chapter 7
Working with Collections
In the following JavaScript sample code, the table data is defined in a JavaScript
array, deptArray, which is passed into the definition for the
oj.ArrayTableDataSource. The self.pagingDatasource is then defined as an
oj.PagingTableDataSource which gets its data from the oj.ArrayTableDataSource.
When you configure a column for sorting, the column header displays arrow indicators
to indicate that the column is sortable when the user hovers over the column header.
In the following image, the Department ID is sortable, and the sort indicator is showing
a down arrow to indicate that the sort is currently descending. The user can select the
down arrow to change the sort back to ascending.
7-26
Chapter 7
Working with Collections
• auto: Sort the table or indicated column if the underlying data supports sorting.
oj-table sorting uses standard JavaScript array sorting. If your application requires
custom sorting, you can use ArrayDataProvider and its sortComparator property. For an
example, see Tables - Custom Sort.
For additional information about oj-table and sorting options, see the oj-table API
documentation.
7-27
Chapter 7
Working with Collections
For examples in the Oracle JET Cookbook that implement tables and table sorting,
see Tables.
7-28
Chapter 7
Working with Collections
The HTML code sample below shows a portion of the markup for a table using various
inline templates for column header, column footer, data for table cells and others.
<oj-table id='table' aria-label='Departments Table'
data='[[dataprovider]]'
columns='{{columnArray}}'
columns-default='{"template": "cellTemplate",
"headerTemplate": "headerTemplate"}'
style='width: 100%;'>
<template slot="cellTemplate">
<oj-bind-text value="[[$current.data]]"></oj-bind-text>
</template>
<template slot="ratingCellTemplate">
<oj-rating-gauge value='[[$current.data]]' readonly style="width:60px;height:
15px;">
</oj-rating-gauge>
</template>
<template slot="headerTemplate">
<oj-bind-text value="[[$current.data]]"></oj-bind-text>
</template>
<template slot="totalFooterTemplate">
<div id="table:emp_total"><oj-bind-text
value="{{emp_total_func($current)}}"></oj-bind-text></div>
</template>
</oj-table>
The selection attribute monitors the current selection value in the array.
In the following JavaScript sample code, array data in a variable, deptArray, is passed
to a variable, dataprovider, using the oj.ArrayDataProvider object:
var deptArray = [{DepartmentId: 1001, DepartmentName: 'ADFPM 1001 neverending',
LocationId: 200, ManagerId: 300, EmployeeCount: 20, Rating: 1},
{DepartmentId: 556, DepartmentName: 'BB', LocationId: 200, ManagerId: 300,
EmployeeCount: 10, Rating: 1},
{DepartmentId: 10, DepartmentName: 'Administration', LocationId: 200, ManagerId:
300, EmployeeCount: 30, Rating: 2},
{DepartmentId: 20, DepartmentName: 'Marketing', LocationId: 200, ManagerId: 300,
EmployeeCount: 20, Rating: 3},
{DepartmentId: 30, DepartmentName: 'Purchasing', LocationId: 200, ManagerId:
300, EmployeeCount: 50, Rating: 4},
{DepartmentId: 40, DepartmentName: 'Human Resources1', LocationId: 200,
ManagerId: 300, EmployeeCount: 200, Rating: 5},
{DepartmentId: 50, DepartmentName: 'Administration2', LocationId: 200,
ManagerId: 300, EmployeeCount: 20, Rating: 1.5},
{DepartmentId: 60, DepartmentName: 'Marketing3', LocationId: 200, ManagerId:
300, EmployeeCount: 30, Rating: 2.5}];
self.dataprovider = new oj.ArrayDataProvider(deptArray, {idAttribute:
'DepartmentId'});
7-29
Chapter 7
Working with Collections
{"headerText": "Rating",
"field": "Rating",
"template": "ratingCellTemplate"}];
See Table Using Inline Template for the complete example to create a table using an
inline template.
In this example, the expanded Links node is a root node, and the Oracle, IBM, and
Microsoft nodes are its children. The Oracle child node contains the USA, Europe, and
Asia nodes which also contain child nodes. The USA node is expanded to show its
Northeast, Midwest, South, and West child nodes.
To create the oj-tree-view component, add the oj-tree-view element to the HTML
markup. Assign it a meaningful ID and specify attributes on the oj-tree-view element.
Construct the tree using a predefined HTML unordered list (ul) element in the oj-tree-
view element.
7-30
Chapter 7
Working with Collections
7-31
Chapter 7
Working with Collections
<ul>
... contents omitted
</ul>
</li>
</ul>
</li>
<li id="ibm">
<span class="oj-treeview-item-icon"></span
><span class="oj-treeview-item-text">IBM</span>
</li>
<li id="microsoft">
<span class="oj-treeview-item-icon"></span
><span class="oj-treeview-item-text">Microsoft</span>
</li>
</ul>
</li>
</ul>
</oj-tree-view>
Tip:
To ensure that there is no extra white space between the icon and the
text in the display, place the icon span and the text span elements on a
single line or use the broken closing bracket notation.
<li id="news">
<span class="oj-treeview-item-icon"></span
><span class="oj-treeview-item-text">News</span>
</li>
In this example below, the tree view uses JSON data. The sample code also
shows the Knockout applyBindings() call to complete the component binding.
require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout',
'ojs/ojtreeview', 'ojs/ojjsontreedatasource'
],
function(oj, ko, $) {
function TreeViewModel() {
var jsonData = [{
"attr": {
"title": "News",
"id": "news"
}
}, {
"attr": {
"title": "Blogs",
"id": "blogs"
},
"children": [{
"attr": {
"title": "Today",
"id": "today"
}
}, {
"attr": {
"title": "Yesterday",
"id": "yesterday"
7-32
Chapter 7
Working with Collections
}
}, {
"attr": {
"title": "Archive",
"id": "archive"
}
}]
}, {
"attr": {
"title": "Links",
"id": "links"
},
"children": [{
"attr": {
"title": "Oracle",
"id": "oracle"
},
"children": [{
"attr": {
"title": "USA",
"id": "usa"
},
"children": [{
"attr": {
"title": "Northeast",
"id": "northeast"
}
}, {
"attr": {
"title": "Midwest",
"id": "midwest"
}
}, {
"attr": {
"title": "South",
"id": "south"
}
}, {
"attr": {
"title": "West",
"id": "west"
}
}]
}, {
"attr": {
"title": "Europe",
"id": "europe"
}
}, {
"attr": {
"title": "Asia",
"id": "asia"
},
"children": [{
"attr": {
"title": "Japan",
"id": "japan"
}
}, {
"attr": {
"title": "China",
7-33
Chapter 7
Working with Collections
"id": "china"
}
}, {
"attr": {
"title": "India",
"id": "india"
}
}]
}]
}, {
"attr": {
"title": "IBM",
"id": "ibm"
}
}, {
"attr": {
"title": "Microsoft",
"id": "microsoft"
},
}]
}];
this.data = new oj.JsonTreeDataSource(jsonData);
}
$(document).ready(function() {
ko.applyBindings(new TreeViewModel(),
document.getElementById('treeview'));
});
}
);
• oj.CollectionTreeDataSource : Use when your data is coming from an
oj.Collection object, typically representing an external data source.
In your application’s viewModel, define the key set. The example below sets the tree
view to expand the Links node on initial display. Note that you must also add the
ojknockout-keyset module and keySet function to the require definition.
7-34
Chapter 7
Working with Controls
Examples
The Oracle JET Cookbook contains the complete recipe and code for the samples
used in this section at Tree Views. The cookbook also includes a link to the oj-tree-
view API documentation which includes additional examples for creating tree views.
Topics:
• Working with Buttons
• Working with Button Sets
• Working with Conveyor Belts
• Working with File Picker
• Working with Film Strips
• Working with Menus
• Working with Progress Indicators
• Working with Tags
• Working with Toolbars
• Working with Trains
7-35
Chapter 7
Working with Controls
Create a push button by adding the oj-button element to the HTML markup and
adding a listener in your view model to respond to click events. The code sample
below shows the markup for Button 1, Button 2, and Button 3.
<div id='buttons-container'>
<oj-button id='button1' on-oj-action='[[buttonClick]]'>Button 1</oj-button>
<oj-button id='button2' on-oj-action='[[buttonClick]]'>
<span><oj-bind-text value="[[button2Text]]"></oj-bind-text></span>
</oj-button>
<oj-button id='button3' on-oj-action='[[buttonClick]]' display='icons'>
<img slot="startIcon"
src="css/samples/cookbook/images/hiResContrast/icon.png"
alt="Button 3"
width="16" height="16"/>Button 3
</oj-button>
<p>
<p id="last" class="bold">ID of last button to be clicked:
<span id="results"><oj-bind-text value="[[clickedButton]]"></oj-bind-text></span>
</p>
</div>
Each button’s on-oj-action attribute specifies the buttonClick listener. In this example,
the listener simply sets the value of clickedButton to the id of the button that the user
clicked: button1, button2, or button3.
function buttonModel(){
var self = this;
The Oracle JET Cookbook at Buttons includes complete examples for all buttons,
including a section on button styling that shows you how to add icons, control
chroming, and configure colors, width, and responsive behavior. oj-button API
documentation describes support for keyboard interaction, accessibility, and event
handling.
7-36
Chapter 7
Working with Controls
In your view model, set an initial value for the button set and apply the bindings.
require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'ojs/ojbutton'],
function(oj, ko, $)
{
function ButtonModel() {
var self = this;
// observable array bound to the Buttonset:
self.formats = ko.observableArray(["bold", "underline"]);
}
$(function() {
ko.applyBindings(new ButtonModel(), document.getElementById('buttons-
container'));
});
});
The Oracle JET cookbook at Button Sets contains the complete recipe to create the
button set shown above.
Here are some tips for working with button sets:
• A button set that contains radios should contain all radios in the radio group.
• Checkboxes and radio buttons created by oj-option in the button set should
specify the value attribute, since the value attribute on the oj-buttonset refers to
that attribute.
• The application should not do anything to interfere with the focus management.
For example, it should not set the tabindex of the buttons.
• Enabled buttons should remain user visible. Otherwise, the arrow-key navigation
to the button will cause the focus to seemingly disappear.
• The button set's focusManagement attribute should be set to none when placing the
button set in an oj-toolbar element.
• The application is responsible for applying WAI-ARIA aria-label and aria-controls
attributes to the button set, as appropriate.
aria-label="Choose only one. Use left and right arrow keys to navigate."
aria-controls="myTextEditor"
7-37
Chapter 7
Working with Controls
• If the value attribute and DOM get out of sync (for example, if a set of buttons
contained in a button set changes, possibly due to a Knockout binding), then the
application is responsible for updating the value attribute.
• The application doesn't need to listen for this event, since the value binding will
update the bound observable whenever the value state changes.
For additional information about the oj-buttonset-many and oj-buttonset-one
components’ attributes, events, and methods, see the oj-buttonset API documentation.
Define the oj-conveyor-belt element in the HTML file. Specify a group of child sibling
elements to be managed by the oj-conveyor-belt. You can add the sibling child
elements as either direct children of the conveyor belt or as the nested children of a
container element that is itself a direct or nested child of the conveyor belt. In this
example, the oj-conveyor-belt is configured for horizontal scrolling with a maximum
width that varies on the screen width, and the sibling child elements are defined as
Oracle JET oj-button elements.
<div id="conveyorbelt-horizontal-example">
<div class="oj-flex">
<oj-conveyor-belt class="oj-lg-6 oj-md-9 oj-sm-12">
<oj-button class="demo-button">Hydrogen</oj-button>
<oj-button class="demo-button">Helium</oj-button>
<oj-button class="demo-button">Lithium</oj-button>
<oj-button class="demo-button">Beryllium</oj-button>
<oj-button class="demo-button">Boron</oj-button>
<oj-button class="demo-button">Carbon</oj-button>
<oj-button class="demo-button">Nitrogen</oj-button>
<oj-button class="demo-button">Oxygen</oj-button>
<oj-button class="demo-button">Fluorine</oj-button>
<oj-button class="demo-button">Neon</oj-button>
<oj-button class="demo-button">Sodium</oj-button>
<oj-button class="demo-button">Magnesium</oj-button>
</oj-conveyor-belt>
</div>
</div>
Note:
The oj-conveyor-belt component does not provide accessibility features
such as keyboard navigation. It is the responsibility of the application
developer to make the items in the conveyor belt accessible. For tips and
additional detail, see the oj-conveyor-belt API documentation.
7-38
Chapter 7
Working with Controls
When you configure the child elements as direct children of the oj-conveyor-belt, the
element will ensure that they are laid out according to the specified orientation. If,
however, you configure the child elements as the children of a container element, you
must take additional steps to ensure the correct display. For details, see the oj-
conveyor-belt API documentation.
The Oracle JET Cookbook Conveyor Belts demos contain the complete code for this
example. In addition, you will find examples for a vertical conveyor belt and conveyor
belts with nested content, tab-based scrolling, and programmatic scrolling.
Note:
The Oracle JET oj-film-strip component also manages a group of sibling
child elements to provide horizontal or vertical scrolling to cycle through the
other child elements. However, it also provides the ability to:
• lay out a set of items across discrete logical pages.
• control which and how many items are shown.
• hide items outside the current viewport from tab order and screen
readers.
For additional information, see Working with Film Strips.
The following example provides the clickable drop zone with customized text:
<oj-file-picker on-select='[[selectListener]]'>
<div tabindex='0' slot='trigger' class='oj-filepicker-dropzone'>
<p class='oj-filepicker-text'>I'm a clickable dropzone</p>
</div>
</oj-file-picker>
7-39
Chapter 7
Working with Controls
The following example provides the customized file picker and the slot attribute is set
to trigger the custom content of the file picker:
<oj-file-picker class='oj-filepicker-custom' selectOn='click' on-
select='[[selectListener]]'>
<oj-button slot='trigger'>
<span slot='startIcon' class='oj-fwk-icon oj-fwk-icon-arrowbox-n'></span>
Upload
</oj-button>
</oj-file-picker>
By specifying the accept attribute, you can allow the file picker to accept an array of
strings of allowed MIME types. Default value is undefined. If accept attribute is not
specified, all file types are accepted.
Note:
If the accept attribute is specified, files with empty string type will be rejected
if no match is found in the accept attribute value.
To allow multiple selection of files, set the selection-mode attribute to multiple. Also, to
get the selected files, add an on-select listener.
The following image illustrates the default file picker with selection mode set to multiple
file selection and accept attribute set to any image type.
7-40
Chapter 7
Working with Controls
<div style="padding-top:8px"></div>
<div class="oj-choice-item">
<oj-checkboxset id="selection" aria-label="Mulitple selection"
value="{{multiple}}">
<oj-option id="multipleSelect" value="multiple">Select Multiple</oj-option>
</oj-checkboxset>
</div>
<oj-label for="acceptFld">Accept</oj-label>
<oj-text-area id="acceptFld" rows="3" value="{{acceptStr}}"></oj-text-area>
<div style="padding-top:8px">
<oj-label for="selected">Selected files: </oj-label>
<span id="selected">
<oj-bind-text value="[[ko.toJSON(fileNames)]]"></oj-bind-text>
</span>
</div>
</div>
In this example, the file picker uses a Knockout array to accept the selected files,
identify the file name of the selected file, and to display the file name.
require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'ojs/ojfilepicker'],
function(oj, ko, $) {
var self = this;
function basicModel() {
self.multiple = ko.observable(true);
self.multipleStr = ko.pureComputed(function() {
return self.multiple() ? "multiple" : "single";
}, self);
7-41
Chapter 7
Working with Controls
self.acceptStr = ko.observable("image/*");
self.acceptArr = ko.pureComputed(function() {
var accept = self.acceptStr();
return accept ? accept.split(",") : [];
}, self);
self.fileNames = ko.observableArray([]);
self.selectListener = function(event) {
var files = event.detail.files;
for (var i = 0; i < files.length; i++) {
self.fileNames.push(files[i].name);
}
}
}
$(function() {
ko.applyBindings(new basicModel(), document.getElementById('parentContainer'));
});
});
The Oracle JET Cookbook File Picker demos contain the complete code for this
example.
Tip:
You can add the oj-progress-list custom element to display the progress of
the file upload. For a demo, see Progress List.
7-42
Chapter 7
Working with Controls
You can use the arrow-placement attribute to control the location of the arrows. By
default, it is set to adjacent which displays arrows outside the content, but you can set
it to overlay to overlay the arrows on the content.
In this example, the film strip uses the Knockout foreach binding to iterate through the
list of chemicals defined in the application's main script, shown below.
7-43
Chapter 7
Working with Controls
self.chemicals = [
{ name: 'Hydrogen' },
{ name: 'Helium' },
{ name: 'Lithium' },
{ name: 'Beryllium' },
{ name: 'Boron' },
{ name: 'Carbon' },
{ name: 'Nitrogen' },
{ name: 'Oxygen' },
{ name: 'Fluorine' },
{ name: 'Neon' },
{ name: 'Sodium' },
{ name: 'Magnesium' }
];
self.currentNavArrowPlacement = ko.observable("adjacent");
self.currentNavArrowVisibility = ko.observable("auto");
getItemInitialDisplay = function(index)
{
return index <3 ? '' : 'none';
};
};
ko.applyBindings(model,
document.getElementById('filmstrip-navarrows-example'));
}
);
});
To set the oj-film-strip to loop around from first page to last page and last page to
first page, set the attribute looping to page. If looping is set to off, the navigation is
bounded between the fist page and last page and upon reaching the last page the
navigation cannot go back to first page again. The code sample below shows the
markup for the film strip example using looping attribute set to page.
<div id="filmstrip-looping-example">
7-44
Chapter 7
Working with Controls
</oj-bind-for-each>
</oj-film-strip>
</div>
</div>
oj-film-strip will lay out the child items across multiple logical pages and allow for
changing between logical pages. When the element is resized, the layout will adjust
automatically, and the number of pages and items shown per page may change.
The Oracle JET Cookbook at Film Strips includes the complete code for the example
used in this section. You can also find examples for vertical film strips, lazy loading a
film strip, film strips with pagination, film strips that contain master-detail data, filmstrip
to display paging information, and filmstrip with page looping.
Note:
oj-film-strip is a layout element, and it is the responsibility of the
application developer to make the items in the film strip accessible. For tips
and additional detail, see the Accessibility section in the oj-film-strip API
documentation.
Topics:
• Working with oj-menu
• Working with Menu Buttons
• Working with Context Menus
• Working with Menu Select Many
7-45
Chapter 7
Working with Controls
The following code sample shows the markup used to create the basic menu. To
handle menu selection, add an action listener.
<div id='menubutton-container'>
<oj-menu-button id="menuButton">
Actions
<!-- To handle menu item selection, use an action listener as shown, not a click
listener. -->
<oj-menu id="myMenu" slot="menu" style="display:none" on-oj-
action="[[menuItemAction]]">
<oj-option id="zoomin" value="Zoom In">
<span class="oj-menu-item-icon oj-fwk-icon oj-fwk-icon-arrow-n"
slot="startIcon"></span>Zoom In
</oj-option>
<oj-option id="zoomout" value="Zoom Out">
<span class="oj-menu-item-icon demo-icon-font demo-bookmark-icon-16"
slot="startIcon"></span>Zoom Out
</oj-option>
<oj-option id="divider"></oj-option>
<oj-option id="save" value="Save">
<span class="oj-menu-item-icon demo-icon-font demo-palette-icon-24"
slot="startIcon"></span>Save
</oj-option>
<oj-option id="print" value="Print..." disabled="true">
<span class="oj-menu-item-icon demo-icon-font demo-chat-icon-24"
slot="startIcon"></span>Print...
</oj-option>
</oj-menu>
</oj-menu-button>
</div>
function MenuModel() {
var self = this;
self.selectedMenuItem = ko.observable("(None selected yet)");
$(function() {
ko.applyBindings(new MenuModel(), document.getElementById('menubutton-
container'));
});
});
For additional information about the oj-menu component's attributes, events, and
methods, see the oj-menu API documentation.
The Oracle JET cookbook includes advanced examples for working with oj-menu,
including demos for working with menus programmatically and using templates. For
details, see Menu (Advanced).
7-46
Chapter 7
Working with Controls
To create the menu button, add the basic oj-menu element as a child of an oj-menu-
button element.
The following code sample shows the markup for the menu button, with the details for
the basic menu omitted.
<div id='menubutton-container'>
<oj-menu-button id="menuButton">
Actions
<!-- To handle menu item selection, use an action listener as shown, not a click
listener. -->
<oj-menu id="myMenu" slot="menu" style="display:none" on-oj-
action="[[menuItemAction]]">
<oj-option id="zoomin" value="Zoom In">
<span class="oj-fwk-icon oj-fwk-icon-arrow-n" slot="startIcon"></span>Zoom
In
</oj-option>
...contents omitted
</oj-menu>
</oj-menu-button>
<p>
<p>
<p>Last selected menu item:
<span id="results"><oj-bind-text value="[[selectedMenuItem]]"></oj-bind-text></
span>
</p>
</div>
The following code sample shows the code that initializes the menu button.
require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'ojs/ojbutton', 'ojs/
ojmenu', 'ojs/ojoption'],
function(oj, ko, $)
{
function MenuModel() {
7-47
Chapter 7
Working with Controls
$(function() {
ko.applyBindings(new MenuModel(), document.getElementById('menubutton-
container'));
});
});
The Oracle JET Cookbook includes the complete code sample for the menu button
shown in this section. For details, see Menu Buttons.
In this example, the oj-menu element is placed on an oj-button element. The code
sample below shows the markup.
<div id="button-container">
<h3>A JET component with a context menu</h3>
The button below associates a context menu with an oj-button using the
contextMenu slot.
A contextMenu can be activated on the Launch contextMenu button using the
following mouse, keyboard and touch gestures:
<ul>
<li>On desktop platforms, right mouse click on the button.</li>
<li>On desktop platforms, press shift+F10 when the button has focus.</li>
<li>For mobile or touch platforms, press and hold (long touch) on the
button.</li>
</ul>
<oj-button id="myButton">
Launch contextMenu
<oj-menu slot="contextMenu" style="display:none" aria-label="Order Actions" on-
oj-action="[[menuItemAction]]" >
7-48
Chapter 7
Working with Controls
<br><br>
<h3>Results:</h3>
<p>Last selected menu item in the button's context menu:
<span id="buttonResults" class="italic">
<oj-bind-text value="[[selectedItem]]"></oj-bind-text>
</span>
</p>
</div>
The following code sample shows the code to initialize the context menu.
require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'ojs/ojbutton', 'ojs/
ojmenu', 'ojs/ojoption'],
function(oj, ko, $)
{
function MenuModel() {
var self = this;
self.selectedItem = {
myButton: ko.observable("(None selected yet)"),
myDiv: ko.observable("(None selected yet)")
};
self.selectedItem[launcherId](event.detail.item.textContent);
};
}
$(function() {
ko.applyBindings(new MenuModel(), document.getElementById('button-container'));
});
});
The Oracle JET cookbook contains the complete example, including the CSS for the
menu icons, at Context Menus.
7-49
Chapter 7
Working with Controls
The following code sample shows the markup used to create a menu with submenus.
In this example, the Zoom option has a submenu with Zoom In and Zoom Out options.
<div id='menubutton-container'>
<oj-menu-button id="menuButton">
Actions
<!-- To handle menu item selection, use an action listener as shown, not a click
listener. -->
<oj-menu id="myMenu" slot="menu" style="display:none" on-oj-
action="[[menuItemAction]]">
<oj-option id="save" value="Save">
<span class="demo-icon-font demo-palette-icon-24" slot="startIcon"></
span>Save
</oj-option>
<oj-option id="print" value="Print..." disabled="true">
<span class="demo-icon-font demo-chat-icon-24" slot="startIcon"></
span>Print...
</oj-option>
<oj-option id="zoom">
<span>Zoom</span>
<oj-menu id="zoom_menu">
<oj-option id="zoomin" value="Zoom In">
<span class="oj-fwk-icon oj-fwk-icon-arrow-n" slot="startIcon"></
span>Zoom In
</oj-option>
<oj-option id="zoomout" value="Zoom Out">
<span class="demo-icon-font demo-bookmark-icon-16" slot="startIcon"></
span>Zoom Out
</oj-option>
</oj-menu>
</oj-option>
</oj-menu>
</oj-menu-button>
</div>
To handle the menu selection, add an action listener. The following Java Script defines
the action listener.
require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'ojs/ojbutton', 'ojs/
ojmenu', 'ojs/ojoption'],
function(oj, ko, $)
{
function MenuModel() {
var self = this;
self.selectedMenuItem = ko.observable("(None selected yet)");
7-50
Chapter 7
Working with Controls
$(function() {
ko.applyBindings(new MenuModel(), document.getElementById('menubutton-
container'));
});
});
To create a submenu that supports multiple selection of items, use the oj-menu-select-
many element with an oj-option element representing each child menu item. The oj-
menu-select-many element cannot contain submenus, but can be a child of a top-level
oj-menu or a submenu.
The following code sample shows the markup used to create the top-level menu and
submenu with checkable menu items. To handle menu selection, add
an action listener.
<div id='menubutton-container'>
<oj-menu-button id="menuButton">
Colors
<oj-menu id="myMenu" slot="menu" style="display:none" on-oj-
action="[[menuItemAction]]">
<oj-menu-select-many value="{{primaryColorsMSMValue}}">
<oj-option value="red">
<span slot="endIcon" class="red icon"></span>Red
</oj-option>
<oj-option value="blue">
7-51
Chapter 7
Working with Controls
$(function() {
ko.applyBindings(new MenuModel(), document.getElementById('menubutton-
container'));
7-52
Chapter 7
Working with Controls
});
});
For additional information about the oj-menu component's attributes, events, and
methods, see the oj-menu-select-many API documentation.
The Oracle JET cookbook includes examples for working with oj-menu-select-many,
including demos for working with Menu Select Many using DataProvider options and
Submenus. For details, see Menu Select Many.
To indicate that the value is indeterminate, set the value option to -1, and the progress
indicator will change to reflect the indeterminate status.
To create a progress indicator, use the oj-progress element with a defined value
attribute.
<div id="progressBarWrapper">
<oj-progress style="width:50%" value="{{progressValue}}"></oj-progress>
<oj-progress id="progressCircle" type="circle" value="{{progressValue}}"></oj-
progress>
<oj-input-number id="spinner-input" step="10" value="{{progressValue}}"></oj-
input-number>
</div>
The script that sets the value defines a Knockout observable and sets the initial value.
In this example, the progress indicator's initial value is set to -1, to indicate that the
value is indeterminate.
7-53
Chapter 7
Working with Controls
The Oracle JET Cookbook at Progress contains a complete example for an oj-
progress that shows the effect of adjusting the progress indicator's value. You can also
find examples that show a progress indicator with labeling and a progress indicator
that is linked to changing data.
7-54
Chapter 7
Working with Controls
The oj-toolbar can contain buttons, menu buttons, button sets, and non-focusable
content such as separator icons.
Here are some tips for working with toolbars:
• A toolbar that contains radio buttons should contain all radio buttons in the radio
group.
• The application should not do anything to interfere with the focus management.
• Enabled buttons should remain user visible. Otherwise, the arrow-key navigation
to the button would cause the focus to seemingly disappear.
• The button set's focusManagement attribute should be set to none when placed in a
toolbar.
The Oracle JET Cookbook includes toolbar examples at Toolbars. For additional
information about the oj-toolbar component's attributes, events, and methods, see the
oj-toolbar API documentation.
Each step can display information about its visited state (visited, unvisited, or
disabled) and a message icon of type confirmation, error, warning, or info.
To create a train, you can directly add the oj-train element in the HTML file. Then you
can define the selected-step and steps attributes. The code sample below shows a
portion of the markup for the first oj-train shown in this section.
<div id="train-container">
<oj-train id="train"
class="oj-train-stretch"
style="max-width:700px;margin-left:auto;margin-right:auto;"
on-selected-step-changed="[[updateLabelText]]"
selected-step="{{selectedStepValue}}"
steps="[[stepArray]]">
</oj-train>
7-55
Chapter 7
Working with Forms
The code sample below shows the code that applies the binding, defines the steps
and captures the step selection.
require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'ojs/ojtrain', 'ojs/
ojbutton'],
function(oj, ko, $)
{
function TrainData() {
var self = this;
this.selectedStepValue = ko.observable('stp1');
this.selectedStepLabel = ko.observable('Step One');
this.stepArray =
ko.observableArray(
[{label:'Step One', id:'stp1'},
{label:'Step Two', id:'stp2'},
{label:'Step Three', id:'stp3'},
{label:'Step Four', id:'stp4'},
{label:'Step Five', id:'stp5'}]);
this.updateLabelText = function(event) {
var train = document.getElementById("train");
self.selectedStepLabel(train.getStep(event.detail.value).label);
};
};
$(function() {
ko.applyBindings(trainModel, document.getElementById('train-container'));
});
});
The Oracle JET Cookbook includes the complete code for this example at Trains. You
can also find additional examples that show a stretched train, a train with messages,
and a train with button navigation.
Topics:
• Working with Checkbox and Radio Sets
• Working with Color Pickers
• Working with Comboboxes
• Working with Form Controls
• Working with Form Layout Features
7-56
Chapter 7
Working with Forms
Important:
When working with forms, use the HTML div element to surround any Oracle
JET input component. Do not use the HTML form element because its
postback behavior can cause unwanted page refreshes when the user
submits or saves the form.
The oj-checkboxset and oj-radioset components manage the selected value of their
group and add required validation. In addition, the components manage the styles of
the input elements, adding and removing the Oracle JET styles depending upon state.
To create the oj-checkboxset or oj-radioset, add an oj-checkboxset or oj-radioset
node that wraps a set of oj-options and creates the necessary input and label
elements. The initial value of the checkbox or radio button is defined in the
component's value option. Provide data with oj-option. The following code example
shows the markup that defines the oj-checkboxset shown in the image above.
<div id="formId">
<oj-label id="mainlabelid">Colors</oj-label>
<!-- You need to set the aria-labelledby attribute
to make this accessible.
role="group" is set for you by oj-checkboxset. -->
<oj-checkboxset id="checkboxSetId" labelled-by="mainlabelid"
value="{{currentColor}}">
<oj-option id="blueopt" name="color" value="blue">Blue</oj-option>
<oj-option id="greenopt" name="color" value="green">Green</oj-option>
7-57
Chapter 7
Working with Forms
Note:
For accessibility, the oj-checkboxset and oj-radioset components require
that you set the labelled-by attribute on the component element. For
additional information about creating accessible Oracle JET components,
see Using the Accessibility Features of Oracle JET Components.
The code that defines the currentColor value is defined in the checkboxsetModel()
function, shown below.
require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'ojs/ojcheckboxset',
'ojs/ojlabel'],
function(oj, ko, $)
{
function checkboxsetModel()
{
var self = this;
self.currentColor = ko.observable(["red"]);
}
$(document).ready(
function()
{
ko.applyBindings(vm, document.getElementById('formId'));
}
);
});
The Oracle JET Cookbook contains complete examples for configuring the oj-
checkboxset and oj-radioset components. You can also find examples that show how
to disable the component or one of its input elements, display the component inline,
and test validation. For details, see Checkbox Sets and Radio Sets.
The Oracle JET Cookbook contains the complete examples that you can use to create
color pickers and define their behavior at Color Palette and Color Spectrum.
7-58
Chapter 7
Working with Forms
Topics:
• Working with oj-color-palette
• Working with oj-color-spectrum
The swatch size of the oj-color-palette component using a grid layout can be large,
small or extra small.
The following image shows the oj-color-palette component using a list layout with
small color swatches with labels.
7-59
Chapter 7
Working with Forms
The swatch size of the oj-color-palette component using a list layout can be large or
small.
To create the color palette, add the oj-color-palette element directly in the HTML file.
The code sample below shows the markup for the color palette component that uses
large color swatches with labels.
<div id="colorPaletteDemo">
7-60
Chapter 7
Working with Forms
self.labelDisplay = ko.observable(self.labelDisplays[0]);
To set a component to disabled, you set the disabled option in the markup.
<div id="colorPaletteDemo">
For additional information about adding an Oracle JET component to your page, see
Color Palette.
To create the color spectrum, you can add the oj-color-spectrum element directly in
the HTML file.
7-61
Chapter 7
Working with Forms
<div id="colorSpectrumDemo">
You can disable the component. To set a component to disabled, you set the disabled
option in the markup.
<div id="colorSpectrumDemo">
</div>
For additional information about adding an Oracle JET component to your page, see
Color Spectrum.
The image below shows three single-select comboboxes and one multi-select
combobox. In this example, the first combobox displays the default dropdown list. The
other single-select combo boxes illustrate a disabled item option and items that include
images with the item text.
7-62
Chapter 7
Working with Forms
The user can select one of the items from the dropdown list or erase the current value
and enter text in the input field to search the list of available options. The user can also
enter non-matching text to add a new item. In the example below, the user typed re
which matches the Internet Explorer and Firefox list items.
Tip:
You can use the Oracle JET oj-select-one component or the oj-select-many
component to create read-only dropdown lists. For additional information,
see Working with Select.
To create the combobox, you can use the oj-combobox-one element or the oj-combobox-
many element. Use the oj-option component to display the options in the form of a data
list in the combobox. The code sample below shows the markup for the single-select
combobox with all option items enabled.
<div id="form1">
<oj-label for="combobox">Single-select Combobox</oj-label>
<oj-combobox-one id="combobox" value="{{val}}"
style="max-width:20em">
<oj-option value="Internet Explorer">Internet Explorer</oj-option>
<oj-option value="Firefox">Firefox</oj-option>
<oj-option value="Chrome">Chrome</oj-option>
<oj-option value="Opera">Opera</oj-option>
<oj-option value="Safari">Safari</oj-option>
7-63
Chapter 7
Working with Forms
</oj-combobox-one>
<div>
<br/>
<oj-label for="curr-value" class="oj-label">Curr value is: </oj-label>
<span id="curr-value"><oj-bind-text value="[[val]]"></oj-bind-text></span>
</div>
</div>
The code that defines the val value is defined in the ValueModel() constructor function,
shown below.
require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'ojs/
ojselectcombobox'],
function(oj, ko, $)
{
$(
function()
{
function ValueModel() {
this.val = ko.observableArray(["Chrome"]);
}
ko.applyBindings(new ValueModel(), document.getElementById('form1'));
}
);
});
You can initialize the combobox with the options array. Set the element's options
attribute to a knockout observableArray. The array contains objects with the value and
label fields in string format. Group data is also supported by specifying the label and
children, which is an array of options inside the group. You can also redefine keys
used in the array by specifying them in the options-keys. The code below defines the
options binding for a multi-select combobox.
require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'ojs/
ojselectcombobox'],
function(oj, ko, $)
{
function comboboxModel () {
this.optionsKeys1 = {label: 'regions', children: 'states', childKeys: {value:
'state_abbr', label: 'state_name'}};
this.optionsKeys2 = {label: 'regions', children: 'states', childKeys: {value:
'state_abbr', label: 'state_name',
children: 'cities', childKeys: {value: 'city_abbr', label: 'city_name'}}};
this.browsers = ko.observableArray([
{value: 'Internet Explorer', label: 'Internet Explorer'},
{value: 'Firefox', label: 'Firefox'},
{value: 'Chrome', label: 'Chrome'},
{value: 'Opera', label: 'Opera', disabled: true},
{value: 'Safari', label: 'Safari'}
]);
this.groupData = ko.observableArray([
{label: "Alaskan/Hawaiian Time Zone",
children: [
{value: "AK", label: "Alaska"},
{value: "HI", label: "Hawaii"}
]},
{label: "Pacific Time Zone",
children: [
{value: "CA", label: "California"},
{value: "NV", label: "Nevada"},
7-64
Chapter 7
Working with Forms
this.groupDataWithKeys = ko.observableArray([
{regions: "Alaskan/Hawaiian Time Zone",
states: [
{state_abbr: "AK", state_name: "Alaska"},
{state_abbr: "HI", state_name: "Hawaii"}
]},
{regions: "Pacific Time Zone",
states: [
{state_abbr: "CA", state_name: "California"},
{state_abbr: "NV", state_name: "Nevada"},
{state_abbr: "OR", state_name: "Oregon"},
{state_abbr: "WA", state_name: "Washington"}
]}
]);
this.triLevelGroupData = ko.observableArray([
{regions: "Alaskan/Hawaiian Time Zone",
states: [
{state_abbr: "AK", state_name: "Alaska",
cities: [
{city_abbr: "AN", city_name: "Anchorage"}
]},
{state_abbr: "HI", state_name: "Hawaii",
cities: [
{city_abbr: "HO", city_name: "Honolulu"},
{city_abbr: "HL", city_name: "Hilo"}
]}
]},
{regions: "Pacific Time Zone",
states: [
{state_abbr: "CA", state_name: "California",
cities: [
{city_abbr: "SF", city_name: "San Francisco"},
{city_abbr: "LA", city_name: "Los Angeles"}
]},
{state_abbr: "NV", state_name: "Nevada",
cities: [
{city_abbr: "LV", city_name: "Las Vegas"}
]},
{state_abbr: "OR", state_name: "Oregon",
cities: [
{city_abbr: "PL", city_name: "Portland"},
{city_abbr: "BD", city_name: "Bend"}
]},
{state_abbr: "WA", state_name: "Washington",
cities: [
{city_abbr: "ST", city_name: "Seattle"},
{city_abbr: "SK", city_name: "Spokane"}
]}
]}
]);
}
$(
function() {
ko.applyBindings(new comboboxModel(),
document.getElementById('form1'));
7-65
Chapter 7
Working with Forms
}
);
});
You can also populate the combobox with an ArrayDataProvider. Bind the element's
options attribute to an ArrayDataProvider that is created from an array containing
objects with value and label fields in string format. The code below defines the
ArrayDataProvider binding for the multi-select component.
function(oj, ko, $)
{
function selectModel () {
this.selectVal = ko.observableArray(['CH', 'SA']);
this.browsers = [
{value: 'IE', label: 'Internet Explorer'},
{value: 'FF', label: 'Firefox'},
{value: 'CH', label: 'Chrome'},
{value: 'OP', label: 'Opera'},
{value: 'SA', label: 'Safari'}
];
$(
function() {
ko.applyBindings(new selectModel(), document.getElementById("containerDiv"));
}
);
});
The Oracle JET Cookbook contains complete examples for configuring the single-
select and multi-select comboboxes at Comboboxes. You can also find examples for
setting the width, handling events, adding new entries, and including images with the
list items.
7-66
Chapter 7
Working with Forms
The following code sample shows the markup to create a basic combobox search
component shown in this section.
<oj-label for="search">Basic Search</oj-label>
<oj-combobox-one id="search"
options="[[tagsDataProvider]]"
filter-on-open="rawValue"
on-oj-value-updated="[[search]]"
placeholder="Search..."
style="max-width:20em">
<a slot="end" id="search-button" class="demo-search-button oj-fwk-icon-magnifier
oj-fwk-icon
oj-clickable-icon-nocontext" style="width: 32px;" role="button" aria-
label="search" on-click="[[search]]"></a>
</oj-combobox-one>
The Oracle JET Cookbook includes the complete code for this example at Combobox
Search.
For additional information about the oj-combobox-one component's attributes, events,
and methods, see the oj-comboxbox-one API documentation.
To set a component to disabled or read only, you set the disabled or readOnly option in
the markup and specify the method that will be called when the component is marked
disabled or readOnly. The following code sample shows the markup for the oj-input-
text component.
7-67
Chapter 7
Working with Forms
<div class="oj-flex">
<div class="oj-flex-item">
<oj-label for="inputcontrol1">input</oj-label>
</div>
<div class="oj-flex-item">
<oj-input-text id="inputcontrol1" placeholder="placeholder text"
value='{{placeholder()? null : "text"}}'
disabled="[[disableFormControls()]]" readonly="[[readonlyFormControls]]"
messages-custom="{{messages}}"></oj-input-text>
</div>
</div>
Note:
You can also set disabled as an attribute on the input element. If you use this
method, then disabled will only be picked up at component creation.
Changing the native input element's disabled state after creation will have no
effect.
The Oracle JET Cookbook at Form Controls includes the complete example for the oj-
input-text component as well as the other Oracle JET components that support
disabled and readOnly options. In addition, you can find examples for using placeholder
text and controlling the form's width and height.
7-68
Chapter 7
Working with Forms
Use the Oracle JET oj-form-layout component to create a responsive layout. Use
custom elements within oj-form-layout component as children elements and group
them to create an organized layout that can be optimized for multiple display sizes.
The oj-form-layout supports custom elements, such as oj-input-text and oj-text-
area that supports label-hint and help-hints attributes.
For each custom element, used as a child element, with label-hint attribute, the oj-
form-layout generates an oj-label element and pairs them together in the layout as a
label/value pair. When an oj-label element is followed by any element, the oj-label
element will be in the label area and the following element will be in the value area. An
oj-label-value child component allows you to place the elements in the label or value
area as label and value slot children. All other elements spans the entire width of a
single label/value pair.
The image below shows a responsive layout using the oj-form-layout with various
editable child components. Utilizing the oj.ResponsiveKnockoutUtils, oj-form-layout
can be made to respond to different screen sizes differently. For a display area that
has sufficient space, such as a desktop monitor, the form layout can be set to two
columns and have the labels inline. For a smaller display area, such as a mobile
device, the oj.ResponsiveKnockoutUtils can dynamically set the form layout to one
column with the labels on top of their respective fields. Also, using the oj-label-value
child component we have placed the Save and Cancel buttons.
The following code sample shows the markup for the oj-form-layout component with
editable value child components.
<oj-form-layout id="ofl1" label-edge="{{labelEdge}}" max-columns="{{columns}}">
<oj-input-text id="inputcontrol" required value="text" label-hint="input 1"></oj-
input-text>
<oj-text-area id="textareacontrol" value='text' rows="6" label-
hint="textarea"></oj-text-area>
<oj-input-text id="inputcontrol2" value="text" label-hint="input 2"></oj-input-
text>
7-69
Chapter 7
Working with Forms
oj.ResponsiveUtils.getFrameworkQuery(oj.ResponsiveUtils.FRAMEWORK_QUERY_KEY.SM_ONLY))
;
this.isLargeOrUp = oj.ResponsiveKnockoutUtils.createMediaQueryObservable(
oj.ResponsiveUtils.getFrameworkQuery(oj.ResponsiveUtils.FRAMEWORK_QUERY_KEY.LG_UP));
The Oracle JET Cookbook contains complete examples for configuring the form layout
at Form Responsive Layout .
7-70
Chapter 7
Working with Forms
The editable input components include converters and validators that you can
customize as needed. For additional information, see Validating and Converting Input.
The components also support help messages that you can customize to provide user
assistance in your application. For additional information, see Working with User
Assistance.
7-71
Chapter 7
Working with Forms
For accessibility, you must associate the oj-label component to its JET form
component. For most JET form components you can do this using the oj-label's for
attribute and the JET form component's id attribute.
Labels are top aligned by default, following best practices for mobile devices.
7-72
Chapter 7
Working with Forms
You can modify the labels to display inline by adding the oj-label-inline class to the
oj-label element when you don’t want to use responsive design classes.
<div class='oj-form'>
<div class='oj-flex-bar'>
<div class='oj-flex-bar-start'>
<oj-label id="radiosetlabel" show-required="[[isRequired]]" class="oj-label-
inline"
help.definition='[[helpDef]]' help.source='[[helpSource]]'>radioset</oj-label>
</div>
<div class='oj-flex-bar-middle'>
<oj-radioset id="radioSetId" labelled-by="radiosetlabel"
required='[[isRequired]]'>
<oj-option id="blueopt" value="blue" name="rbb">Blue</oj-option>
<oj-option id="greenopt" value="green" name="rbb">Green</oj-option>
<oj-option id="redopt" value="red" name="rbb">Red</oj-option>
<oj-option id="limeopt" value="lime" name="rbb">Lime</oj-option>
<oj-option id="aquaopt" value="aqua" name="rbb">Aqua</oj-option>
</oj-radioset>
</div>
</div>
</div>
When the user runs the page, the label for the oj-radioset component displays inline.
The <oj-form-layout> wraps the label and input components separately. So, if label
component is an immediate child component of <oj-form-layout>, then the input
component will not generate label. In this case, input component ignores label and
other label related attributes.
The Oracle JET Cookbook includes additional examples for using help and required
modifiers. In addition, the cookbook contains examples for displaying access keys,
making multiple labels on one field accessible, and making multiple fields on one label
accessible. For details, see Labels.
7-73
Chapter 7
Working with Forms
The dropdown list displays when the user does one of the following:
• Clicks on the select box.
• Sets focus on the select box and starts typing or presses the Enter, Arrow Up, or
Arrow Down key.
If the number of options is less than the minimumResultsForSearch value, then by default
the search box is not displayed when the dropdown is open. However, if the user
starts typing when the select box is in focus, the dropdown will be open along with the
search box displayed.
For example, there are five items in the dropdown list shown above so a search box is
not displayed by default.
Tip:
If you want to provide the user with the ability to add items to the dropdown
list, use the Oracle JET oj-combobox-one and the oj-combobox-many custom
elements instead to create dropdown lists that support single and multiple
selection respectively. For information, see Working with Comboboxes.
The code sample below shows the markup used to create the multi-select component
shown in this section.
<div id="form1">
<oj-label for="multiSelect">Select Many</oj-label>
<oj-select-many id="multiSelect" value={{val}} style="max-width:20em">
<oj-option value="IE">Internet Explorer</oj-option>
<oj-option value="FF">Firefox</oj-option>
<oj-option value="CH">Chrome</oj-option>
<oj-option value="OP">Opera</oj-option>
<oj-option value="SA">Safari</oj-option>
</oj-select-many>
<div>
<br/>
7-74
Chapter 7
Working with Forms
Note:
For accessibility, the select component requires that you set the for attribute
on the label element to point to the id of the oj-select-one element or the oj-
select-many element. For information about creating accessible Oracle JET
components, see Using the Accessibility Features of Oracle JET
Components.
The code sample also defines max-width style attribute. Use max-width instead of width
to ensure that the component adjusts its width automatically when the display width
changes.
The code that defines the val value is defined in the ValueModel() function, shown
below. val is defined as a Knockout observable, with its initial values set to Chrome and
Safari.
function(oj, ko, $)
{
function selectModel () {
//simple select
this.selectVal = ko.observableArray(['Chrome']);
this.browsers = ko.observableArray([
{value: 'Internet Explorer', label: 'Internet Explorer'},
7-75
Chapter 7
Working with Forms
//group
this.groupVal = ko.observableArray(['CA']);
this.groupData = ko.observableArray([
{label: "Alaskan/Hawaiian Time Zone",
children: [
{value: "AK", label: "Alaska"},
{value: "HI", label: "Hawaii"}
]},
{label: "Pacific Time Zone",
children: [
{value: "CA", label: "California"},
{value: "NV", label: "Nevada"},
{value: "OR", label: "Oregon"},
{value: "WA", label: "Washington"}
]}
]);
7-76
Chapter 7
Working with Forms
]);
//option keys
this.optionsKeys = {label: 'regions', children: 'states',
childKeys: {value: 'state_abbr', label: 'state_name'}};
this.groupKeysVal = ko.observableArray(['CA']);
this.groupDataWithKeys = ko.observableArray([
{regions: "Alaskan/Hawaiian Time Zone",
states: [
{state_abbr: "AK", state_name: "Alaska"},
{state_abbr: "HI", state_name: "Hawaii"}
]},
{regions: "Pacific Time Zone",
states: [
{state_abbr: "CA", state_name: "California"},
{state_abbr: "NV", state_name: "Nevada"},
{state_abbr: "OR", state_name: "Oregon"},
{state_abbr: "WA", state_name: "Washington"}
]}
]);
}
$(
function() {
ko.applyBindings(new selectModel(), document.getElementById("containerDiv"));
}
);
});
Note:
A maximum of 15 rows will be displayed in the dropdown. If you have more
than 15 rows, the message, More results available, please filter
further., will be displayed. However, if renderMode attribute in the oj-select-
one or oj-select-many component is set to native, the maximum number of
rows displayed will be 15, but filtering is not available.
function(oj, ko, $)
{
function selectModel () {
this.selectVal = ko.observableArray(['CH', 'SA']);
this.browsers = [
{value: 'IE', label: 'Internet Explorer'},
{value: 'FF', label: 'Firefox'},
{value: 'CH', label: 'Chrome'},
{value: 'OP', label: 'Opera'},
{value: 'SA', label: 'Safari'}
7-77
Chapter 7
Working with Forms
];
$(
function() {
ko.applyBindings(new selectModel(), document.getElementById("containerDiv"));
}
);
});
The Oracle JET Cookbook contains complete examples for configuring oj-select-one
and oj-select-many at Select.
Topics
• About the oj-slider Component
• Creating Sliders
• Formatting Tips for oj-slider
7-78
Chapter 7
Working with Forms
Creating Sliders
To create the oj-slider component, create an oj-slider element and assign an id to
it. Create a HTML oj-label element to add the for attribute points to the id of the oj-
slider component. Use the component's min and max attributes to set the slider range
and the component’s value attribute to set the thumb’s initial value.
Note:
For accessibility, the div container includes a label element that
associates the oj-slider component with the label. See the oj-slider
API documentation for accessibility details and associated keyboard and
touch end user support.
2. Add code to your application script that sets the values for the attributes you
specified in the previous step. The view model for the basic slider is shown below.
require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'ojs/ojslider'],
function(oj, ko, $) {
function SliderModel() {
var self = this;
self.max = ko.observable(200);
self.min = ko.observable(0);
7-79
Chapter 7
Working with Forms
self.value = ko.observable(100);
self.step = ko.observable(10);
}
var sliderModel = new SliderModel();
$(
function() {
ko.applyBindings(sliderModel, document.getElementById('slider-container'));
}
);
});
The step attribute indicates the size of the interval the slider takes between the min
and max values.
Note:
The full specified value of the range (max - min) should be evenly
divisible by step.
To specify a width as a percentage of the maximum width available, set the max-
width style to a percentage.
style:'max-width:100%'
• To create a vertical slider, set the oj-slider component’s orientation option to
vertical.
<oj-label for="slider-id"> ojSlider component </oj-label>
<br>
<oj-slider id="slider-id" orientation="vertical" value="{{value}}"
min="[[min]]" max="[[max]]" step="[[step]]"
style="height: 150px"> </oj-slider>
• To change the vertical slider’s height, set the style attribute directly on the oj-
slider element.
7-80
Chapter 7
Working with Forms
• To display a slider that displays a value but does not allow interaction, set the
component’s disabled option to true.
Cookbook Examples
The Oracle JET cookbook includes the complete examples shown in this section at
Sliders. You can also find examples that show disabled sliders and sliders with icons
on the bar to manipulate the thumb.
To create the oj-switch component, add the oj-switch element directly in the HTML
file and assign it an id. Use the component's value attribute to set the initial state to
true or false.
The following code sample shows the markup for the oj-switch shown in this section.
For accessibility, the form container includes a label element where the for attribute
points to the id of the oj-switch component. See the oj-switch API documentation for
accessibility details and associated keyboard and touch end user support.
<div id="componentDemoContent" style="width: 1px; min-width: 100%;">
<oj-label class="oj-label" for="switch">switch component</oj-label>
<oj-switch id="switch" value="{{isChecked}}"></oj-switch><br/><br/>
<span> switch is <oj-bind-text value="[[ isChecked() ? 'ON' : 'OFF']]"></oj-bind-
text></span>
</div>
The isChecked variable specified for the oj-switch component's value attribute is
defined in the application's main script, shown below. In this example, the isChecked
variable is a boolean set to true by a call to the Knockout observable() function.
define(['ojs/ojcore', 'knockout', 'ojs/ojswitch'],
function(oj, ko) {
function SwitchModel() {
var self = this;
self.isChecked = ko.observable(false);
}
return SwitchModel;
}
);
7-81
Chapter 7
Working with Forms
Tip:
You can configure oj-switch to display inline with its label.
To disable the switch, set the component's disabled attribute to true. To make the
switch read only, set the component’s readOnly attribute to true.
The Oracle JET cookbook includes the complete example shown in this section at
Switches. You can also find examples that implement disabled, read only, and inline
switches.
You can create multiple validators on a component. The image below shows an
example that uses multiple validators on an oj-input-number component.
7-82
Chapter 7
Working with Layout and Navigation
The Oracle JET Cookbook contains complete examples that you can use to customize
help, converter and validator hints, and messaging content. See User Assistance.
For information about validating and converting input on the Oracle JET input
components, see Validating and Converting Input. For information about using and
customizing Oracle JET user assistance, see Working with User Assistance.
Topics:
• Working with Accordions
• Working with Collapsibles
• Working with Dialogs
• Working with Masonry Layouts
• Working with Nav Lists
• Working with offCanvasUtils
• Working with Panels
• Working with Popups
• Working with Tab Bars
For information about the flex layout (oj-flex*) and responsive grid (oj-size) classes,
see Designing Responsive Applications
7-83
Chapter 7
Working with Layout and Navigation
You can define the JET Accordion by adding the oj-accordion element in the HTML
file. Each child of the accordion must be an oj-collapsible element.
The following code shows a portion of the markup used to create the accordion shown
in this section. In this example, the first collapsible (Header 1) is expanded.
<oj-accordion id="accordionPage">
<oj-collapsible id="c1">
<span><span><span class="demo-icon-font demo-education-icon-24"></span> Header
1</span></span>
<p class="oj-p">Content 1.</p>
</oj-collapsible>
<oj-collapsible id="c3">
<span>Header 3</span>
<p class="oj-p">Content 2.</p>
</oj-collapsible>
<oj-collapsible id="c4">
<span>Header 4</span>
<p class="oj-p">Content 3.</p>
</oj-collapsible>
</oj-accordion>
The Oracle JET Cookbook at Accordions contains the sample code to create the
accordion pictured in this section. You can also find examples for expanding multiple
child elements and responding to events.
For additional information about working with the oj-collapsible component, see
Working with Collapsibles.
You can define the collapsible by directly adding the oj-collapsible element in the
HTML file. Add the header and content elements as children of the oj-collapsible
element. You can use any valid markup to represent the header and block of content.
In this example, the collapsible is using the h3 element to contain the header and a p
element to contain the content.
<oj-collapsible id="collapsiblePage">
<h3 id="h">Header 3</h3>
<p id="c">I'm a Collapsible.</p>
</oj-collapsible>
To apply the binding, you can use the Knockout applyBindings() method and reference
the id of the element that contains the oj-collapsible element.
require(['knockout', 'jquery', 'ojs/ojcore', 'ojs/ojknockout', 'ojs/ojcollapsible'],
function(ko, $) {
7-84
Chapter 7
Working with Layout and Navigation
$(
function() {
ko.applyBindings(null, document.getElementById('collapsiblePage'));
}
);
}
);
The Oracle JET Cookbook Collapsibles demos contain the complete example for
creating this collapsible as well as examples that show nested collapsibles,
collapsibles with different header elements or borders, and event handling.
Typically, you create the dialog by directly adding the oj-dialog element in the HTML
file. Define the dialog title in the oj-dialog element's title attribute. Add content to the
dialog using the slot="body" and slot="footer" .
The oj-dialog element includes support for the header close icon and close handler,
but you must add your own markup for creating the OK button in the slot="footer"
section, as shown in the code sample below.
<div id="dialogWrapper">
<oj-dialog style="display:none" id="modalDialog1" title="Modal Dialog">
<div slot="body">
The dialog window can be moved, resized and closed with the 'x' icon.
Arbitrary content can be added to the the body and footer sections.
</div>
<div slot="footer">
<oj-button id="okButton" on-oj-action="[[close]]">OK
</oj-button>
</div>
</oj-dialog>
<oj-button id="buttonOpener" on-oj-action="[[open]]">
Open Modal Dialog
</oj-button>
</div>
You must also add code to handle the events on the OK and button opener buttons, in
addition to the Knockout applyBindings() call that completes the component binding.
7-85
Chapter 7
Working with Layout and Navigation
The Oracle JET Cookbook includes the complete code for this example at Dialogs.
You can also find samples for creating a modeless oj-dialog and dialogs rendered
with custom headers and other display options.
7-86
Chapter 7
Working with Layout and Navigation
Topics:
• Configuring Masonry Layouts
• Understanding the oj-masonry-layout Layout Process
• oj-masonry-layout Size Style Classes
The code sample below shows the markup for the basic oj-masonry-layout shown in
this section. The example uses the oj-bind-for-each element to iterate through the
child elements.
<div id="masonrylayout-basic-example">
<div class="demo-scroll-container">
<oj-masonry-layout id="masonryLayout">
<oj-bind-for-each data="[[chemicals]]">
<template>
<div :class="[[$current.data.sizeClass + ' oj-panel demo-title']]">
<oj-bind-text value="[[$current.data.name]]"></oj-bind-text>
7-87
Chapter 7
Working with Layout and Navigation
</div>
</template>
</oj-bind-for-each>
</oj-masonry-layout>
</div>
</div>
The size of each tile is determined by the value in the sizeClass property which is
defined in the application's main script shown below.
require(['ojs/ojcore', 'knockout', 'jquery',
'ojs/ojknockout', 'ojs/ojmasonrylayout'],
function(oj, ko, $)
{
$(
function()
{
function MyModel() {
var self = this;
self.chemicals = [
{ name: 'Hydrogen',
sizeClass: 'oj-masonrylayout-tile-2x1' },
{ name: 'Helium',
sizeClass: 'oj-masonrylayout-tile-1x2' },
{ name: 'Lithium',
sizeClass: 'oj-masonrylayout-tile-1x1' },
{ name: 'Beryllium',
sizeClass: 'oj-masonrylayout-tile-1x1' },
{ name: 'Boron',
sizeClass: 'oj-masonrylayout-tile-1x1' },
{ name: 'Carbon',
sizeClass: 'oj-masonrylayout-tile-1x1' },
{ name: 'Nitrogen',
sizeClass: 'oj-masonrylayout-tile-1x1' },
{ name: 'Oxygen',
sizeClass: 'oj-masonrylayout-tile-1x1' }
];
}
ko.applyBindings(new MyModel(),
document.getElementById('masonrylayout-basic-example'));
}
);
});
The Oracle JET Cookbook at Masonry Layouts contains the complete example for the
basic masonry layout shown in this section. You can also find demos that illustrate the
different tile sizes, and masonry layouts that let you resize, reorder, and flip tiles.
Note:
The oj-masonry-layout component does not provide accessibility features
such as keyboard navigation. It is the responsibility of the application
developer to make the items in the layout accessible. For tips and additional
detail, see the oj-masonry-layout API documentation.
7-88
Chapter 7
Working with Layout and Navigation
• Processes the tiles in the order in which they originally appear in the DOM.
• Determines the number of columns to display based on the width of the oj-
masonry-layout element and the width of a 1x1 tile.
• Determines the number of rows to display based on the number of columns and
the number and sizes of tiles to lay out.
• Lays out the grid cells in a left-to-right, top-to-bottom order (or right-to-left, top-to-
bottom order when the reading direction is right-to-left).
Tiles will be positioned in the first empty cell in which they fit. This can result in
empty cells in the layout. Subsequent tiles may fill those earlier gaps if they fit.
If the element is resized, oj-masonry-layout will redo the layout, and the number of
columns and rows may change.
Topics:
• Understanding Data Requirements for Nav Lists
• Working with Nav Lists and Knockout Templates
7-89
Chapter 7
Working with Layout and Navigation
The following code sample shows a portion of the markup used to create the oj-
navigation-list element with hierarchical static content and the Oracle JET
component binding. The markup uses ul and li HTML elements to define the nav
list, with nested lists for the hierarchical data. The markup also specifies
selectedItem as a Knockout observable in the component's selection attribute that
will update when the user selects a list item.
<div id="navlistdemo">
<p>
<div id="navlistcontainer" style="max-width:300px">
<oj-navigation-list aria-label="Choose a navigation item"
selection="{{selectedItem}}">
<ul >
<li id="home" >
<a href="#" >Home</a>
</li>
<li id="gettingStarted" >
<a href="#" >Getting Started</a>
</li>
<li id="cookbook">
<a href="#" >Cookbook</a>
</li>
7-90
Chapter 7
Working with Layout and Navigation
The code to apply the binding and define selectedItem is shown below. In this
example, the initial value of the Knockout observable is set to home, and the nav list
will initially display with the Home item selected.
require(['ojs/ojcore','knockout','jquery','ojs/ojknockout','ojs/
ojnavigationlist'],
function(oj, ko, $)
// this callback gets executed when all required modules are loaded
{
function ViewModel(){
this.selectedItem = ko.observable("home");
}
var vm = new ViewModel();
ko.applyBindings(vm, document.getElementById('navlistdemo'));
}
);
7-91
Chapter 7
Working with Layout and Navigation
Typically, you use one of the Oracle JET TreeDataSource class when your list data
contains groups. To use a TreeDataSource, specify the method that returns the tree
data in the data attribute for the oj-navigation-list element.
<div id="navlistdemo">
<div style="max-width:300px">
<oj-navigation-list
drill-mode="sliding"
selection="{{selectedListItem}}"
data="[[dataSource]]"
item.renderer="[[oj.KnockoutTemplateUtils.getRenderer('folder_template',
true)]]">
</oj-navigation-list>
</div>
</div>
• Oracle JET TableDataSource, including oj.CollectionTableDataSource
Use oj.CollectionTableDataSource when oj.Collection is the model for the
underlying data. The nav list will automatically react to model events from the
underlying oj.Collection.
To use a TableDataSource, specify the method that returns the table data in the
data attribute for the oj-navigation-list element.
<div id="navlistdemo">
<div style="max-width:300px">
<oj-navigation-list
selection="{{selectedItem}}"
data="[[dataSource]]"
item.renderer="[[oj.KnockoutTemplateUtils.getRenderer('server_template',
true)]]">
</oj-navigation-list>
</div>
</div>
• Oracle JET oj.ArrayDataProvider
Use the oj.ArrayDataProvider when you want to use an observable array or array
as data for nav list.
The following example shows the HTML markup using data attribute in the oj-
navigation-list element:
<oj-navigation-list
selection="{{selectedItem}}"
edge="top"
data="[[dataSource]]"
item.renderer="[[oj.KnockoutTemplateUtils.getRenderer('server_template',
true)]]">
</oj-navigation-list>
7-92
Chapter 7
Working with Layout and Navigation
Note:
If you do not specify a data source in the navigation list component's
data attribute, Oracle JET will examine the child elements inside the root
element and use it for static content. If the root element has no children,
Oracle JET will render an empty list.
The code sample below shows a portion of the markup and template for a nav list
using an oj.ArrayDataProvider object for its data. In this example, the template is
named itemTemplate.
<div id="navlistdemo">
<div>
<oj-navigation-list selection="{{selectedItem}}"
edge="top"
data="[[dataSource]]"
as="item">
<template slot="itemTemplate">
<li :id="[[item.data.id]]"
:class="[[{'oj-disabled' : item.data.disabled === 'true'}]]">
<a href="#"><oj-bind-text value="[[item.data.name]]"> </oj-bind-text></a>
</li>
</template>
</oj-navigation-list>
</div>
<br>
<div>
<p class="bold">Last selected list item:
<span id="results"> <oj-bind-text value="[[selectedItem]]"> </oj-bind-text> </
span>
</p>
</div>
</div>
At runtime, the nav list iterates through the array and displays the content contained in
name and applies styling for disabled items.
7-93
Chapter 7
Working with Layout and Navigation
You can find the complete code sample for this nav list at Nav List
(ArrayDataProvider).
In your application’s viewModel, define the key set. The following example sets the
navigation list to expand the cookbook item on initial display. Note that you must also
add the ojkeyset module and keySet function to the require definition.
require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojkeyset', 'ojs/ojknockout', 'ojs/
ojnavigationlist', 'ojs/ojswitch'],
function(oj, ko, $, keySet)
{
function ViewModel(){
this.itemOnly = function(context) {
return context['leaf'];
};
this.selectedItem = ko.observable('home');
this.isContrastBackground = ko.observable(false);
this.expanded = new keySet.ExpandedKeySet(['cookbook']);
...contents omitted
);
7-94
Chapter 7
Working with Layout and Navigation
The following image shows two examples of regions configured to slide in at the start
of the viewport. In the region configured for push, offCanvasUtils pushes aside as
much of the viewport content as needed to display the region. In the region configured
for overlay, the content is displayed over the main content, obscuring the content.
7-95
Chapter 7
Working with Layout and Navigation
In this example, three panels are displayed with three different colors. The content for
the first two panels is defined using the HTML header and paragraph elements. The
content for the third panel is defined as an oj-collapsible element. The oj-panel-*
classes are defined on the div elements that represent the panels.
<div id="panelPage">
<div class="oj-flex">
<div class="oj-panel oj-sm-margin-2x demo-mypanel">
<h3>Header</h3>
<p>Hello world!</p>
How is it going out there?
</div>
When panels are on the same row they will have equal height because they are in a
div element defined with the oj-flex style class. You can change the default behavior
by adding responsive classes to set alignment. For an example, see Flex Layouts -
Align.
You can customize the size or display using styles, or you can create a custom style
class. In this example, the panel is customized using the demo-mypanel class, shown
below.
.demo-mypanel {
width: 200px;
}
The Oracle JET Cookbook contains the complete code for this example at Panel
Content. You can also find an example that shows the available panel colors at Panel
Colors.
For additional information about using and customizing the Oracle JET themes
included with Oracle JET, see Theming Applications. For more information about the
oj-collapsible component, see Working with Collapsibles.
7-96
Chapter 7
Working with Layout and Navigation
Topics:
• Working with oj-popup
• Working with the Oracle JET Popup Framework
You can create the popup by directly adding the oj-popup element in the HTML file.
Add the popup's content as the child of that element. For the image shown above, the
popup is defined on the oj-popup element, with the content defined in the span element.
<oj-popup style="display:none" id="popup1">
<span class="blink-rainbow">Hello World!!!</span>
</oj-popup>
In this example, the popup displays when the user clicks Go. The Go button is defined
as an oj-button element that references the popup's id attribute to display the popup
when clicked.
<div id="popupWrapper">
<oj-popup style="display:none" id="popup1">
<span class="blink-rainbow">Hello World!!!</span>
</oj-popup>
<oj-button id="btnGo"
on-oj-action="[[function(data)
{
document.querySelector('#popup1').open('#btnGo');
}]]">
Go
</oj-button>
</div>
In this example, the popup’s modality defaults to modeless which means that the
popup will not block user input on the page behind the popup. You can change this to
modal by setting the element’s modality attribute as described in the oj-popup API
documentation.
<oj-popup class="demo-popup" id="popup1"
tail="none" position.my.horizontal="center" position.my.vertical="bottom"
position.at.horizontal="center" position.at.vertical="bottom"
position.of="window" position.offset.y="-10"
modality="modal"
on-oj-animate-start='[[listener]]'>
7-97
Chapter 7
Working with Layout and Navigation
When the user clicks the Go button now, the popup blocks user input of the page
behind the popup with a blocking overlay pane.
The Oracle JET Cookbook contains the complete example for this popup at Popups.
You can also find examples that customize the popup and that use the popup as a
tooltip for accessibility.
Note:
The oj-popup component defines default roles for accessibility and keyboard
navigation. You can find details about the oj-popup component’s accessibility
support in the oj-popup API documentation. In addition, you can find some
general recommendations about best practices for making the gestures that
launch the popup accessible.
7-98
Chapter 7
Working with Layout and Navigation
When the user clicks the Go button to display the popup, Oracle JET re-parents it into
the oj_zorder_container as a direct child of the body element.
7-99
Chapter 7
Working with Layout and Navigation
parented to the zorder container, and the active popup will be the popup with the
highest z-index weight.
For example, at initial display, the oj-popup's layer is marked with the oj-popup-layer
style which has a z-index value of 1000. Popups of the same type are assigned the
same z-index values. If there are multiple popups open, the popup that has active
focus will be assigned a greater z-index value of 1001, and the oj-popup-layer.oj-
focus-within style is applied.
In most cases, you should never need to modify the CSS z-index defaults since the
resulting behavior may be unpredictable. If needed, however, you can update the
SCSS variables used to generate the application's CSS or modify the CSS styles
directly. The following table shows the default CSS style selectors, SCSS variables,
and z-index values.
When popup elements are initially defined on the page, they are defined with a default
z-index value of 1 using the CSS selectors shown in the following table. The root
popup is absolutely positioned on the page in relation to its parent container. Setting
the value to 1 ensures that the root popup's children will display above the popup.
7-100
Chapter 7
Working with Layout and Navigation
For additional information about Oracle JET's popup strategy, see the oj-popup API
documentation.
For additional information about Oracle JET's use of CSS and SCSS, see Theming
Applications. For more information about the CSS z-index property, see http://
www.w3.org/wiki/CSS/Properties/z-index.
Tip:
Tab bars are designed for toggling between content only. If you want to
perform operations on the content within a tab, use oj-toolbar instead. See
Working with Toolbars.
The Oracle JET tab bar enhances a HTML list element into a WAI-ARIA compliant,
mobile friendly component with advanced interactive features. The tab bar is
customizable, and you can configure the tabs to show icons, text, or both. The tab
bar’s position is also customizable, and you can display the tab bar on the top, bottom,
or side (start or end) of the tab's content.
To create a tab bar, add the oj-tab-bar element to a HTML container element, typically
a div element, on your page. Add the HTML unordered list element (ul) as a child to
the tab bar, and add li child elements to the list for each tab. Each list item must
include an anchor which contains the list item’s text and optional icon.
7-101
Chapter 7
Working with Layout and Navigation
The code sample below shows the markup for an oj-tab-bar that contains the four
tabs shown in the image. The edge attribute specifies the position of the oj-tab-bar. In
this example, it’s set to top , and you can also set it to bottom, start, or end.
<div id="demo-container" class="oj-flex demo-edge-top">
<oj-tab-bar id="hnavlist"
edge="top"
selection="{{selectedItem}}">
<ul>
<li id="home">
<a href="#" aria-controls="home-tab-panel" id="home-tab">
<span class="oj-tabbar-item-icon demo-home-icon-24 demo-icon-font-24">
</span>
Home
</a>
</li>
<li id="settings">
<a href="#" aria-controls="settings-tab-panel" id="settings-tab">
<span class="oj-tabbar-item-icon demo-gear-icon-16 demo-icon-font-24">
</span>
Settings
</a>
</li>
<li id="about">
<a href="#" aria-controls="about-tab-panel" id="about-tab">
<span class="oj-tabbar-item-icon demo-info-icon-24 demo-icon-font-24">
</span>
About
</a>
</li>
<li id="contact">
<a href="#" aria-controls="contact-tab-panel" id="contact-tab">
<span class="oj-tabbar-item-icon demo-email-icon-24 demo-icon-font-24">
</span>
Contact
</a>
</li>
</ul>
</oj-tab-bar>
</div>
Tip:
You can also use the tab bar’s data attribute to populate the tab bar with data
from a table data source or array data provider. See the Tab Bars cookbook
demos for examples.
Adding Content
To provide content to the tabs, add the oj-switcher element to the tab bar’s container.
oj-switcher dynamically decides which child element to display based on the value in
its value attribute, which must match the value in the tab bar’s selection attribute. The
switcher’s slot attribute also determines which tab’s content to display and must
match an id in the tab bar’s list.
<div id="demo-container" class="oj-flex demo-edge-top">
<oj-tab-bar id="hnavlist"
edge="top"
7-102
Chapter 7
Working with Layout and Navigation
selection="{{selectedItem}}">
<ul>
<li id="home">
<a href="#" aria-controls="home-tab-panel" id="home-tab">
<span class="oj-tabbar-item-icon demo-home-icon-24 demo-icon-font-24">
</span>
Home
</a>
</li>
... contents omitted
</oj-tab-bar>
<oj-switcher value="[[selectedItem]]">
<div slot="home"
id="home-tab-panel"
role="tabpanel"
aria-labelledby="home-tab">
<div class="demo-tab-content">
<h2>Home page content area</h2>
<p>Home page sample content</p>
</div>
</div>
<div slot="settings"
id="settings-tab-panel"
role="tabpanel"
aria-labelledby="settings-tab">
<div class="demo-tab-content">
<h2>Settings content area</h2>
<p>Settings sample content</p>
</div>
</div>
<div slot="about"
id="about-tab-panel"
role="tabpanel"
aria-labelledby="about-tab">
<div class="demo-tab-content">
<h2>About content area</h2>
<p>About sample content</p>
</div>
</div>
<div slot="contact"
id="contact-tab-panel"
role="tabpanel"
aria-labelledby="contact-tab">
<div class="demo-tab-content">
<h2>Contact page content area</h2>
<p>Contact sample content</p>
</div>
</div>
</oj-switcher>
</div>
7-103
Chapter 7
Working with Layout and Navigation
function(oj, ko, $) // this callback gets executed when all required modules are
loaded
{
function ViewModel(){
this.selectedItem = ko.observable("home");
}
var vm = new ViewModel();
$(function() {
ko.applyBindings(vm, document.getElementById('tabbardemo'));
});
}
);
For a complete example that illustrates the use of oj-switcher with oj-tab-bar, see
Switcher.
Adding Interactivity
You can add the ability to add, remove, or reorder a tab within the tab bar. To enable
this functionality, first create the tab bar using a supported data source.
• Removing a tab
Add the oj-removable class to each removable tab. Add the on-oj-remove attribute
to the oj-tab-bar to specify an event handler to remove the tab from the data
source.
Important:
For accessibility, add the oj-menu child element to the tab bar as a
contextmenu slot with an option marked as data-oj-command="oj-tabbar-
remove".
item.renderer="[[oj.KnockoutTemplateUtils.getRenderer('server_template'
, true)]]"
on-oj-remove="[[onRemove]]">
<oj-menu slot="contextMenu"
style="display:none"
aria-label="Actions">
<oj-option data-oj-command="oj-tabbar-remove">
Removable
</oj-option>
</oj-menu>
</oj-tab-bar>
• Adding a tab
To add a tab, add code to your view model to push the new tab to the data source.
Provide the user with a dialog or other prompt to add the tab and reference the
event handler that will add the tab to the data source.
7-104
Chapter 7
Working with Layout and Navigation
Important:
For accessibility, define a context menu on the page that includes entries
for cutting and pasting items by specifying the data-oj-command attribute
on the menu item with one of the values: oj-tabbar-cut, oj-tabbar-paste-
before, and oj-tabbar-paste-after.
<oj-tab-bar id="tabbar"
aria-label="Tabs using json data"
selection="settings"
data="[[dataSource]]"
item.renderer="[[renderer]]"
reorderable="enabled"
edge="[[edge]]"
on-oj-reorder="[[handleReorder]]">
<oj-menu id="itemMenu" slot="contextMenu" style="display:none" aria-
label="Item Reorder">
<oj-option id="cutItem" data-oj-command="oj-tabbar-cut"></oj-
option>
<oj-option id="pasteBeforeItem" data-oj-command="oj-tabbar-paste-
before"></oj-option>
<oj-option id="pasteAfterItem" data-oj-command="oj-tabbar-paste-
after"></oj-option>
</oj-menu>
</oj-tab-bar>
For the code that implements the event handlers for adding, removing, and reordering
tabs, see the Tab Bar - Add and Remove and Tab Bar - Reorder demos in the Oracle
JET Cookbook.
7-105
Chapter 7
Working with Visualizations
<oj-tab-bar
selection="{{selectedItem}}"
data="[[dataSource]]"
item.renderer="[[oj.KnockoutTemplateUtils.getRenderer('server_template',
true)]]">
</oj-tab-bar>
Note:
If you do not specify a data source in the tab bar component's data
attribute, Oracle JET will examine the child elements inside the root
element and use it for static content. If the root element has no children,
Oracle JET will render an empty tab bar.
Topics:
• Choosing a Data Visualization Component for Your Application
• Using Attribute Groups With Data Visualization Components
Charts
Charts show relationships among data and display values as lines, bars, and points
within areas defined by one or more axes.
7-106
Chapter 7
Working with Visualizations
Box Plot Displays the minimum, quartiles, median, Use to analyze the distribution of data. Box
and maximum values of groups of plots are also called box and whisker
numerical data. Groups display vertically or diagrams.
horizontally. You can also vary the box
width to make the width of the box
proportional to the size of the group.
Bubble Displays three measures using data Use to show correlations among three
markers plotted on a two-dimensional types of values, especially when you have
plane. The location of the markers a number of data items and you want to
represents the first and second measures, see the general relationships.
and the size of the data markers represents For example, use a bubble chart to plot
the proportional values of the third salaries (x-axis), years of experience (y-
measure. axis), and productivity (size of bubble) for
your work force. Such a chart enables you
to examine productivity relative to salary
and experience.
Combination Displays series of data whose values are Combination charts are commonly
represented by a combination of bars, configured as lines with bars for lines with
lines, or filled-in areas. stacked bars.
For example, you can use a line to display
team average rating with bars to represent
individual team member ratings on a yearly
basis.
Funnel Visually represents data related to steps in Use to watch a process where the different
a process as a three-dimensional chart that sections of the funnel represent different
represents target and actual values, and stages in the process, such as a sales
levels by color. The steps appear as cycle.
vertical slices across a horizontal cone- The funnel chart requires actual values and
shaped section. As the actual value for a target values against a stage value, which
given step or slice approaches the quota might be time.
for that slice, the slice fills.
7-107
Chapter 7
Working with Visualizations
Polar Displays series of data on a polar Use to display data with a cyclical x-axis,
coordinate system. The polar coordinate such as weather patterns over months of
system can be used for bar, line, area, the year, or for data where the categories
combination, scatter, and bubble charts. in the x-axis have no natural ordering, such
Polygonal grid shape (commonly known as as performance appraisal categories.
radar) is supported for polar line and area
charts.
Pyramid Displays values as slices in a pyramid. The Use to display hierarchical, proportional
area of each slice represents its value as a and foundation-based relationships,
percentage of the total value of all slices. process steps, organizational layers, or
topics interconnections.
Range Displays a series of data whose values are Use to display a range of temperatures for
represented either as an area or bar each day of a month for a city.
proportional to the data values.
Scatter Displays two measures using data markers Use to show correlation between two
plotted on a two-dimensional plane. different kinds of data values, such as
sales and costs for top products. Scatter
charts are especially useful when you want
to see general relationships among a
number of items.
7-108
Chapter 7
Working with Visualizations
Gauges
Gauges focus on a single value, displayed in relation to minimum, maximum, or
threshold values.
LED Graphically depicts a measurement, such Use to highlight a specific metric value in
as a key performance indicator (KPI). relation to its threshold.
Several styles of shapes are available,
including round or rectangular shapes that
use color to indicate status, and triangles or
arrows that point up, left, right, or down in
addition to the color indicator.
Rating Displays and optionally accepts input for a Use to show ratings for products or
metric value. services, such as the star rating for a
movie.
7-109
Chapter 7
Working with Visualizations
Gantt Displays bars that indicate the start and Use to display project schedules.
end date of tasks.
Legend Displays a panel which provides an Consider using the legend component
explanation of the display data in symbol when multiple visualizations on the same
and label pairs. page are sharing a coloring scheme. For
an example using ojLegend with a bubble
chart, see Using Attribute Groups With
Data Visualization Components.
NBox Displays data items across two Use to visualize and compare data across
dimensions. Each dimension can be split a two-dimensional grid, represented
into multiple ranges, whose intersections visually by rows and columns.
result in distinct cells representing data
items.
PictoChart Uses stamped images to display discrete Common in infographics. Use when you
data as a visualization of an absolute want to use icons to:
number or the relative size of different parts • visualize a discrete value, such as the
of a population. number of people in a sample that
meets a specified criteria.
• highlight the relative sizes of the data,
such as the number of people
belonging to each age group in a
population sample.
7-110
Chapter 7
Working with Visualizations
Thematic Displays data that is associated with a Use to show trends or patterns in data with
Map geographic location. a spatial element to it.
Timeline Displays a set of events in chronological Use to display time specific events in
order and offers rich support for graphical chronological order.
data rendering, scale manipulation,
zooming, resizing, and objects grouping.
Treemap Displays quantitative hierarchical data Use for identifying trends for large
across two dimensions, represented hierarchical data sets, where the
visually by size and color. Uses nodes to proportional size of the nodes represents
reference the data in the hierarchy. Nodes their importance compared to the whole.
are displayed as a set of nested rectangles. Color can also be used to represent an
additional dimension of information
Use treemaps if you are primarily
interested in displaying two metrics of data
using size and color at a single layer of the
hierarchy.
For examples that implement visualization components, see the Oracle JET Cookbook
at Data Visualizations.
7-111
Chapter 7
Working with Visualizations
Note:
To use an Oracle JET data visualization component, you must configure your
application to use RequireJS. For details about adding RequireJS to your
application, see Use RequireJS to Manage Library, Link, and Script
References.
7-112
Chapter 7
Working with Visualizations
The code excerpt below shows the JavaScript to create the bubble chart with color
and shape attribute groups. The code relating to the attribute groups is highlighted in
bold font.
require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'ojs/ojchart','ojs/
ojlegend'],
function(oj, ko, $)
{
var colorHandler = new oj.ColorAttributeGroupHandler();
var shapeHandler = new oj.ShapeAttributeGroupHandler();
shapeHandler.getValue();
function ChartModel() {
var data = [
{x: 15, y: 25, z: 5, company: "Coke", year: "2010"},
{x: 25, y: 30, z: 12, company: "Coke", year: "2011"},
{x: 25, y: 45, z: 12, company: "Coke", year: "2012"},
{x: 15, y: 15, z: 8, company: "Pepsi", year: "2010"},
{x: 20, y: 35, z: 14, company: "Pepsi", year: "2011"},
{x: 40, y: 55, z: 35, company: "Pepsi", year: "2012"},
{x: 10, y: 10, z: 8, company: "Snapple", year: "2010"},
{x: 18, y: 55, z: 10, company: "Snapple", year: "2011"},
{x: 40, y: 50, z: 18, company: "Snapple", year: "2012"},
{x: 8, y: 20, z: 6, company: "Nestle", year: "2010"},
{x: 11, y: 30, z: 8, company: "Nestle", year: "2011"},
{x: 30, y: 40, z: 15, company: "Nestle", year: "2012"}
];
this.seriesItems = [
{name: "Coke", displayInLegend: 'off', items: []}, {name: "Pepsi",
displayInLegend: 'off', items:[]},
{name: "Snapple", displayInLegend: 'off', items: []}, {name: "Nestle",
displayInLegend: 'off', items:[]}
];
this.bubbleGroups = [];
7-113
Chapter 7
Working with Visualizations
$(
function(){
ko.applyBindings(chartModel, document.getElementById('chart-container'));
}
);
});
The bubble chart's legend uses the same attribute group handlers for color and shape.
Since the color and shape values were initially set in the colorHandler.getValue() and
shapeHandler.getValue() calls above, the calls to getValue() below will return the same
values for color and shape.
...content omitted
var legendSections = {sections: [
{title: "Year", items: [
{markerShape:shapeHandler.getValue("2010"), text: "2010", id:
"2010"},
{markerShape:shapeHandler.getValue("2011"), text: "2011", id:
"2011"},
{markerShape:shapeHandler.getValue("2012"), text: "2012", id: "2012"}
]},
{title: "Brand", items:[
{color: colorHandler.getValue("Coke"), text: "Coke", id: "Coke"},
{color: colorHandler.getValue("Pepsi"), text: "Pepsi", id: "Pepsi"},
{color: colorHandler.getValue("Snapple"), text: "Snapple", id:
"Snapple"},
{color: colorHandler.getValue("Nestle"), text: "Nestle", id:
"Nestle"}
]}
]};
self.legendSections = ko.observable(legendSections);
}
7-114
Chapter 7
Working with Visualizations
$(
function(){
ko.applyBindings(chartModel, document.getElementById('chart-container'));
The Oracle JET Cookbook provides the complete code for implementing both bubble
charts at Bubble Charts.
You can also initialize an attribute group with match rules which consist of a map of
key value pairs for categories and the matching attribute values. For example, if you
wanted to specify colors for specific categories instead of using the default colors, you
could define the color attribute group with match rules.
var colorHandler = new oj.ColorAttributeGroupHandler({"soda":"#336699",
"water":"#CC3300",
"iced tea":"#F7C808"});
7-115
8
Working with Oracle JET Web
Components
Oracle JET Web Components are reusable pieces of user interface code that you can
embed as custom HTML elements. Web Components can contain Oracle JET
components, other Web Components, HTML, JavaScript, and CSS. You can create
your own Web Component or add one to your page.
Topics
• Typical Workflow for Working with Oracle JET Web Components
• About Web Components
• Creating Web Components
• Best Practices for Web Component Creation
• Testing Web Components
• Adding Web Components to Your Page
• Building Web Components
• Optimizing Web Component Files
8-1
Chapter 8
About Web Components
8-2
Chapter 8
About Web Components
• Metadata: Data provided in JSON format which defines the Web Component’s
required properties: name, version, and jetVersion. Metadata may also define
optional properties, including description, displayName, dependencies, icon, methods,
events, and slots.
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.",
8-3
Chapter 8
About Web Components
"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
}
8-4
Chapter 8
About Web Components
},
"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), and disconnected (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.
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.
Each Web Component file should use the following naming convention to match the
purpose:
• my-web-component—view.html: view template
• my-web-component—viewModel.js: ViewModel
• component.json: metadata
8-5
Chapter 8
About Web Components
8-6
Chapter 8
About Web Components
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.
8-7
Chapter 8
Creating Web Components
The procedure below lists the high level steps to create a Web 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 Web Component - Basic. You can
also download the demo files by clicking the download button ( ) in the cookbook.
To create a Web Component:
1. Determine a name for your Web 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.
8-8
Chapter 8
Creating Web Components
Note:
Oracle JET also reserves the oj namespace and prefixes.
8-9
Chapter 8
Creating Web Components
• If you installed the Oracle JET CLI and want to create a Web 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-name
• If you’re not using the Oracle JET CLI, create a jet-composites folder in your
application’s js folder, and add folders containing the name of each Web
Component you will create.
For the demo-card example, create the jet-composites folder and add a demo-
card folder to it. You’ll create the individual Web Component files in the
remaining steps.
4. Determine the properties, methods, and events that your Web Component will
support and add them to the component.json file in the Web Component’s root
folder, creating the file if needed.
8-10
Chapter 8
Creating Web Components
Important:
The name of the Web Component properties, event listeners, and
methods should avoid collision with the existing HTMLElement
properties, event listeners, and methods. Additionally, the property name
slot should 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 Web 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 <6.0.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 Web Component.
You can also add metadata that defines methods and events as shown in the
cookbook’s Web 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.",
8-11
Chapter 8
Creating Web Components
"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"
}
}
}
}
}
5. If your Web Component contains a ViewModel, add its definition to web—component-
name-viewModel.js in the Web Component’s root folder, creating the file if needed.
The code sample below shows the ViewModel for the demo-card Web
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
*/
8-12
Chapter 8
Creating Web Components
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;
}
)
6. In the Web Component’s root folder, add the View definition to web—component-
name-view.html, creating the file if needed.
The View for the demo-card Web Component is shown below. Any property
defined in the component’s metadata is accessed using the $properties property
of the View binding context.
<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, with aria-label specified for
the contact’s name. In general, follow the same accessibility guidelines for the
8-13
Chapter 8
Creating Web Components
Web Component View markup that you would anywhere else within the
application.
7. If you’re not using the Oracle JET CLI, create the loader.js RequireJS module and
place it in the Web Component’s root folder.
The loader.js module defines the Web Component dependencies and registers
the component’s tagName, demo-card in this example.
define(['ojs/ojcomposite', 'text!./demo-card-view.html', './demo-card-viewModel',
'text!./component.json', 'css!./demo-card-styles'],
function(Composite, view, viewModel, metadata) {
Composite.register('demo-card', {
view: view,
viewModel: viewModel,
metadata: JSON.parse(metadata)
});
}
);
8. Configure any custom styling that your Web Component will use.
• If you only have a few styles, add them to web—component-name-styles.css file in
the Web Component’s root folder, creating the file if needed.
For example, the demo-card Web 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.css in the
Web Component - Basic cookbook sample.
/* This is to prevent the flash of unstyled content before the Web Component
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:
8-14
Chapter 8
Creating Web Components
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
d. To compile Sass, at a terminal prompt type ojet build or ojet serve with
the --sass flag and application-specific options.
ojet build|serve [options] --sass
ojet build --sass will compile your application and generate web—
component-name-styles.css and web—component-name-styles.css.map files in
the default platform’s folder. For a web application, the command will
place the CSS in web/js/js-composites/web—component-name.
ojet serve --sass will also compile your application but will display the
web application in a running browser with livereload enabled. If you save
a change to web—component-name-styles.scss in the application’s src/js/
jet-composites/web—component-name folder, Oracle JET will compile Sass
again and refresh the display.
Tip:
For help with ojet command syntax, type ojet help at a terminal
prompt.
8-15
Chapter 8
Best Practices for Web Component Creation
9. If you want to add documentation for your Web Component, add content to
README.md in your Web Component's root folder, creating the file if needed.
Your README.md file 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 for README file format is
markdown. For help with markdown, refer to https://guides.github.com/features/
mastering-markdown/.
Topics
• Recommended Standard Patterns and Coding Practices
• CSS and Theming Standards
Component Versioning
Your Web Component must be assigned a version number in semantic version format.
Important:
When assigning and incrementing the version number associated with your
components, be sure to follow semantic version rules and update Major,
Minor and Patch version numbers appropriately. By doing so, component
consumers will have a clear understanding about the compatibility and costs
of migrating between different versions of your component.
Translatable Resources
Developers who want to localize Web Component translatable resources now get a
resource bundle (template) when they create their Web Component. These
components should use the standard Oracle JET mechanism using the ojL10n
requireJS plugin. You must store the translation bundles in a resources/nls
8-16
Chapter 8
Best Practices for Web Component Creation
subdirectory within your Web Component’s root folder. You can declare the languages
and locales that you support in the Web Component metadata.
Peer-to-Peer Communication
Components must prefer a shared observable provided by the consumer over any kind
of secret signaling mechanism when you are dealing with a complex integration. For
example, a filter component and a data display component. By using a shared
observable you can pre-seed and programmatically interact with the components
through the filter.
Alternatively, you can use events and public methods based on one of the following
approaches being used:
• A hierarchical relationship between the source and receiver of the event
• The identity of the source being passed to the receiver
Note:
In some runtime platforms, the developer doing the wiring may not have
access to component IDs to pass the relevant identity.
Note:
Under the web-component standards (shadow DOM), events will be re-
targeted as they transition the boundary between the component and the
consuming view. That is, the apparent identity of the raising element might
be changed, particularly in the case of Nested Web Component architecture
where the event would get tagged with the element representing the outer
Web Component rather than the inner Web Component. Therefore, you
should not rely on the event.target attribute to identify the Web Component
source when listening at the document level. Instead, the event.deepPath
attribute can be used to understand the actual origin of the event.
Object Scope
All properties and functions of Web Components should be confined to the scope of
the view model. No window or global scope objects should be created. Similarly, the
existence of window scope objects should not be assumed by the Web Component
8-17
Chapter 8
Best Practices for Web Component Creation
External References
If a Web Component must reference an external component, it should be part of the
formal API of the component. The formal API passes the component reference
through a property. For example, to allow the registration of a listener, the Web
Component code requires a component reference defined externally. You must not
allow Web Components to obtain IDs from hard-coded values, global storage, or
walking the DOM.
Subcomponent IDs
Within the framework if any component needs a specific ID, use context.unique or
context.uniqueId value to generate the ID. This ID is unique within the context of the
page.
ID Storage
Any generated IDs should not be stored across invocation, such as in local storage or
in cookies. The context.unique value for a particular Web Component may change
each time a particular view is executed.
LocalStorage
It is difficult to consistently identify a unique instance of a Web Component within an
application. So, it is advised not to allow a Web Component to utilize the local storage
of a browser for persisting information that is specific to an instance of that Web
Component. However, if the application provides a unique key through the public
properties of the component you can then identify the unique instance of the
component.
Additionally, do not use local storage as a secret signaling mechanism between
composites. You cannot assure the availability of the capability and so it is
recommended to exchange information through a shared JavaScript object or events
as part of the public API for the component(s).
String Overrides
Web Components will often contain string resources internally to service their default
needs for UI and messages. However, sometimes you may want to allow the
consumer to override these strings. To do this, expose a property for this purpose on
the component. By convention such a property would be called translations, and
within it you can have sub-properties for each translatable string that relates to a
required property on the component, for example requiredHint,
requiredMessageSummary, and so on. These properties can then be set on the
component tag using sub-property references. For example:
"properties" : {
"translations": {
"description": "Property to allow override of default messages",
"writeback" : false,
"properties" : {
8-18
Chapter 8
Best Practices for Web Component Creation
"requiredHint": {
"description": "Change the text of the required hint",
"type": "string"
},
"requiredMessageSummary": {
"description": "...",
"type": "string"
},
"requiredMessageDetail": {
"description": "...",
"type": "string"
}
}
}
}
}
Logging
Use oj.Logger to write log messages from your code in preference to console.log().
The Web Components should respect the logging level of the consuming application
and not change it. You should ideally prefix all log messages emitted from the
component with an identification of the source Web Component. As a preference, you
can use the Web Component name. The components should not override the writer
defined by the consuming application.
Expensive Initialization
Web Components should carry out minimum work inside the constructor function.
Expensive initialization should be deferred to the activated lifecycle method or later.
The constructor of a Web Component is invoked even if the component is not actually
added to the visible DOM. For example, if a constructor is invoked within a Knockout
if block. The further lifecycle phases will only occur when the component is actually
needed.
Service Classes
The use of global service classes, that is functionality shared across multiple Web
Components, can constitute an invisible contract that the consumer of your Web
Component has to know about. To avoid this, we recommend:
• Create the service as a module that every Web Component can explicitly set it as
require() block, thus removing the need for the consumer to do this elsewhere.
• Consider the timing issues that might occur if your service class needs some time
to initialize, for example fetching data from a remote service. In such cases, you
should be returning promises to the service object so that the components can
safely avoid trying to use the information before it is actually available.
Using ojModule
If you use ojModule in a Web Component and plan to distribute the Web Component
outside of your application, you must take additional steps to ensure that the contained
ojModule could be loaded from the location relative to the location of the Web
Component. Unless the View and ViewModel instances are being passed to ojModule
directly, you will need to provide the require function instance and the relative paths for
views and view models. The require function instance should be obtained by the
component loader module by specifying require as a dependency.
8-19
Chapter 8
Best Practices for Web Component Creation
• connected(context): Invoked after the View is first inserted into the DOM and then
each time the Web Component is reconnected to the DOM after being
disconnected.
• bindingsApplied(context): Invoked after the bindings are applied on the View.
Template Aliasing
JET components that support inline templates can now use an optional data-oj-as
attribute to provide template specific aliases for $current variable at the application
level. In the instances where the component must support multiple template slots as in
the case of chart and table components, a single alias may not be sufficient. In such
cases, you can use an optional data-oj-as attribute on the template element. For more
information on the usage of this optional attribute with template slots, see oj-bind-
template-slot API documentation.
8-20
Chapter 8
Testing Web Components
Avoid The application will often style HTML tag elements like
Avoid styling on elements like
element headers, links, and so on. In order to blend in with the
headers.
styling application, avoid styling these elements in your Web
Component. For information about Oracle JET’s use of acme-branding .acme-branding-
styles on HTML tags, see Using Tag Selectors or Classes. header h3{
font-size: 45px;
}
8-21
Chapter 8
Adding Web Components to Your Page
Regardless of the test method you choose, be sure that your tests fully exercise the
Web Component’s:
• ViewModel (if it exists)
Ideally, your test results should be verifiable via code coverage numbers.
• HTML view
Be sure to include any DOM branches that might be conditionally rendered, and
test all slots with and without default content.
• Properties and property values
• Events
• Methods
• Accessibility
• Security
For additional information about testing Oracle JET applications, see Testing Oracle
JET Applications.
8-22
Chapter 8
Adding Web Components to Your Page
Note:
The framework maps the attribute names in the markup to the
component’s properties.
• Attribute names are converted to lowercase. For example, a
workTitle attribute will map to a worktitle property.
3. In your application’s ViewModel, set values for the properties you declared in the
previous step and add the component’s loader file to the list of application
dependencies.
For example, the following code adds the ViewModel to the application’s
RequireJS bootstrap file. The code also defines the demo-card/loader dependency.
require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'demo-card/
loader'],
function(oj, ko, $) {
function model() {
var self = this;
self.employees = [
{
name: 'Deb Raphaely',
avatar: 'images/composites/debraphaely.png',
title: 'Purchasing Director',
work: 5171278899,
email: '[email protected]'
},
{
name: 'Adam Fripp',
avatar: null,
title: 'IT Manager',
work: 6501232234,
email: '[email protected]'
}
];
}
$(function() {
ko.applyBindings(new model(), document.getElementById('composite-
container'));
});
});
4. Add any supporting CSS, folders, and files as needed.
For example, the demo card example defines a background image for the contact
card in the application’s demo.css:
8-23
Chapter 8
Building Web Components
For example, if your Web Component name is demo-card-example, use the following
command:
ojet build component demo-card-example
Note:
• Do not use the --release attribute with the build command, as the build
command generates both the debug and minified version of the
component.
• The /min folder is not added to the version directory structure of the
component. It is added to the web/js/jet-composites/demo-card-example
directory.
8-24
Chapter 8
Optimizing Web Component Files
Note:
If you are using a UNIX or MAC machine, you may use sudo npm install -g
requirejs command to install r.js.
Once the execution of the above command is complete, you can use r.js in the
command-line interface (CLI) to run the optimizer. For example, if you are using
Node.js with npm, use the following command to run the optimizer on your Web
Component:
r.js -o <optimizer script>
The <optimizer script> is generally referenced from the scripts folder of your root
application directory that contains the configuration for optimizing your Web
Component. To create the <optimizer script>, see Optimizing a Web Component.
1. Create a foo-person-build.js file in the scripts folder of your Oracle JET project
for Web Component with the following content:
({
baseUrl: '../src/js/jet-composites',
name: 'foo-person/loader',
out: '../src/js/jet-composites/foo-person/min/loader.js',
optimize: 'uglify',
separateCSS: false,
generateSourceMaps: true,
paths: {
'text': '../../../node_modules/requirejs-text/text',
'css': '../../../node_modules/require-css/css.min',
'css-builder': '../../../node_modules/require-css/css-builder',
'normalize': '../../../node_modules/require-css/normalize',
'ojL10n': '../../../node_modules/@oracle/oraclejet/dist/js/libs/oj/ojL10n',
8-25
Chapter 8
Optimizing Web Component Files
'knockout': 'empty:',
'jquery': 'empty:',
'ojs': 'empty:'
},
exclude: [
'text',
'css',
'css-builder',
'normalize',
'ojL10n'
]
})
• The libraries that are required by the Web Component but not directly used by
r.js to optimize the code are mapped to empty: (with a trailing colon) to
prevent r.js from including the contents of those libraries in the output file.
The require-text and require-css libraries are used by r.js to include the
HTML, JSON, and CSS dependencies of the composite component.
• If you need a non-uglified version of the Web Component, you can set the
optimize property to none.
• If a source map is required to help with debugging in the browser, then set
the generateSourceMaps flag to true.
2. Now run the optimizer with the following command:
r.js -o foo-person-build.js
The output will contain a definition for each file that is required by the Web
Component, and each file will have a module name that matches the original file
path relative to the baseUrl.
By default, r.js combines all the CSS files referenced by the require-css library into
one string, and then wraps the string in a <style> tag. However, if these CSS files
contain URLs for images, fonts, and so on, then you must generate a separate CSS
file that you can load using a <link> tag. To accomplish this, set
the separateCSS property to true. This will create a single CSS file with the same file
name and directory as the JavaScript output file. For example, if the JavaScript output
file name is loader.js, then the CSS file is named loader.css by default.
8-26
Chapter 8
Optimizing Web Component Files
Note:
Setting the separateCSS property in your script will cause an extra HTTP
request. You can avoid this if your Web Component does not need to load
external resources such as, fonts or background images from within its CSS
definitions.
To use the separateCSS property, you must load the optimized CSS file manually by
editing the foo-person-build.js file as shown below:
({
...
separateCSS: true,
wrap: {
end: "require(['css!foo-person/loader']);"
},
...
});
"foo-person": {
"debug": {
"path": "jet-composites/foo-person"
},
"release": {
"path": "jet-composites/foo-person/min"
}
}
If you have additional language translation bundles under the /resources/nls directory,
you must copy the translation bundles into your /min directory as they are loaded
dynamically and cannot be included in the compressed loader.js file created by r.js.
8-27
Chapter 8
Optimizing Web Component Files
/jet-composites
/foo
/foo-person
loader.js
component.json, etc.
/foo-address
loader.js
component.json, etc.
/foo-schedule
loader.js
component.json, etc.
bundle.js
/libs
foo-build.js
1. Create a bundle.js bundle loader for your Web Component foo that defines the
individual Web Component loaders. For example:
define(['foo/foo-person/loader', 'foo/foo-address/loader', 'foo/foo-schedule/
loader'], function() {});
2. Create a foo-build.js file in the scripts folder of your project root with the following
content:
({
baseUrl: 'jet-composites',
name: 'foo/bundle',
out: 'jet-composites/foo/min/bundle.js',
optimize: 'uglify',
separateCSS: true,
generateSourceMaps: true,
wrap: {
end: "require(['css!foo/bundle']);"
},
paths: {
'text': '../../../node_modules/requirejs-text/text',
'css': '../../../node_modules/require-css/css.min',
'css-builder': '../../../node_modules/require-css/css-builder',
'normalize': '../../../node_modules/require-css/normalize',
'ojL10n': '../../../node_modules/@oracle/oraclejet/dist/js/libs/oj/ojL10n',
'knockout': 'empty:',
'jquery': 'empty:',
'ojs': 'empty:'
},
exclude: [
'text',
'css',
'css-builder',
'normalize',
'ojL10n'
]
})
3. Run the optimizer with the following command:
r.js -o foo-build.js
8-28
Chapter 8
Optimizing Web Component Files
8-29
9
Using the Common Model and Collection
API
The Oracle JET Common Model and Collection API provides a two-way data binding
model using Knockout. Use the Model and Collection classes to build Create, Read,
Update, Delete (CRUD) applications that integrate REST services or data from any
web service that returns data in the form of JSON objects.
Topics:
• Typical Workflow for Binding Data in Oracle JET
• About Oracle JET Data Binding
• Using the Oracle JET Common Model and Collection Framework
• Integrating REST Services
• Creating a CRUD Application Using Oracle JET
9-1
Chapter 9
Using the Oracle JET Common Model and Collection Framework
the UI components, and user input from the UI components is written back into the
ViewModel.
Oracle JET uses Knockout to perform the data binding between the UI elements and
the ViewModel. The ViewModel normally contains data fields for the UI state as well
as references to external data. One of the ways to provide external data is to use the
Common Model and Collection API.
Data for an Oracle JET application can come from any web data source that generates
JSON data, such as a REST service, Server Sent Event (SSE), or WebSocket. In
addition, Oracle JET also provides specific support for integrating web service data
based on the Oracle REST standard.
Oracle JET also provides UI components for the View layer that include properties for
data binding with Knockout. For additional information about Oracle JET's UI
components and data binding options, see Understanding Oracle JET User Interface
Basics.
Topics:
• About the Oracle JET Common Model and Collection API
• About Oracle JET Data Binding and Knockout
• Using the Oracle JET Common Model and Collection API
9-2
Chapter 9
Using the Oracle JET Common Model and Collection Framework
oj.Model and oj.Collection include client-side API that provides one way to bring
external data into an Oracle JET application. oj.KnockoutUtils provides the map()
method to map the attributes in a model object or the attributes of all models in a
collection object to the application's view data model.
To utilize the data model in Oracle JET applications, Oracle JET provides custom
elements which support Knockout variables in the data attribute. For additional
information about working with Oracle JET UI components, see Understanding Oracle
JET User Interface Basics.
<oj-table data="[[dataSource]]">
</oj-table>
9-3
Chapter 9
Using the Oracle JET Common Model and Collection Framework
urlRoot: self.serviceURL,
parse: self.parseDept,
idAttribute: 'DepartmentId'
});
/**
* Callback to map attributes returned from RESTful data service to desired view
model attribute names
*/
self.parseDept = function(response) {
return {DepartmentId: response['DepartmentId'],
DepartmentName: response['DepartmentName'],
LocationId: response['LocationId'],
ManagerId: response['ManagerId']};
};
2. Add JavaScript code to your application that extends oj.Collection.
The following code example creates a collection object for its entire data set (or
list) of task records and ties that to a specific instance of a task record model. The
fetch() method tells the collection to go to the data service and asynchronously
retrieve the data services' data set using the given URL, through jQuery AJAX
calls. The application's success callback method is invoked when the call
successfully returns and the collection has been populated with model records.
self.DeptCol = ko.observable();
self.myDept = new self.Department();
// Create a specific instance for the departments. This will be filled with
instances of the
// model "department" for each record when the data is retrieved from the data
service
self.DeptCol(new self.DeptCollection());
self.datasource(new oj.CollectionTableDataSource(self.DeptCol()));
3. Bind the returned data collection to a Knockout ViewModel to make it ready for
consumption by one or more components on the application page.
The following code sample maps the vm Collection object to the element in the
view with the ID 'table' using the ko.applyBindings() function.
var vm = new viewModel;
$(document).ready(function() {
ko.applyBindings(vm, document.getElementById('table'));
});
4. Add code to the application's index.html or main page that consumes the
Knockout ViewModel.
The following code examples shows a simple table that is defined with four
columns: Department Id, Department Name, Location Id, and Manager Id. The data
attribute binds the datasource collection to the ViewModel using Knockout.
<oj-table id="table" summary="Department List" aria-label="Departments Table"
data='[[datasource]]'
columns='[{"headerText": "Department
9-4
Chapter 9
Integrating REST Services
Id",
"field": "DepartmentId"},
{"headerText": "Department
Name",
"field": "DepartmentName"},
{"headerText": "Location Id",
"field": "LocationId"},
{"headerText": "Manager Id",
"field": "ManagerId"}]'>
</oj-table>
For a complete list of Oracle JET Common Model and Collection API properties and
functions, see the oj.Model and oj.Collection API documentation.
Topics:
• About Oracle JET Support for Integrating REST Services
• Passing Custom AJAX Options in Common Model CRUD API calls
• Supplying a customURL Callback Function
• Replacing oj.sync or oj.ajax Functions
Model objects also include the url property. By default, this is set to the model ID
appended to the oj.Collection.url property: http://myserver/departments/modelID.
To override the default path set by the Collection object, set the oj.model.urlRoot
property, and the application will use the url.Root/modelID as the path to the data.
Oracle JET also provides three ways to customize the AJAX requests that Oracle JET
makes when accessing REST services through the Common Model.
• Pass custom AJAX options in Common Model CRUD API calls.
• Supply a customURL callback function.
• Replace the oj.sync or oj.ajax functions.
9-5
Chapter 9
Integrating REST Services
To customize the AJAX options, add property/value pairs to the options argument of
the oj.Collection.create(create), oj.Collection.fetch(read),oj.Model.save(update),
and oj.Model.destroy(delete) functions.
The following code example shows how an application could pass in a custom header
when doing a read from the REST service.
myOjCollection.fetch(
{headers:{my-custom-header:"header-value"},
beforeSend: myBeforeSendCallbackFunc,
success:function(collection){
}});
The callback function should include the following parameters which are passed to it
by default:
• operation: create, read, update, patch, or delete
• fromID: Retrieve records starting with or after the record with the given unique ID.
Based on your REST server’s interpretation, the record with the given ID may or
may not be included.
• since: Retrieve records with timestamps after the given timestamp
9-6
Chapter 9
Integrating REST Services
}
var retObj = {};
if (operation === "read") {
retObj['url'] = "http://fetch";
if (options['sort']) {
retObj['url'] += "/order="+options['sort'] + ";" + options['sortDir'];
}
retObj['headers'] = {my-custom-header:"header-value"};
retObj['mimeType'] = "text/plain";
return retObj;
}
// Update or patch
retObj['url'] = "http://update/model="+options['recordID'];
retObj['type'] = "POST";
retObj['beforeSend'] = myBeforeSendCallback;
return retObj;
}
Valid values for the method parameter are the CRUD operations: create, read, update,
patch, and delete. The model parameter accepts either the oj.Model being created,
read, updated, patched, or deleted, or the oj.Collection being read. Options are
passed down from the higher-level common model API call and vary with the type of
operation.
The replacement oj.sync() method is completely responsible for implementing all of
these operations using whatever transport mechanism is being used, whether it be the
application's own AJAX routines, WebSockets, a JavaScript server, or something else.
The method must return a Promise object similar to the AJAX XMLHttpRequest (XHR) in
order to maintain compatibility with potential virtual API calls being made by the
oj.Model and oj.Collection object.
Note:
Replacing oj.sync() replaces the transport mechanism for the JET common
model used by all calls within the application. This is a very advanced use of
the Oracle JET common model.
The oj.ajax() method is the master AJAX entry point for all oj.Model and
oj.Collection server interactions, when they are using the default sync
implementations. oj.ajax() passes its parameters and return value through to the
jQuery.ajax() method by default. For additional information about the jQuery.ajax()
method's expected parameters and return value, see http://api.jquery.com/jquery.ajax.
9-7
Chapter 9
Creating a CRUD Application Using Oracle JET
For additional information about the oj.sync() or oj.ajax() methods, see the Oracle
JET oj.sync() and oj.ajax() API documentation.
Topics:
• Defining the ViewModel
• Reading Records
• Creating Records
• Updating Records
• Deleting Records
Note:
The application shown in this section also includes code for defining and
displaying header and footer detail. You can download the complete sample
application here: OracleJET-CommonModel-CRUD.zip.
{
"Departments" : [ {
"DepartmentId" : 10,
"DepartmentName" : "Administration",
"ManagerId" : null,
"LocationId" : null,
"version" : "ACED00057...contents truncated",
"links" : {
"self" : {
"rel" : "self",
"href" : "http://mockrest/stable/rest/Departments/10"
},
"canonical" : {
"rel" : "canonical",
"href" : "http://mockrest/stable/rest/Departments/10"
},
"Employees" : {
"rel" : "child",
"href" : "http://mockrest/stable/rest/Departments/10/Employees"
9-8
Chapter 9
Creating a CRUD Application Using Oracle JET
}
}
}, {
"DepartmentId" : 20,
"DepartmentName" : "Retail Marketing",
"ManagerId" : null,
"LocationId" : null,
"version" : "ACED00057...contents truncated",
"links" : {
"self" : {
"rel" : "self",
"href" : "http://mockrest/stable/rest/Departments/20"
},
"canonical" : {
"rel" : "canonical",
"href" : "http://mockrest/stable/rest/Departments/20"
},
"Employees" : {
"rel" : "child",
"href" : "http://mockrest/stable/rest/Departments/20/Employees"
}
}
}, {
... contents omitted
} ],
"links" : {
"self" : {
"rel" : "self",
"href" : "http://mockrest/stable/rest/Departments"
}
},
"_contextInfo" : {
"limit" : 25,
"offset" : 0
}
}
Tip:
The Oracle JET Common Model CRUD sample application uses JSON
data returned from a mock rest service. You can find the mock rest
service scripts in the public_html/js/rest folder. You can find the sample
JSON data in the departments.json file located in the public_html/js
folder.
9-9
Chapter 9
Creating a CRUD Application Using Oracle JET
3. Add a JavaScript function to your application that will contain your ViewModel.
The following code shows a skeleton function that defines the ViewModel in the
Oracle JET Common Model CRUD application. In this example, the function is
stored in a file named app.js. The code to complete the ViewModel will be defined
in upcoming steps.
define(['ojs/ojcore', 'knockout', 'ojs/ojmodel', 'MockRESTServer'],
function(oj, ko)
{
function viewModel() {
// To be defined
};
return {'deptVM': viewModel};
}
)
Note:
This example uses RequireJS for modular development. The RequireJS
bootstrap file will be shown in a later step. For additional information
about using RequireJS, see Using RequireJS for Modular Development.
4. Add a JavaScript function to the function you defined in the previous step that
defines the data model using oj.Model.extend().
The highlighted code in the example below defines the data model for the
application shown in the preceding figure. The Department variable represents a
single record in the database and is displayed as a row in the table. Department is
declared using the oj.Model.extend function call and instantiated in the declaration
for myDept. The urlRoot property defines the data source, which in this case is the
REST service URL.
9-10
Chapter 9
Creating a CRUD Application Using Oracle JET
function parseDept(response) {
if (response['Departments']) {
var innerResponse = response['Departments'][0];
if (innerResponse.links.Employees == undefined) {
var empHref = '';
} else {
empHref = innerResponse.links.Employees.href;
}
return {DepartmentId: innerResponse['DepartmentId'],
DepartmentName: innerResponse['DepartmentName'],
links: {Employees: {rel: 'child', href: empHref}}};
}
return {DepartmentId: response['DepartmentId'],
DepartmentName: response['DepartmentName'],
LocationId:response['LocationId'],
ManagerId:response['ManagerId'],
links: {Employees: {rel: 'child', href: response['links']
['Employees'].href}}};
}
The parse property is an optional user callback function to allow parsing of JSON
record objects as they are returned from the data service. In this example,
parseDept is the callback function and simply maps the DepartmentId and
DepartmentName returned from the REST service to DepartmentId and
DepartmentName. If the LocationId or ManagerId records contain data, parseDept
maps the attributes to LocationId and ManagerId.
The parse callback can be useful for mapping database attribute names to names
that may make more sense. For example, if your database uses Id and Name as the
attributes that represent the department ID and department name, you could
replace the return call in the parseDept function with:
return {DepartmentId: response['Id'], DepartmentName: response['Name']};
For a complete list of oj.Model properties and functions, see the oj.Model API
documentation.
5. Define the collection that will hold the data model object you defined in the
previous step using oj.Collection.extend().
9-11
Chapter 9
Creating a CRUD Application Using Oracle JET
The highlighted code in the example below defines the collection object for the
Department model object. The DeptCollection variable is declared in the
viewModel() function using the oj.Collection.extend function and instantiated in
the declaration for self.DeptCol. The url property defines the data source, which in
this case is the REST service URL, and limits the collection to 50 records.
define(['ojs/ojcore', 'knockout', 'ojs/ojmodel', 'MockRESTServer'],
function(oj, ko){
function viewModel() {
var self = this;
self.serviceURL = 'http://mockrest/stable/rest/Departments';
self.Departments = ko.observableArray([]);
self.DeptCol = ko.observable();
self.datasource = ko.observable();
self.DeptCol(new DeptCollection());
self.datasource(new
oj.CollectionTableDataSource(self.DeptCol()));
}
return {'deptVM': viewModel};
}
);
Both the DeptCol and datasource objects are defined as Knockout observables so
that changes to the data collection can be handled. The datasource object will
contain the column data needed by the oj-table element and is passed the
DeptCol observable as a parameter to oj.CollectionTableDataSource().
9-12
Chapter 9
Creating a CRUD Application Using Oracle JET
self.DeptCol(new DeptCollection());
self.datasource(new
oj.CollectionTableDataSource(self.DeptCol()));
}
return {'deptVM': viewModel};
}
);
The fetch() function also defines an error callback that will log a message to the
console if the fetch() call fails.
7. Add the ViewModel or the file containing the name of your ViewModel to your
RequireJS bootstrap file, typically main.js.
If you created your Oracle JET application using an Oracle JET Starter template or
modified your Oracle JET download as described in Use RequireJS to Manage
Library, Link, and Script References, you should already have a main.js file.
Locate the line that defines the require modules and add your file to the list.
For example, the code below lists the modules defined for the Common Model
Sample. The application stores its ViewModel in the app.js file. The reference to
the app.js file is highlighted in bold.
require(['ojs/ojcore',
'knockout',
'jquery',
'app',
'footer',
'MockRESTServer'
9-13
Chapter 9
Creating a CRUD Application Using Oracle JET
'ojs/ojmodel',
'ojs/ojknockout',
'ojs/ojknockout-model',
'ojs/ojdialog',
'ojs/ojinputtext',
'ojs/ojinputnumber',
'ojs/ojbutton',
'ojs/ojtable',
'ojs/ojcollectiontabledatasource'],
You must also add the app reference in the callback definition as shown in the
following example.
// this callback gets executed when all required modules are loaded
function(ko, $, oj, app, footer, MockRESTServer)
{
...
}
8. Update your RequireJS bootstrap file to instantiate the ViewModel, create the
Knockout bindings, and display the content on the page.
The highlighted code in the code sample below creates a Knockout observable for
each element in the deptData collection and assigns the resulting array to the
Departments Knockout observable array you defined in a previous step.
require(['ojs/ojcore',
'knockout',
'jquery',
'app',
'footer',
'MockRESTServer'
'ojs/ojmodel',
'ojs/ojknockout',
'ojs/ojknockout-model',
'ojs/ojdialog',
'ojs/ojinputtext',
'ojs/ojinputnumber',
'ojs/ojbutton',
'ojs/ojtable',
'ojs/ojcollectiontabledatasource'],
function(ko, $, oj, app, footer, MockRESTServer) // this callback gets
executed when all required modules are loaded
{
var fvm = new footer.footerVM();
$(document).ready(function(){
$.getJSON("js/departments.json",
function (data) {
new MockRESTServer(data, {id:"DepartmentId",
url:/^http:\/\/mockrest\/stable\/rest\/Departments(\?
limit=([\d]*))?$/i,
idUrl:/^http:\/\/mockrest\/stable\/rest\/Departments\/
([\d]+)$/i});
9-14
Chapter 9
Creating a CRUD Application Using Oracle JET
Reading Records
To read the records, define the Oracle JET elements that will read the records in your
main HTML5 page.
The following sample code shows a portion of the index.html file that displays a table
of records using the ViewModel defined in the previous steps and the oj-table
element. In this example, the mainContent div includes the table definition that creates
Department Id, Department Name, Location Id, and Manager Id as the table header text
and defines the content for each row.
<div id="mainContent" class="oj-md-12 oj-flex-item page-padding" style="display:
none;">
<div class="page-padding">
<oj-table id="table" data="[[datasource]]"
columns='[{"headerText": "Department Id",
"field": "DepartmentId", "sortable": "enabled"},
{"headerText": "Department Name",
"field": "DepartmentName", "sortable": "enabled"},
{"headerText": "Location Id",
"field": "LocationId"},
{"headerText": "Manager Id",
"field": "ManagerId"}]'>
</oj-table>
</div>
</div>
The data attribute reads the variable datasource from the REST service and binds it to
the oj-table element.
Creating Records
To add the ability to create new records, add elements to your HTML5 page that
accept input from the user and create a function that sends the new record to the
REST server.
The figure below shows the result of adding a form using oj-input-* elements to the
Oracle JET Common Model sample application. The user can enter a new department
number in the provided field or use the side arrows to increment or decrement the
value. The user then enters the name and clicks Add.
9-15
Chapter 9
Creating a CRUD Application Using Oracle JET
To add the ability to create new records to the application's ViewModel and HTML5
page:
1. Add elements to the application's main page that accept input from the user.
The highlighted code in the example below adds oj-input-* elements to the
index.html page shown in the previous task.
9-16
Chapter 9
Creating a CRUD Application Using Oracle JET
<div class="oj-flex-item">
<oj-input-number id="newDepartId" value= "555"></oj-
input-number>
</div>
</div>
<div class="oj-flex">
<div class="oj-flex-item">
<oj-label for="newDepartName">Department Name</oj-
label>
</div>
<div class="oj-flex-item">
<oj-input-text id="newDepartName" maxlength='30'
placeholder="enter new name"></oj-input-text>
<oj-button id="saveBtn" on-oj-
action="[[addDepartment]]" label='Add Department'></oj-button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
The form is defined to contain two input fields: an oj-input-number for the
department ID, and an oj-input-text for the department name. The oj-button
component is used for the form's button.
The oj-button's on-oj-action attribute is bound to the addDepartment function which
is defined in the next step.
2. Add code to the ViewModel to add the user's input as a new record (model) in the
data collection.
The highlighted code in the example below shows the addDepartment() function
that adds the new department number and department name to the DeptCol data
collection. In this example, the function calls the oj.Collection.create() method
which creates the new model instance, adds it to the data collection, and saves
the new DepartmentId and DepartmentName to the data service.
define(['ojs/ojcore', 'knockout', 'ojs/ojmodel'],
function(oj, ko) {
function viewModel() {
var self = this;
self.serviceURL = 'http://RESTServerIP:Port/stable/rest/
Departments';
self.Departments = ko.observableArray([]);
self.DeptCol = ko.observable();
self.datasource = ko.observable();
self.fetch = function(successCallBack) {
... contents omitted
};
function parseDept(response){
... contents omitted
};
9-17
Chapter 9
Creating a CRUD Application Using Oracle JET
parseSave:parseSaveDept,
idAttribute: 'DepartmentId'
});
self.DeptCol(new DeptCollection());
self.datasource(new
oj.CollectionTableDataSource(self.DeptCol()));
function parseSaveDept(response){
return {DepartmentId: response['DepartmentId'],
DepartmentName: response['DepartmentName'],
LocationId: response['LocationId'],
ManagerId: response['ManagerId'],
links: {Employees: {rel: 'child', href:
response['links']['Employees'].href}}};
};
Updating Records
To add the ability to update records, add elements to your HTML5 page that accept
input from the user and create a function that sends the updated record to the REST
server.
The figure below shows the Oracle JET Common Model sample application configured
to allow updates to the department name. When the user moves the focus over a
9-18
Chapter 9
Creating a CRUD Application Using Oracle JET
department name, a tooltip appears that prompts the user to click to edit. department
name, a tooltip prompts the user to click to edit. If the user clicks the department
name, a dialog appears that enables the user to change the name. The user can type
in a new department name and click Change to update the record or Cancel to keep
the existing department name.
To add the ability to update records to the application's ViewModel and HTML5 page:
1. Add elements to the application's main page that identifies updatable elements
and enables the user to perform an action to update them.
The highlighted code in the example below adds the oj-dialog element to the
page and provides the prompt to the user to click to edit the page.
9-19
Chapter 9
Creating a CRUD Application Using Oracle JET
</oj-table>
</div>
<oj-dialog id="editDialog" style="display:none" title="Change Department
Name" drag-affordance="title-bar"
modality="modeless" resize-behavior="none">
<div slot="header" class="oj-helper-clearfix" aria-labelledby="dialog-
title-id">
<div>
<span id="infoIcon" class="oj-fwk-icon oj-fwk-icon-status-info"
style="float:left; margin-right: 10px"></span>
<span id="dialog-title-id" class="oj-dialog-title">Change Department
Name</span>
</div>
</div>
<div slot="body">
<div class="oj-md-odd-cols-4">
<oj-label for="newName" class="oj-label">Department Name</oj-label>
<oj-input-text id="newName" value="{{currentDeptName}}"></oj-input-
text>
</div>
</div>
<div slot="footer">
<oj-button id="submitBtn" on-oj-action="[[updateDeptName]]">Change</oj-
button>
<oj-button id="resetBtn" on-oj-action="[[cancelDialog]]">Cancel</oj-
button>
</div>
</oj-dialog>
</div>
</div>
</div>
</div>
<script type="text/html" id="row_tmpl">
<tr>
<td>
<div id='deptId' on-click="[[function(data, event)
{$root.showChangeNameDialog(DepartmentId,data,event)}]]">
<oj-bind-text value="[[DepartmentId]]"></oj-bind-text>
</div>
</td>
<td>
<div id="deptName" on-click="[[function(data, event)
{$root.showChangeNameDialog(DepartmentId,data,event)}]]">
<oj-bind-text value="[[DepartmentName]]"></oj-bind-text>
</div>
</td>
<td>
<div id="locId">
<oj-bind-text value="[[LocationId]]"></oj-bind-text>
</div>
</td>
<td>
<div id="mgrId">
<oj-bind-text value="[[ManagerId]]"></oj-bind-text>
</div>
</td>
</tr>
</script>
9-20
Chapter 9
Creating a CRUD Application Using Oracle JET
The oj-dialog-body class formats the dialog body which includes the oj-label for
Department Name and an oj-input-text element to capture the user's input. The oj-
dialog-footer defines two oj-button components which add the Change and Cancel
buttons to the dialog. When the user clicks Change, the updateDepartmentName()
function handles the updates to the record.
The original table is also modified to define the row-renderer attribute which
specifies the row_tmpl template to use for the row display. The template script calls
the showChangeNameDialog() function to display the dialog.
The updateDepartmentName() and showChangeNameDialog() functions are defined in
the next step. For additional information about the oj-dialog component, see the
oj-dialog API documentation.
2. Add code to the ViewModel to update the record.
The highlighted code in the example below shows the updateDepartment() and
showChangeNameDialog() functions.
function parseDept(response){
... contents omitted
}
function parseSaveDept(response){
... contents omitted
}
self.DeptCol(new DeptCollection());
self.datasource(new
oj.CollectionTableDataSource(self.DeptCol()));
9-21
Chapter 9
Creating a CRUD Application Using Oracle JET
Deleting Records
To add the ability to delete records, add elements to your HTML5 page that accept
input from the user and create a function that sends the new record for deletion to the
REST server.
The figure below shows the Oracle JET Common Model CRUD application configured
to allow record deletion. The user can check one or more departments in the list and
click Remove Department to delete the record or records.
9-22
Chapter 9
Creating a CRUD Application Using Oracle JET
To add the ability to delete records to the application's ViewModel and HTML5 page:
1. Add elements to the application's main page that identifies records marked for
deletion and enables the user to perform an action to delete them.
The highlighted code in the example below adds the Remove column with a check
box to the department list and adds the Remove Department button below the list.
<div id="mainContent" class="oj-flex-item oj-sm-12 oj-md-12 demo-page-content-
area page-padding" style="display: none;">
<div class="page-padding">
<div id="deptList" class="oj-flex-item oj-md-9 oj-sm-12">
<oj-table id="table" data="[[datasource]]"
columns='[{"headerText": "Remove", "id": "column1", "sortable":
"disabled"},
{"headerText": "Department Id",
"field": "DepartmentId", "sortable": "enabled"},
{"headerText": "Department Name",
"field": "DepartmentName", "sortable": "enabled"},
{"headerText": "Location Id",
"field": "LocationId"},
{"headerText": "Manager Id",
"field": "ManagerId"}]'
selectionMode='{"row": "none", "column": "none"}'
row-renderer='[[oj.KnockoutTemplateUtils.getRenderer("row_tmpl",
true)]]'>
</oj-table>
<br/>
<oj-button id="deleteDept_btn" disabled="[[!somethingChecked()]]" on-
oj-action="[[deleteDepartment]]">Remove
Department</oj-button>
</div>
9-23
Chapter 9
Creating a CRUD Application Using Oracle JET
</div>
</div>
The original table is modified to include the Remove column. The row-renderer
attribute specifies the row_tmpl template to use for the row display. The template
script adds the checkbox input element to the first column and the value of
DepartmentId, DepartmentName, and LocationId to the remaining columns.
9-24
Chapter 9
Creating a CRUD Application Using Oracle JET
self.DeptCol(new DeptCollection());
self.datasource(new
oj.CollectionTableDataSource(self.DeptCol()));
self.enableDelete = function() {
if (!$('input[type=checkbox]:checked').length) {
self.somethingChecked(false);
} else {
self.somethingChecked(true);
}
return true;
}
self.findDeptIds = function() {
var selectedIdsArray = [];
$("input:checkbox").each(function() {
var cb = $(this);
if (cb.is(":checked")) {
selectedIdsArray.push(cb.attr("id"));
}
});
return selectedIdsArray;
}
}
return {'deptVM': viewModel};
}
);
The deleteDepartment() function calls the findDeptIds() function which returns the
list of selected departments marked for deletion. The enableDelete() function
resets the check box after the department list is deleted.
9-25
10
Validating and Converting Input
Oracle JET includes validators and converters on a number of Oracle JET editable
elements, including oj-combobox, oj-input*, and oj-text-area. You can use them as is
or customize them for validating and converting input in your Oracle JET application.
Some editable elements such as oj-checkboxset, oj-radioset, and oj-select have a
simple attribute for required values that implicitly creates a built-in validator.
Note:
The oj-input* mentioned above refers to the family of input components
such as oj-input-date-time, oj-input-text, and oj-input-password, among
others.
Topics:
• Typical Workflow for Validating and Converting Input
• About Oracle JET Validators and Converters
• Using Oracle JET Converters
• Using Oracle JET Validators
10-1
Chapter 10
About Oracle JET Validators and Converters
Topics:
• About Validators
• About Converters
About Validators
All Oracle JET editable elements support a value attribute and provide UI elements
that allow the user to enter or choose a value. These elements also support other
attributes that page authors can set that instruct the element how it should validate its
value.
An editable element may implicitly create a built-in converter and/or built-in validators
for its normal functioning when certain attributes are set.
For example, editable elements that support a required property create the required
validator implicitly when the property is set to true. Other elements like oj-input-date,
10-2
Chapter 10
About Oracle JET Validators and Converters
Topics
• Oracle JET Validators
• Oracle JET Component Validation Attributes
• Oracle JET Component Validation Methods
10-3
Chapter 10
About Oracle JET Validators and Converters
Some editable elements do not support specific validation attributes as they might be
irrelevant to its intrinsic functioning. For example, oj-radioset and oj-checkboxset do
not support a converter attribute since there is nothing for the converter to convert. For
an exact list of attributes and how to use them, refer to the Attributes section in the
element’s API documentation. For Oracle JET API documentation, see JavaScript API
Reference for Oracle® JavaScript Extension Toolkit (Oracle JET). Select the
component you're interested in viewing from the API list.
For details on calling a element's method, parameters, and return values, See the
Methods section of the element's API documentation in JavaScript API Reference for
Oracle® JavaScript Extension Toolkit (Oracle JET). You can also find detail on how to
register a callback for or bind to the event and for information about what triggers the
events. Select the component you're interested in viewing from the API list.
About Converters
The Oracle JET converters include date-time, number, and color converters and are
described in the following table.
10-4
Chapter 10
About Oracle JET Validators and Converters
Color Converters
Color converters support the following options.
For an exact list of options and methods and how to use them, refer to API
documentation of the ColorConverter class. See ColorConverter.
Number Converters
Number converters support a number of options that can be used to format decimal
numbers, currency, percentages, or units. Some of the important options are:
10-5
Chapter 10
About Oracle JET Validators and Converters
For an exact list of options and methods and how to use them, refer to API
documentation of the IntlNumberConverter class. See IntlNumberConverter.
For an example illustrating the use of Number Converter options, see Number
Converter.
DateTime Converters
DateTime converters support a wide array of options that can be used to format date
and time values in all common styles across locales. Some of the important options
are:
10-6
Chapter 10
Using Oracle JET Converters
For an exact list of options and methods and how to use them, refer to API
documentation of the IntlDateTimeConverter class. See IntlDateTimeConverter.
For an example illustrating the use of DateTime Converter options, see DateTime
Converter.
10-7
Chapter 10
Using Oracle JET Converters
For examples that illustrate the date-time and number converters in action, see the
Converters section in the Oracle JET Cookbook. For examples using the color
converter, see the Color Palette and Color Spectrum Cookbook samples.
Note:
The bundles that hold the locale symbols and data used by the Oracle JET
converters are downloaded automatically based on the locale set on the
page when using RequireJS and the ojs/ojvalidation-base, ojs/
ojvalidation-datetime, or ojs/ojvalidation-number module. If your application
does not use RequireJS, the locale data will not be downloaded
automatically.
You can use the converters with an Oracle JET component or instantiate and use
them directly on the page.
Topics:
• Using Oracle JET Converters with Oracle JET Components
• Understanding Oracle JET Converters Lenient Parsing
• Understanding Time Zone Support in Oracle JET
• Using Custom Converters in Oracle JET
• Using Oracle JET Converters Without Oracle JET Components
The script to create the view model for this example is shown below.
10-8
Chapter 10
Using Oracle JET Converters
$(
function()
{
ko.applyBindings(new MemberViewModel(),
document.getElementById('datetime-converter-example'));
}
);
});
When the user runs the page, the oj-input-date element displays an input field with
the expected date format. In this example, the element also displays a hint when the
user hovers over the input field, and displays a calendar when the user clicks in the
input field. If the user inputs data that is not in the expected format, the built-in
converter displays an error message with the expected format.
10-9
Chapter 10
Using Oracle JET Converters
The error that the converter throws when there are errors during parsing or formatting
operations is represented by the oj.ConverterError object, and the error message is
represented by an object that duck-types oj.Message. The messages that Oracle JET
converters use are resources that are defined in the translation bundle included with
Oracle JET. For more information about messaging in Oracle JET, see Working with
User Assistance.
You can also specify the converter directly on the element's converter attribute, if it
exists. The code excerpt below defines another oj-input-date element on the sample
form and specifies the oj.IntlDateTimeConverter converter with options that will convert
the user's input to a numeric year, long month, and numeric day according to the
conventions of the locale set on the page. The options parameter is an object literal
that contains the ECMA-402 options as name-value pairs.
<div class="oj-flex">
<div class="oj-flex-item">
<oj-label for="date2">input date</oj-label>
</div>
<div class="oj-flex-item">
<oj-input-date id="date2" value="{{date}}" name="date2"
help.instruction="enter a date in your preferred format and we
will attempt to figure it out"
converter= '{
"type":"datetime",
"options": {"year": "numeric", "month": "long", "day":
"numeric"}}'>
</oj-input-date>
</div>
</div>
When the user runs the page in the en-us locale, the oj-input-date element displays
an input field that expects the user's input date to be in the mmmm d, yyyy format. The
converter will accept alternate input if it makes sense, such as 18/07/17 (MM/dd/yy),
and perform the conversion, but will throw an error if it cannot parse the input. For
details about Oracle JET converters and lenient parsing support, see Understanding
Oracle JET Converters Lenient Parsing.
10-10
Chapter 10
Using Oracle JET Converters
10-11
Chapter 10
Using Oracle JET Converters
• Swap month and day positions, as long as the date value is greater than 12 when
working with the Gregorian calendar. For example, if the user enters 2013-16-11
when y-M-d is expected, the converter will autocorrect the date to 2013-11-16.
However, if both date and month are less or equal to 12, no assumptions are
made about the day or month, and the converter parses the value against the
exact pattern.
• Enter weekday and month names or mix short and long names anywhere in the
string. For example, if the expected pattern is E, MMM, d, y, the user can enter any
of the following dates:
Tue, Nov 26 2013
Nov, Tue 2013 26
2013 Tue 26 Nov
• Omit weekdays. For example, if the expected pattern is E, MMM d, y, then the user
can enter Nov 26, 2013, and the converter autocorrects the date to Tuesday, Nov
26, 2013. Invalid weekdays are not supported. For instance, the converter will
throw an exception if the user enters Wednesday, Nov 26, 2013.
oj.IntlNumberConverter supports parser leniency as follows:
• If the input does not match the expected pattern, Oracle JET attempts to locate a
number pattern within the input string. For instance, if the pattern is #,##0.0, then
the input string abc-123.45de will be parsed as -123.45.
• For the currency style, the currency symbol can be omitted. Also, the negative sign
can be used instead of a negative prefix and suffix. As an example, if the pattern
option is specified as "\u00a4#,##0.00;(\u00a4#,##0.00)", then ($123), (123), and
-123 will be parsed as -123.
• When the style is percent, the percent sign can be omitted. For example, 5% and 5
will both be parsed as 0.05.
10-12
Chapter 10
Using Oracle JET Converters
The image below shows the basic oj-input-date-time element configured for time
zone support. In this example, the component is converted using the Z pattern.
The oj-input-date-time element is initialized with its converter attribute, in this case a
method named dateTimeConverter.
<div id="div1">
<oj-label for="timezone">InputDateTime Timezone converter</oj-label>
<oj-input-date-time id="timezone" value={{dateTimeValue}}
converter=[[dateTimeConverter]]>
</oj-input-date-time>
<br/><br/>
<p>
<oj-label for="patternSelector">Pattern options:</oj-label>
<oj-combobox-one id="patternSelector" value="{{patternValue}}">
<oj-option value="MM/dd/yy hh:mm:ss a Z">MM/dd/yy hh:mm:ss a Z</oj-option>
<oj-option value="MM-dd-yy hh:mm:ss a VV">MM-dd-yy hh:mm:ss a VV</oj-option>
<oj-option value="MM-dd-yy hh:mm X">MM-dd-yy hh:mm X</oj-option>
</oj-combobox-one>
</p>
<p>
<oj-label for="isoStrFormatSelector">isoStrFormat options:</oj-label>
<oj-combobox-one id="isoStrFormatSelector" value="{{isoStrFormatValue}}">
<oj-option value="offset">offset</oj-option>
<oj-option value="zulu">zulu</oj-option>
<oj-option value="local">local</oj-option>
</oj-combobox-one>
</p>
<br/>
<span class="oj-label">Current dateTime value is: </span>
<span><oj-bind-text value="[[dateTimeValue]]"></oj-bind-text></span>
//...contents omitted
</div>
10-13
Chapter 10
Using Oracle JET Converters
The ViewModel contains the dateTimeConverter() definition. Note that you must also
add the ojs/timezonedata to your RequireJS definition to access the time zone data
files.
require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'ojs/
ojdatetimepicker',
'ojs/ojselectcombobox', 'ojs/ojtimezonedata', 'ojs/ojlabel'],
function FormatModel()
{
var self = this;
this.dateTimeValue = ko.observable("2013-12-01T20:00:00-08:00");
this.patternValue = ko.observable("MM/dd/yy hh:mm:ss a Z");
this.isoStrFormatValue = ko.observable("offset");
this.dateTimeConverter =
ko.observable(oj.Validation.converterFactory(oj.ConverterFactory.CONVERTER_TYPE_DATET
IME).
createConverter(
{
pattern : self.patternValue(),
isoStrFormat: self.isoStrFormatValue(),
timeZone:'Etc/GMT-08:00'
}));
//Note that ojCombobox's value is always encapsulated in an array
this.patternValue.subscribe(function (newValue)
{
self.dateTimeConverter(oj.Validation.converterFactory(oj.ConverterFactory.CONVERTER_T
YPE_DATETIME).
createConverter(
{
pattern : newValue,
isoStrFormat: self.isoStrFormatValue(),
timeZone:'Etc/GMT-08:00'
}));
});
this.isoStrFormatValue.subscribe(function (newValue)
{
self.dateTimeConverter(oj.Validation.converterFactory(oj.ConverterFactory.CONVERTER_T
YPE_DATETIME).
createConverter(
{
pattern : self.patternValue(),
isoStrFormat: newValue,
timeZone:'Etc/GMT-08:00'
}));
});
//...contents omitted
$(function ()
{
ko.applyBindings(new FormatModel(), document.getElementById('div1'));
});
});
10-14
Chapter 10
Using Oracle JET Converters
For an additional example illustrating how to add time zone support to oj-input-date-
time and oj-input-time elements, see Input Date and Time - Time Zone.
10-15
Chapter 10
Using Oracle JET Converters
a specialized format() method that turns dates close to the current date into
relative terms for display. For example, in the en-US locale, the relative terms will
display Today, Yesterday, and Tomorrow. If a relative notation for the date value does
not exist, then the date is formatted using the regular Oracle JET format() method
supported by the Oracle JET IntlDateTimeConverter.
RelativeDateTimeConverter._DEFAULT_RELATIVE_DATETIME_CONVERTER_OPTIONS =
{
'formatUsing' : "displayName",
'dateField' : "week"
};
// Formats a value using the relative format options. Returns the formatted
// value and a title that is the actual date, if formatted value is a
relative date.
RelativeDateTimeConverter.prototype.format = function(value)
{
var base;
var formattedRelativeDate;
// We get our wrapped converter and call its formatRelative function and
10-16
Chapter 10
Using Oracle JET Converters
store the
// return value ("Today", "Tomorrow" or null) in formatted variable
// See oj.IntlDateTimeConverter#formatRelative(value, relativeOptions)
// where relativeOptions has formatUsing and dateField options.
dateField is
// 'day', 'week', 'month', or 'year'.
formattedRelativeDate = this._getWrapped().formatRelative(value,
this._getRelativeOptions());
// We get our wrapped converter and call its format function and store
the returned
// string in base variable. This will be the actual date, not a relative
date.
base = this._getWrapped().format(value);
// Returns a hint
RelativeDateTimeConverter.prototype.getHint = function ()
{
return "";
};
RelativeDateTimeConverter.prototype._getWrapped = function ()
{
return this._wrappedConverter;
};
RelativeDateTimeConverter.prototype._getRelativeOptions = function ()
{
var options = this._options;
var relativeOptions = {};
10-17
Chapter 10
Using Oracle JET Converters
The following code sample shows a simple converter factory for the
RelativeDateTimeConverter. The code also registers the factory with Oracle JET as
a new relativeDate type using the oj.Validation module.
/**
* A converter factory for "relativeDate" that supports custom
* formatting of normal and relative date times. A relative date example is
"Today",
* "Tomorrow", or "This Week", etc.
*/
RelativeDateTimeConverterFactory = (function () {
/**
* Private function that takes regular and relative options.
*/
function _createRelativeDateTimeConverter(options)
{
return new RelativeDateTimeConverter(options);
}
return {
'createConverter': function (options) {
return _createRelativeDateTimeConverter(options);
}
};
}());
The Oracle JET Cookbook includes the Converters Factory demo that shows how to
use the number and date time converters directly in your pages without binding them
to an Oracle JET component. In the demo image, the salary is a number formatted as
currency, and the start date is an ISO string formatted as a date.
10-18
Chapter 10
Using Oracle JET Converters
The sample code below shows a portion of the viewModel that defines a
salaryConverter to format a number as currency and a dateConverter that formats the
start date using the date format style and medium date format.
The code sample below shows the portion of the markup that sets the display output to
the formatted values contained in amySalary, amyStartDate, garySalary, garyStartDate.
<td>
<div class="oj-panel oj-panel-alt4 demo-panel-customizations">
<h3 class="oj-header-border">Amy Flanagan</h3>
<img src="images/Amy.png" alt="Amy">
<p>Product Manager</p>
<span style="white-space:nowrap;"><b>Salary</b>:
<span>
<oj-bind-text value="[[amySalary]]"></oj-bind-text>
</span>
</span>
<br />
<span style="white-space:nowrap;"><b>Joined</b>:
<span>
<oj-bind-text value="[[amyStartDate]]"></oj-bind-text>
</span>
</span>
<br />
10-19
Chapter 10
Using Oracle JET Validators
</div>
</td>
<td>
<div class="oj-panel oj-panel-alt2 demo-panel-customizations">
<h3 class="oj-header-border">Gary Fontaine</h3>
<img src="images/Gary.png" alt="Gary">
<p>Sales Associate</p>
<span style="white-space:nowrap;"><b>Salary</b>:
<span>
<oj-bind-text value="[[garySalary]]"></oj-bind-text>
</span>
</span>
<br />
<span style="white-space:nowrap;"><b>Joined</b>:
<span>
<oj-bind-text value="[[garyStartDate]]"></oj-bind-text>
</span>
</span>
<br />
</div>
</td>
Topics:
• Using Oracle JET Validators with Oracle JET components
• Using Custom Validators in Oracle JET
• What are Asynchronous Validators?
10-20
Chapter 10
Using Oracle JET Validators
The script to create the view model for this example is shown below.
require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout', 'ojs/ojinputnumber',
'ojs/ojinputtext', 'ojs/ojdatetimepicker', 'ojs/ojvalidation-datetime', 'ojs/
ojlabel'],
function(oj, ko, $)
{
function DemoViewModel()
{
var self = this;
self.dateValue1 = ko.observable();
self.dateValue2 = ko.observable();
self.todayIsoDate = ko.observable(oj.IntlConverterUtils.dateToLocalIso(new
Date()));
self.milleniumStartIsoDate =
ko.observable(oj.IntlConverterUtils.dateToLocalIso(new Date(2000, 00, 01)));
self.validators = ko.computed(function()
{
return [{
type: 'dateTimeRange',
options: {
max: self.todayIsoDate(),
min: self.milleniumStartIsoDate(),
hint: {
'inRange': 'Enter a date that falls in the current millennium.'}}}];
});
};
$(
function()
{
ko.applyBindings(new DemoViewModel(), document.getElementById('validator-
example'));
}
);
});
When the user runs the page, the oj-input-date element displays an input field with
the expected date format. The help.instruction attribute set on the element displays
as a tooltip upon hovering. When the user clicks on the field, the validator hint
provided by the implicitly created oj.DateTimeRangeValidator is shown in a note
window, along with a calendar popup. If the user inputs data that is not within the
expected range, the built-in validator displays an error message with the expected
range.
10-21
Chapter 10
Using Oracle JET Validators
The error thrown by the Oracle JET validator when validation fails is represented by
the oj.ValidatorError object, and the error message is represented by an object that
duck-types oj.Message. The messages and hints that Oracle JET validators use when
they throw an error are resources that are defined in the translation bundle included
with Oracle JET. For more information about messaging in Oracle JET, see Working
with User Assistance.
You can also specify the validator on the element's validators attribute, if it exists. The
code sample below adds another oj-input-date element to the sample form and calls
a function which specifies the oj.DateTimeRangeValidator validator (dateTimeRange) in
the validators attribute.
<div class="oj-flex">
<div class="oj-flex-item">
<oj-label for="dateTimeRange2">'dateTimeRange' type in 'validators' option</oj-
label>
</div>
<div class="oj-flex-item">
<oj-input-date id="dateTimeRange2" name="dateTimeRange2" value="{{dateValue2}}"
validators="[[validators]]"
help.instruction="enter a date that falls in the current millenium
and not greater than today's date.></oj-input-date>
</div>
</div>
The highlighted code below shows the additions to the viewModel, including the
defined function, with options that set the valid minimum and maximum dates and a
hint that displays when the user sets the focus in the field.
10-22
Chapter 10
Using Oracle JET Validators
$(
function()
{
ko.applyBindings(new DemoViewModel(), document.getElementById('validator-
example'));
}
);
});
When the user runs the page for the en-US locale, the oj-input-date element displays
an input field that expects the user's input date to be between 01/01/00 and the current
date. When entering a date value into the field, the date converter will accept alternate
input as long as it can parse it unambiguously. This offers end users a great deal of
leniency when entering date values.
For example, typing 1-2-3 will convert to a Date that falls on the 2nd day of January,
2003. If the Date value also happens to fall in the expected Date range set in the
validator, then the value is accepted. If validation fails, the component will display an
error.
The options that each validator accepts are specified in JavaScript API Reference for
Oracle® JavaScript Extension Toolkit (Oracle JET).
The Oracle JET Cookbook contains the complete example used in this section as well
as examples that show the built-in validators for date restrictions, length, number
range, regular expression, and required fields. For details, see Validators.
For more information about Oracle JET component validation, see Understanding How
Validation and Messaging Works in Oracle JET Editable Components.
10-23
Chapter 10
Using Oracle JET Validators
10-24
Chapter 10
Using Oracle JET Validators
self.equalToPassword = {
validate: function(value)
{
var compareTo = self.password.peek();
if (!value && !compareTo)
return true;
else if (value !== compareTo)
{
throw new Error(bundle['app']['validator-equalTo']['summary']);
}
return true;
}
};
}
$(
function()
{
ko.applyBindings(new DemoViewModel(), document.getElementById('custom-
validator-example'));
}
);
2. Add code to your application that uses the custom validator.
The code sample below shows how you could add code to your page to use the
custom validator. In this example, both input fields are defined as oj-input-
password elements. The first oj-input-password element uses oj.RegExpValidator to
validate that the user has input a password that meets the application's password
requirements. The second oj-input-password element uses the equalToPassword
validator to verify that the password in the second field is equal to the password
entered in the first field.
<div id="custom-validator-example" class="oj-form oj-md-odd-cols-4 oj-md-labels-
inline oj-sm-odd-cols-12">
<div class="oj-flex">
<div class="oj-flex-item">
<oj-label show-required for="password">Password</oj-label>
</div>
<div class="oj-flex-item">
<oj-input-password id="password" name="password" required
value="{{password}}"
help.instruction="Enter at least 6 characters including a
number, one uppercase and lowercase letter"
validators= '[{
"type": "regExp",
"options" : {
"pattern": "(?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{6,}",
10-25
Chapter 10
Using Oracle JET Validators
"label": "Password",
"messageSummary" : "{label} too Weak",
"messageDetail": "You must enter a password that meets our
minimum security requirements."}}]'>
</oj-input-password>
</div>
</div>
<div class="oj-flex">
<div class="oj-flex-item">
<oj-label for="cpassword">Confirm Password</oj-label>
</div>
<div class="oj-flex-item">
<oj-input-password id="cpassword" name="cpassword"
value="{{passwordRepeat}}"
validators="[[[equalToPassword]]]"></oj-input-password>
</div>
</div>
</div>
For the complete code sample used in this section, see Validators (Custom).
The following JavaScript code shows the number range validator created in the
asyncvalidator object that returns a Promise.
10-26
Chapter 10
Using Oracle JET Validators
self.asyncValidator = {
// 'validate' is a required method
'validate' : function(value) {
var converterOption =
{type:"number",
options: {style: "currency", currency: "USD",
currencyDisplay: "code", pattern: "¤ ##,##0.00"}};
Note:
A Promise object in JavaScript represents a value that may not be available
yet, but will be resolved at some point in the future. In asynchronous
validation, the oj.AsyncValidator.validate() function returns a Promise that
evaluates to Boolean true if validation passes and if validation fails, it returns
an Error. For more, see the async-validators attribute section of ojInputText
or see Promise (MDN).
10-27
11
Working with User Assistance
The Oracle JET user assistance framework includes support for user assistance on
the editable components in the form of help, hints, and messaging that you can
customize as needed for your application. Editable components include oj-
checkboxset, oj-color*, oj-combobox*, oj-input*, oj-radioset, oj-select*, oj-slider, oj-
switch, and oj-text-area.
Note:
The oj-input* mentioned above refers to the family of input components
such as oj-input-date-time, oj-input-text, and oj-input-password, among
others. oj-color*, oj-combobox*, and oj-select* each represent two
components.
Topics:
• Typical Workflow for Working with User Assistance
• Understanding Oracle JET's Messaging APIs on Editable Components
• Understanding How Validation and Messaging Works in Oracle JET Editable
Components
• Using Oracle JET Messaging
• Configuring an Editable Component's oj-label Help Attribute
• Configuring an Editable Component's help.instruction Attribute
• Controlling the Display of Hints, Help, and Messages
Tip:
To add tooltips to plain text or other non-editable components, use oj-popup.
See Working with oj-popup.
11-1
Chapter 11
Understanding Oracle JET's Messaging APIs on Editable Components
• oj-color-palette
• oj-color-spectrum
• oj-combobox-many
• oj-combobox-one
• oj-input-date
• oj-input-date-time
• oj-input-number
• oj-input-password
• oj-input-text
11-2
Chapter 11
Understanding Oracle JET's Messaging APIs on Editable Components
• oj-input-time
• oj-radioset
• oj-select-many
• oj-select-one
• oj-slider
• oj-switch
• oj-text-area
Topics:
• Oracle JET Editable Component Messaging Attributes
• Oracle JET Component Messaging Methods
The Oracle JET Cookbook also includes descriptions and examples for working with
each component at: Form Controls.
See the Attributes section of the element's API documentation in JavaScript API
Reference for Oracle® JavaScript Extension Toolkit (Oracle JET) for additional details
about its messaging properties. Select the component you're interested in viewing
from the API list.
11-3
Chapter 11
Understanding How Validation and Messaging Works in Oracle JET Editable Components
See the Methods section of the component's API documentation in the JavaScript API
Reference for Oracle® JavaScript Extension Toolkit (Oracle JET) for details on how to
call the method, its parameters, and return value. Select the component you're
interested in viewing from the list.
Note:
When the application changes certain properties, the component might
decide to run normal validation depending on its current state. See
Mixed Validation below for additional details.
• Deferred Validation: Uses the required validator to validate the component's value.
The required validator is the only validator that participates in deferred validation.
During deferred validation all messages properties are cleared unless specified
otherwise. If the value fails deferred validation, validation errors are not shown to
the user immediately.
The editable component always runs deferred validation when:
11-4
Chapter 11
Understanding How Validation and Messaging Works in Oracle JET Editable Components
Note:
When the application changes certain properties programmatically, the
component might decide to run deferred validation depending on its
current state. See Mixed Validation below for additional details.
• Mixed Validation: Runs when the following properties are changed or methods are
called by the application. Either deferred or normal validation is run based on the
component's current state, and any validation errors are either hidden or shown to
the user. Mixed validation runs when:
– converter property changes
Topics:
• Understanding How an Oracle JET Editable Component Performs Normal
Validation
• Understanding How an Oracle JET Editable Component Performs Deferred
Validation
The Oracle JET Cookbook includes additional examples that show normal and
deferred validation at Validators (Component). For additional information about the
validators and converters included with Oracle JET, see Validating and Converting
Input.
Topics:
• Normal Validation Process When User Changes Value of an Editable Component
• Normal Validation Process When Validate() is Called on Editable Component
11-5
Chapter 11
Understanding How Validation and Messaging Works in Oracle JET Editable Components
b. The new value is formatted for display using the converter again and displayed
on the component.
Note:
If the component's value property happens to be bound to a Knockout
observable, then the value is written to the observable as well.
c. Errors are displayed on the component. The user can also view the details of
the error by setting focus on the component. By default, this will open a note
window.
Note:
If the component's value attribute is bound to a Knockout observable,
then the value is written to the observable array as well.
6. When the user fixes the error, the validation process begins again.
11-6
Chapter 11
Understanding How Validation and Messaging Works in Oracle JET Editable Components
Topics:
• Deferred Validation Process When an Oracle JET Editable Component is Created
• Deferred Validation Process When value Property is Changed Programmatically
You can also find additional detail in the JavaScript API Reference for Oracle®
JavaScript Extension Toolkit (Oracle JET). Select the component you’re interested in
from the navigation list.
Note:
Page authors can call showMessages() at any time to reveal deferred
messages.
11-7
Chapter 11
Using Oracle JET Messaging
• The page has code that changes the element's value attribute, or
• The page author refreshes the ViewModel observable with a new server value.
In both cases, the element will update itself to show the new value as follows:
1. All messages properties are cleared on the editable element and
onMessagesCustomChanged events triggered if applicable.
Note:
Page authors should ensure that the value you set is the expected type as
defined by the component's API and that the value can be formatted without
any errors for display.
Topics:
• Notifying an Oracle JET Editable Component of Business Validation Errors
• Understanding the oj-validation-group Component
• Creating Page Level Messaging
Topics:
• Using the messages-custom Attribute
• Using the showMessages() Method on Editable Components
11-8
Chapter 11
Using Oracle JET Messaging
In this example, the severity type button is toggled and a message of the selected
severity type is pushed onto the messages-custom array. The messages-custom attribute is
set on every form control in this example. When the messages-custom attribute is
changed, it is shown immediately. In this example, the user selected the Error severity
type, and the associated messages are shown for the various text input and selection
components.
In the corresponding JavaScript file, set the severity type and pass it to the
observable, appMessages, to display associated messages.
if (summary && detail)
{
msgs.push({summary: summary, detail: detail, severity: type});
}
self.appMessages(msgs);
11-9
Chapter 11
Using Oracle JET Messaging
In the corresponding JavaScript file you must set the messages-custom attribute as
emailAddressMessages.
For the complete example and code used to create the custom validator, see Cross-
Field Validation. The demo uses a custom validator to validate an observable value
against another. If validation fails the custom validator updates the messages-custom
attribute.
11-10
Chapter 11
Using Oracle JET Messaging
• Set focus on the first enabled component in the group using the focusOn() method.
They can also focus on the first enabled component showing invalid messages
using focusOn("@firstInvalidShown").
• Show deferred messages on all tracked components using the showMessages()
method.
For details about the oj-validation-group component's attributes and methods, see oj-
validation-group.
The oj-validation-group searches all its descendants for a valid property, and adds
them to the list of components it is tracking. When it adds a component, it does not
check the tracked component’s children since the component’s valid state should
already be based on the valid state of its children, if applicable.
When it finds all such components, it determines its own valid property value based
on all the enabled (including hidden) components it tracks. Any disabled or readonly
components are ignored in calculating the valid state.
The most invalid component's valid property value will be the oj-validation-group
element’s valid property value. When any of the tracked component's valid value
changes, oj-validation-group will be notified and will update its own valid value if it
has changed.
The following code sample shows how an oj-validation-group can be used to track
the overall validity of a typical form.
<div id="validation-usecase">
<oj-validation-group id="tracker" valid="{{groupValid}}">
<oj-form-layout label-edge="start" id="fl1">
11-11
Chapter 11
Using Oracle JET Messaging
A portion of the script to create the view model for this example is shown below. This
portion pertains to the oj-validation-group used above. The full script is contained in
the Cookbook sample linked below.
require(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojknockout',
'ojs/ojcheckboxset', 'ojs/ojformlayout',
'ojs/ojinputtext', 'ojs/ojbutton',
'ojs/ojvalidationgroup'],
function (oj, ko, $)
// this callback gets executed when all required modules
// for validation are loaded
{
function DemoViewModel() {
var self = this;
self.tracker = ko.observable();
...
$(
function () {
ko.applyBindings(new DemoViewModel(),
11-12
Chapter 11
Using Oracle JET Messaging
document.getElementById('validation-usecase'));
}
);
});
The figure below shows the output for the code sample. The status text at the bottom
of each instance shows the valid state of the oj-validation-group, and by extension,
the form.
The Oracle JET Cookbook contains the complete example used in this section. See
Form Fields.
For an example showing the oj-validation-group used for cross-field validation, see
Cross-Field Validation.
11-13
Chapter 11
Using Oracle JET Messaging
• Notification Messages: These are small popups, usually in one corner of the
view port.
• Inline Messages: These are messages in line with other components on the
page, such as messages below the page header.
• Overlay Messages: These are messages that pop up on top of other
components, such as below the page header overlapping the page contents.
The oj-messages element is a wrapper tag that manages the layout of individual
messages. This wrapper tag allows for mass dismissal of messages. For single page
applications, it is recommended that no more than one instance of oj-messages be
defined for these three layouts to avoid clutter on the page.
Inline-style messages are typically used as page level inline messages just below the
page header.
The Cookbook contains an example that showcases inline messages. See Inline
messages in page layout.
Overlay-style messages are typically used as a page level overlay message just below
the page header. These messages usually pertain to the page or region of the
application.
The Cookbook contains an example that showcases overlay messages. See Overlay
messages in page layout.
Notification-style messages are typically used for asynchronous page-level messages
in a corner of the viewport.
11-14
Chapter 11
Using Oracle JET Messaging
2. To specify that the message displays as an overlay, set the display attribute to
general and the position attribute to {}.
<oj-messages id="oj-messages-id" messages="[[appMessages]]" display="general"
position= "{}">
3. To create notification style messages, set the display attribute to notification and
the position attribute to {}.
<oj-messages id="notificationMessages" messages="[[emailMessages]]"
position="{}" display="notification"></oj-messages>
Note:
For overlay and notification messages, you can specify the position
attribute fully instead of {}. Theming variables are available for setting
default positioning at application level. See the API doc for more.
4. There are three ways to create oj-message children for any type of message:
a. Include oj-message as direct children of oj-messages.
<oj-messages id="inlineMessages">
<oj-message message='{"summary": "Some summary", "detail": "Some detail",
"autoTimeout": 5000}'></oj-message>
<oj-message message="[[surveyInstructions]]"></oj-message>
<oj-message message="[[surveySubmitConfirmation]]"></oj-message>
</oj-messages>
b. Use oj-bind-for-each to generate oj-message children.
11-15
Chapter 11
Configuring an Editable Component's oj-label Help Attribute
Note:
The close-affordance attribute should be set to none only when the user
cannot dismiss messages manually. Messages can still be closed
programmatically by calling the close() method on oj-message.
Note:
Sound is an accessibility feature required for low-vision users who view a
zoomed section of the UI. Because messages may be shown outside of
the zoomed section, users require sound to be played to notify them of
new messages.
For details about the oj-messages component's attributes and methods, see oj-
messages. For oj-message, see oj-message.
11-16
Chapter 11
Configuring an Editable Component's oj-label Help Attribute
• definition: Contains the help definition text that displays when the user does one
of the following:
– hovers over the help icon
– tabs into the help icon with the keyboard
– presses and holds the help icon on a mobile device
• source: Contains the URL to be used in the help icon's link
The following image shows three oj-label components configured to use the help
attribute. The top component is configured with both a definition and source help sub-
property, and the image shows the text and clickable pointer that displays when the
user hovers over the help icon. In the middle image, the oj-label component includes
a help icon that links to a URL when the user clicks it. In the bottom image, the oj-
label displays custom help text when the user hovers over the label or help icon.
11-17
Chapter 11
Configuring an Editable Component's help.instruction Attribute
11-18
Chapter 11
Configuring an Editable Component's help.instruction Attribute
self.validators = ko.computed(function()
{
return [{
type: 'regExp',
options: {
pattern: '[a-zA-Z0-9]{3,}',
messageDetail: 'You must enter at least 3 letters or
numbers'}}];
});
self.helpInstruction = "enter at least 3 alphanumeric characters";
};
$(
function()
{
ko.applyBindings(new MemberViewModel(), document.getElementById('form-
container'));
}
);
});
For the complete example, see Help and Title in the Oracle JET Cookbook. For
additional detail about the oj-input-text component, see the ojInputText API
documentation.
For additional information about the regular expression validator, see About Oracle
JET Validators and Converters.
11-19
Chapter 11
Controlling the Display of Hints, Help, and Messages
The code sample below shows the markup for the oj-input-date component used in
this example. The example includes definitions for help.instruction, validator hints,
and a data value for custom messages on validation failure. The sample also shows
the markup for a oj-label element with the help attribute.
<div id="form-container" class="oj-form">
<h3 class="oj-header-border">Default Display of Messages, Hints, Help Instruction</
h3>
<oj-label for="date10" help.definition="custom help text"> Input Date</oj-label>
<oj-input-date id="date10" size="30" name="date10" required placeholder="month day,
11-20
Chapter 11
Controlling the Display of Hints, Help, and Messages
year"
help.instruction='enter a date in your preferred format and we
will attempt to figure it out'
converter="[[longDateConverter]]"
value="{{birthdate}}" validators="[[validators]]"
translations='{
"required": {
"hint": "validator hint: required",
"messageSummary": "<html>custom summary: {label}
<b>Required</b></html>",
"messageDetail": "<html>custom detail: A value is required for
this field</html>"}}'>
</oj-input-date>
The code sample below shows the custom messages on validation failure set in the
application’s script.
function MemberViewModel()
{
var self = this;
self.validators = ko.computed(function()
{
return [{
type: 'datetimeRange',
options: {
min: oj.IntlConverterUtils.dateToLocalIso(new Date(1930, 00, 01)),
max: oj.IntlConverterUtils.dateToLocalIso(new Date(1995, 11,31)),
hint: {
inRange: 'Validator hint: datetimeRange: January 1, 1930 -
November 30, 1995 years'},
messageSummary:{
rangeOverflow: 'Date later than max.',
rangeUnderflow: 'Date earlier than min.'},
messageDetail: {
rangeOverflow: 'The value \'{value}\' is not in the expected
range; it is too high.',
rangeUnderflow: 'The value \'{value}\' is not in the expected
range; it is too low.'}
}}];
});
//...Contents Omitted
}
$(function(){
ko.applyBindings(new MemberViewModel(), document.getElementById('form-
container'));
});
Using the display-options element attribute in your markup, you can change the
default behavior of the hints, help, and messaging properties of a single editable
component on your page. To control the behavior of all editable components on the
page, you can use the oj.Component.setDefaultOptions() method in your application
script to set displayOptions values.
• converterHint: Set to none to turn off the display or set to notewindow to change the
default placement from placeholder text to a note window.
• validatorHint: Set to none to turn off the display.
11-21
Chapter 11
Controlling the Display of Hints, Help, and Messages
• messages: Set to none to turn off the display or set to notewindow to change the
default placement from inline to a note window.
To change the default display type (inline or note window) and display options for
hints, help, and messages:
1. Add the editable element to your page.
If you need help, see Adding an Oracle JET Custom Element to Your Page.
2. To change the default display type (inline or note window) for an individual editable
component, add the display-options attribute to your component definition and set
it as needed.
For example, to turn off the display of hints and help.instruction and to display
messages in a note window, add the highlighted markup to your component
definition:
<oj-input-date id="date12" required value="{{birthdate}}"
converter="[[longDateConverter]]" validators="[[validators]]"
help.instruction="enter a date in your preferred format and we will attempt to
figure it out"
display-options='{"converterHint": "none", "validatorHint": "none",
"helpInstruction": "none", "messages": "notewindow"}'
... contents omitted
}"
3. To change the default display and location for all editable components in your
application, add the oj.Component.setDefaultOptions() method to your application's
script and specify the desired displayOptions.
For example, to turn off the display of hints and help and to display messages in a
note window, add the ojComponent.setDefaultOptions() method with the arguments
shown below.
oj.Components.setDefaultOptions({
'editableValue':
{
'displayOptions':
{
'converterHint': ['none'],
'validatorHint': ['none'],
'messages': ['notewindow'],
'helpInstruction': ['none']
}
}});
The Oracle JET cookbook contains the complete code for this example at User
Assistance. You can also find additional examples that illustrate hints, help, and
messaging configuration.
11-22
12
Developing Accessible Applications
Oracle JET and Oracle JET components have built-in accessibility features for
persons with disabilities. Use these features to create accessible Oracle JET web and
hybrid mobile application pages.
Topics:
• Typical Workflow for Developing Accessible Oracle JET Applications
• Oracle JET and Accessibility
• Using the Accessibility Features of Oracle JET Components
• Creating Accessible Oracle JET Pages
12-1
Chapter 12
Using the Accessibility Features of Oracle JET Components
12-2
Chapter 12
Creating Accessible Oracle JET Pages
Note:
Some Oracle products have run-time accessibility modes that render content
optimized for certain types of users, such as users of screen readers. For the
most part, Oracle JET renders all accessibility-related content all of the time.
There is only a mode for users that rely on the operating system's high
contrast mode, which is described in Creating Accessible Oracle JET Pages.
Oracle JET components that provide keyboard and touch navigation list the keystroke
and gesture end user information in their API documentation. Since the navigation is
built into the component, you do not need to do anything to configure it.
You can access an individual Oracle JET component's accessibility features and
requirements in the JavaScript API Reference for Oracle® JavaScript Extension
Toolkit (Oracle JET). Select the component you're interested in from the list on the left.
You can also find the list of supported keystrokes and gestures for each Oracle JET
component that supports keystrokes and gestures in the Oracle® JavaScript
Extension Toolkit (JET) Keyboard and Touch Reference.
Note:
In most cases, end-user documentation for your application must describe
information about accessibility, such as example keystrokes needed to
operate certain components.
Topics:
• Configuring WAI-ARIA Landmarks
• Configuring High Contrast Mode
• Hiding Screen Reader Content
• Using ARIA Live Region
12-3
Chapter 12
Creating Accessible Oracle JET Pages
directly to a desired region. Without landmarks, assistive technology users must use
the TAB key to navigate through a page.
The Oracle JET team recommends the use of WAI-ARIA landmarks to ensure page
accessibility and provides examples you can use in the Oracle JET Starter Template
collection. The following figure shows the run-time view of the Oracle JET Web Nav
Drawer Starter Template. In this example, the page is organized into regions
compatible with WAI-ARIA landmark regions, including regions for the banner,
navigation, main, and contentinfo landmarks.
The highlighted code in the following example shows the landmarks for the Web Nav
Drawer Starter Template. Each landmark is placed on the HTML element that defines
the landmark region: div for the navigation regions, header for the banner region, oj-
module for the main region, and footer for the contentinfo region.
<!DOCTYPE html>
<html lang="en-us">
<head>
<title>Oracle JET Starter Template - Web Nav Drawer</title>
...contents omitted
</head>
<body class="oj-web-applayout-body">
...contents omitted
item.renderer="[[oj.KnockoutTemplateUtils.getRenderer('navTemplate', true)]]"
on-click="[[toggleDrawer]]"
selection="{{router.stateId}}">
</oj-navigation-list>
</div>
12-4
Chapter 12
Creating Accessible Oracle JET Pages
item.renderer="[[oj.KnockoutTemplateUtils.getRenderer('navTemplate', true)]]"
selection="{{router.stateId}}">
</oj-navigation-list>
</div>
</header>
</div>
</div>
</body>
</html>
For information about downloading the Oracle JET Starter Templates, see
Downloading Oracle JET with a Starter Template. For details about working with the
Oracle JET Starter Templates, see Create a Web Application Using the Oracle JET
Starter Templates.
For additional information about WAI-ARIA landmark roles, see landmark_roles.
12-5
Chapter 12
Creating Accessible Oracle JET Pages
Oracle JET provides the oj-hicontrast class that you can use to configure high
contrast mode in your application.
Topics:
• Understanding Color and Background Image Limitations in High Contrast Mode
• Adding High Contrast Mode to Your Oracle JET Application
• Adding High Contrast Images or Icon Fonts
• Testing High Contrast Mode
12-6
Chapter 12
Creating Accessible Oracle JET Pages
high contrast mode. If the script succeeds at detection, it will place the oj-hicontrast
class on the body element.
When the class is added, the .oj-hicontrast CSS styles are applied to the page where
defined. The code below shows an excerpt from the Alta CSS which changes the
outline-width to 3 on the ojButton component when the button has focus.
.oj-hicontrast .oj-button.oj-focus {
outline-width: 3px; }
Note:
For disabled content, JET supports an accessible luminosity contrast ratio,
as specified in WCAG 2.0 - Section 1.4.3 Contrast (Minimum), in the themes
that are accessible.
Section 1.4.3 says that text or images of text that are part of an inactive user
interface component have no contrast requirement. Because disabled
content may not meet the minimum contrast ratio required of enabled
content, it cannot be used to convey meaningful information. For example, a
checkbox may still appear checked in disabled mode, but since the colors
may not pass contrast ratio, you cannot rely on all users being able to see
that it's checked. If the information is needed to interact with the page
correctly, you must convey it using a different method, for example as plain
text.
12-7
Chapter 12
Creating Accessible Oracle JET Pages
You can find the .oj-helper-hidden-accessible style defaults in the Oracle JET CSS
file. For the Alta theme, the CSS file is: css/libs/oj/vxxx/alta/oj-alta.css. For
additional information about theming and Oracle JET, see Theming Applications.
The aria-live attribute identifies an element as a live region. It takes three possible
values:
• off: No notification
• polite: Screen reader notifies user once the current task is complete
If the value of the aria-live attribute defined for an element is set to polite, your
screen reader will not be interrupted and will announce the changes in the ARIA live
region when the user has no activity on the screen. If the value is set to assertive, the
new information has high priority and should be notified or announced to the user
immediately.
12-8
Chapter 12
Creating Accessible Oracle JET Pages
You can also use some of the advanced ARIA live region attributes to intimate
information of the entire live region or only a portion of the live region to assistive
technologies. Some of the advanced live region attributes to use are:
• aria-atomic: The aria-atomic attribute is used along with aria-live attribute when
the page contains live regions. This attribute is used to set whether the assistive
technologies should present the entire live region as a single unit or to only
announce the regions that have been changed. The possible values are true or
false. The default value is false.
The following example shows how to set up the observables and event listeners in the
appController.js file of the application.
self.manner = ko.observable('assertive');
self.message = ko.observable();
document.getElementById('globalBody').addEventListener('announce',
announcementHandler, false);
function announcementHandler(event) {
self.message(event.detail.message);
self.manner(event.detail.manner);
};
var smQuery =
oj.ResponsiveUtils.getFrameworkQuery(oj.ResponsiveUtils.FRAMEWORK_QUERY_KEY.SM_ONLY);
self.smScreen = oj.ResponsiveKnockoutUtils.createMediaQueryObservable(smQuery);
var mdQuery =
oj.ResponsiveUtils.getFrameworkQuery(oj.ResponsiveUtils.FRAMEWORK_QUERY_KEY.MD_UP);
12-9
Chapter 12
Creating Accessible Oracle JET Pages
self.mdScreen = oj.ResponsiveKnockoutUtils.createMediaQueryObservable(mdQuery);
self.router = oj.Router.rootInstance;
self.router.configure({
'dashboard': {
label: 'Dashboard', enter: function () {
}, isDefault: true
},
'incidents': {
label: 'Incidents', enter: function () {
}
},
'customers': {
label: 'Customers', enter: function () {
}
},
'about': {
label: 'About', enter: function () {
}
}
});
oj.Router.defaults['urlAdapter'] = new oj.Router.urlParamAdapter();
... contents omitted
To send the announcement, you can use a dispatcher that can be defined within the
page viewModel files or in the router enter method. The following example shows a
page viewModel file that fires a dispatch in the self.handleBindingsApplied lifecycle
method.
define(['ojs/ojcore', 'knockout', 'jquery', 'ojs/ojpopup'],
function (oj, ko, $) {
function DashboardViewModel() {
var self = this;
For more information on using an ARIA live region, see ARIA Live Regions.
12-10
13
Internationalizing and Localizing
Applications
Oracle JET supports internationalization and globalization of Oracle JET web and
hybrid mobile applications. Configure your Oracle JET application so that the
application can be used in a variety of locales and international user environments.
Topics:
• Typical Workflow for Internationalizing and Localizing Oracle JET Applications
• About Internationalizing and Localizing Oracle JET Applications
• Internationalizing and Localizing Oracle JET Applications
• Working with Oracle JET Translation Bundles
13-1
Chapter 13
About Internationalizing and Localizing Oracle JET Applications
without engineering changes to the software. Localization (L10N) is the use of locale-
specific language and constructs at run time. Oracle has adopted the industry
standards for I18N and L10N such as World Wide Web Consortium (W3C)
recommendations, Unicode technologies, and Internet Engineering Task Force (IETF)
specifications to enable support for the various languages, writing systems, and
regional conventions of the world.
Languages and locales are identified with a standard language tag and processed as
defined in BCP 47.
Oracle JET includes Oracle National Language Support (NLS) translation support for
the languages listed in the following table.
Oracle JET translations are stored in a resource bundle. You can add your own
translations to the bundles. For additional information see Adding Translation Bundles
to Oracle JET.
Oracle JET also includes formatting support for over 180 locales. Oracle JET locale
elements are based upon the Unicode Common Locale Data Repository (CLDR) and
are stored in locale bundles. For additional information about Unicode CLDR, see
http://cldr.unicode.org. You can find the supported locale bundles in the Oracle JET
distribution:
js/libs/oj/vxxx/resources/nls
It is the application's responsibility to determine the locale used on the page. Typically
the application determines the locale by calculating it on the server side from the
browser locale setting or by using the user locale preference stored in an identity store
and the supported translation languages of the application.
Once the locale is determined, your application must communicate this locale to
Oracle JET for its locale sensitive operations such as loading resource bundles and
formatting date-time data. Oracle JET determines the locale for locale sensitive
operations in the following order:
1. Locale specification in the RequireJS configuration.
13-2
Chapter 13
Internationalizing and Localizing Oracle JET Applications
Note:
The locale and resource bundles are loaded automatically only when your
application uses the RequireJS ojl10n plugin. For information about using
RequireJS in your Oracle JET application, see Using RequireJS for Modular
Development.
Finally, Oracle JET includes validators and converters that use the locale bundles.
When you change the locale on the page, an Oracle JET component has built in
support for displaying content in the new locale. For additional information about
Oracle JET's validators and converters, see Validating and Converting Input.
Topics:
• Using Oracle JET's Internationalization and Localization Support
• Enabling Bidirectional (BiDi) Support in Oracle JET
• Setting the Locale Dynamically
• Working with Currency, Dates, Time, and Numbers
If you want to specify the French (Canada) locale, you would specify the following
instead:
<html lang="fr-CA">
13-3
Chapter 13
Internationalizing and Localizing Oracle JET Applications
Tip:
The lang specification isn’t case sensitive. Oracle JET will accept FR-FR, fr-
fr, etc., and map it to the correct resource bundle directory.
When you specify the locale in this manner, any Oracle JET component on your page
will display in the specified language and use locale constructs appropriate for the
locale.
If the locale doesn’t have an associated resource bundle, Oracle JET will load the
lesser significant language bundle. If Oracle JET doesn’t have a bundle for the lesser
significant language, it will use the default root bundle. For example, if Oracle JET
doesn’t have a translation bundle for fr-CA, it will look for the fr resource bundle. If the
fr bundle doesn’t exist, Oracle JET will use the default root bundle and display the
strings in English.
In the image below, the page is configured with the oj-input-date-time component.
The figure shows the effect of changing the lang attribute to fr-FR.
If you type an incorrect value in the oj-input-date-time field, the error text is displayed
in the specified language. In this example, the error is displayed in French.
13-4
Chapter 13
Internationalizing and Localizing Oracle JET Applications
The image below shows the same oj-input-date-time field that displays if you specify
the Hebrew Israel locale and change the dir attribute to rtl.
Once you have enabled BiDi support in your Oracle JET application, you must still
ensure that your application displays properly in the desired layout and renders strings
as expected.
Note:
JET does not support the setting of the dir attribute on individual HTML
elements which would cause a page to show mixed directions. Also, if you
programmatically change the dir attribute after the page has been initialized,
you must reload the page or refresh each JET component.
13-5
Chapter 13
Internationalizing and Localizing Oracle JET Applications
The image below shows the Oracle JET JET-Localization.zip application configured to
display a menu that displays a department list when clicked and a date picker. By
default, the page is set to the en-US locale. Both the menu and date picker are
displayed in English.
The application also includes a button set which shows the United States of America,
France, and Czech Republic flags. When the user clicks one of the flags, the page
locale is set to the locale represented by the flag: en-US, fr-FR, or cs-CZ.
Note:
The flags used in this example are for illustrative use only. Using national
flags to select a UI language is strongly discouraged because multiple
languages are spoken in one country, and a language may be spoken in
multiple countries as well. In a real application, you can use clickable text
instead that indicates the preferred language to replace the flag icons.
The figure below shows the updated department list after the user clicks the French
and Czech Republic flags.
13-6
Chapter 13
Internationalizing and Localizing Oracle JET Applications
The code that sets the locale in this example uses the oj.Config.setLocale() function
call highlighted below. The menu is refreshed in the ViewModel to reload the
department list for the chosen locale.
// When the country flags are clicked we get a new language to set as the current
locale
self.setLang = function(evt) {
var newLang = '';
var lang = evt.currentTarget.id;
switch (lang){
case '?eština':
newLang = 'cs-CZ';
break;
case 'français':
newLang = 'fr-FR';
break;
default:
newLang = 'en-US';
}
oj.Config.setLocale(newLang,
function() {
$('html').attr('lang', newLang);
// In this callback function we can update whatever is needed with the
// new locale. In this example, we reload the menu items.
loadMenu();
}
);
}
When the application changes the locale by calling refresh() on the oj-input-date
component, the page will automatically update to use the new locale and display the
menu and date in the new locale. However, you must explicitly define the strings that
appear in the menu items and then retrieve those strings using the
oj.Translations.getTranslatedString() method.
getTranslatedString(key, var_args)
13-7
Chapter 13
Internationalizing and Localizing Oracle JET Applications
Note:
Do not use this functionality unless the application itself can switch the UI
locale dynamically. Dynamically changing the UI locale often ends up with
the UI in mixed languages or locales because the application may have
cached data that are locale sensitive.
The code that loads the menu is shown below. The menu items and menu button
labels are defined with a call to getTranslatedString(). The refresh() method of both
the menu and date component are called after the translations and locale bundles are
loaded for the new locale to refresh the display.
function DashboardViewModel() {
var self = this;
// Setting up knockout observables for the button label and the menu items
self.localeLabel = ko.observable();
self.menuNames = ko.observableArray([]);
For information about defining your own translation strings and adding them to the
Oracle JET resource bundle, see Adding Translation Bundles to Oracle JET.
When you use this approach to internationalize and localize your application, you must
consider every component and element on your page and provide translation strings
where needed. If your page includes a large number of translation strings, the page
can take a performance hit.
Also, if SEO (Search Engine Optimization) is important for your application, be aware
that search engines normally do not run JavaScript and access static text only.
13-8
Chapter 13
Working with Oracle JET Translation Bundles
Tip:
To work around issues with performance or SEO, you can add pages to your
application that are already translated in the desired language. When you
use pages that are already translated, the Knockout bindings are executed
only for truly dynamic pieces.
Topics
• About Oracle JET Translation Bundles
• Adding Translation Bundles to Oracle JET
Each supported language is contained in a directory under the nls directory. The
directory names use the following convention:
• lowercase for the language sub-tag (zh, sr, etc.)
• title case for the script sub-tag (Hant, Latn, etc.)
• uppercase for the region sub-tag (HK, BA, etc.)
The language, script, and region sub-tags are separated by hyphens (-). The following
image shows a portion of the directory structure.
13-9
Chapter 13
Working with Oracle JET Translation Bundles
// supported locales.
"fr-CA":1,
ar:1,
ro:1,
"zh-Hant":1,
nl:1,
it:1,
fr:1,
// ... contents omitted
13-10
Chapter 13
Working with Oracle JET Translation Bundles
tr:1,fi:1
});
The strings are defined in nested JSON objects so that each string is referenced by a
name with a prefix: oj-message.fatal, oj-message.error, etc.
The language translation resource bundles contain the Oracle JET string definitions
with the translated strings. For example, the following code sample shows a portion of
the French (Canada) translation resource bundle, contained in nls/fr-CA/
ojtranslations.js.
define({
"oj-message":{
fatal:"Fatale",
error:"Erreur",
warning:"Avertissement",
info:"Infos",
confirmation:"Confirmation",
"compact-type-summary":"{0}: {1}"
},
// ... contents omitted
});
When there is no translation available for the user's dialect, the strings in the base
language bundle will be displayed. If there are no translations for the user's preferred
language, the root language bundle, English, will be displayed.
At run time, the actual values are replaced into the message at the position of the
tokens by calling oj.Translations.getTranslatedString() with the key of the message
as the first argument and the parameters to be inserted into the translated pattern as
the second argument.
var params = {'username': 'foo', 'groupname': 'bar'};
oj.Translations.getTranslatedString("MyKey", params);
A message can have up to 10 numeric tokens. For example, the message "Sales
order {0} has {1} items" contains two numeric tokens. When translated, the tokens
can be reordered so that message token {1} appears before message token {0} in the
translated string, if required by the target language grammar. The JavaScript code that
calls getTranslatedString() remains the same no matter how the tokens are reordered
in the translated string.
13-11
Chapter 13
Working with Oracle JET Translation Bundles
Tip:
Use named tokens instead of numeric tokens to improve readability and
reuse.
For example, if you want your output to display [Date: {01/02/2015}, Time: {01:02
PM}, Cost: $38.99, Book Name: John's Diary], enter the following in your resource
bundle string:
"productDetail": "$[Date: ${01/02/2015$}, Time: ${01:02 PM$}, Cost: $$38.99, Book
Name: John's Diary$]"
13-12
Chapter 13
Working with Oracle JET Translation Bundles
define({
"root": {
"department": {
"label": "Select a department",
"menu1": "Sales",
"menu2": "Human Resources",
"menu3": "Transportation"
}
}
},
"fr": true,
"cs": true
});
When the locale is set to a French locale, the French translation bundle is loaded.
The code below shows the definition for the label and menu items in French.
define({
"label": "Sélectionnez un département",
"menu1": "Ventes",
"menu2": "Ressources humaines",
"menu3": "Transports"
})
You can also provide regional dialects for your base language bundle by just
defining what you need for that dialect.
define({
"label": "Canadian French message here"
});
Note:
When there is no translation available for the user's dialect, the strings in
the base language bundle will be displayed. In this example, the menu
items will be displayed using the French translations. If there are no
translations for the user's preferred language, the root language bundle,
whatever language it is, will be displayed.
config: {
ojL10n: {
merge: {
'ojtranslations/nls/ojtranslations': 'resources/nls/menu'
13-13
Chapter 13
Working with Oracle JET Translation Bundles
}
}
}
Note:
Oracle JET supports only one custom bundle to be merged with the
default JET translation bundle. If your application has more than one
translation bundle, combine them into one and then add the bundle to
the merge option.
13-14
14
Theming Applications
Oracle JET includes themes that provide styling across a web or hybrid mobile
application. You can use these themes as provided, or you can customize them
manually and through the tooling.
Topics:
• Typical Workflow for Theming an Oracle JET Application
• CSS Files Included With Oracle JET
• Best Practices for Using CSS and Themes
• Customizing Themes Using the Tooling
• Sass Files, Variables, and Tools
• Working with Images
14-1
Chapter 14
CSS Files Included With Oracle JET
Web themes are designed to be used in a browser on all platforms, and the same
theme can be used regardless of whether you are looking at a web page on a
desktop Firefox, Android Chrome, or iOS Safari browser.
• android , ios, and windows hybrid mobile themes for Android, iOS, and Windows
Hybrid themes are designed to be used with Cordova to create a hybrid mobile
application. The colors use Oracle's Alta look and feel, but otherwise these themes
try to match the look and feel of a native mobile application.
The Alta CSS included with the Oracle JET distribution are generated by the Sass
preprocessor and include the following files:
• oj-alta*.css: Readable version of the CSS
In addition, the Alta web theme includes the following generated CSS:
• oj-alta-notag.css: Readable version of the CSS generated without tag selectors
For additional details about Oracle JET theming and tag selectors, see Using Tag
Selectors or Classes.
• oj-alta-notag-min.css: Minified version of the CSS generated without tag
selectors.
If the CSS files provided by Oracle JET with your application are sufficient and you
only want to add a few application-specific styles, you may find that adding the classes
to app.css in your application’s src/css folder will meet your needs.
If, however, you want to use a different theme or add more than a few application-
specific classes, then you can use the Oracle JET tooling to generate your own CSS.
For instructions, see Customizing Themes Using the Tooling.
Use the recommended standards to generate your CSS and Themes. For more
information, see Best Practices for Using CSS and Themes.
Important:
Do not override Oracle JET CSS. Oracle JET’s CSS is considered private
and can change at any time.
14-2
Chapter 14
CSS Files Included With Oracle JET
DOCTYPE Requirement
In order for Oracle JET's theming to work properly, you must include the following line
at the top of all HTML5 pages:
<!DOCTYPE html>
If you don't include this line, the CSS in your application may not function as expected.
For example, you may notice that some elements aren't properly aligned.
Tip:
If you create an Oracle JET application using the tooling or one of the
sample applications, this line is already added for you, and you do not need
to add it yourself.
ThemeUtils
Oracle JET provides the oj.ThemeUtils class that you can use to obtain information
about the current theme and use that information to generate and apply a class.
The oj.ThemeUtils class provides the following services:
14-3
Chapter 14
Best Practices for Using CSS and Themes
• Accent colors: special colors that are used in things like messaging, drag and
drop, and data visualization components.
To look at examples of the different color sets and associated variables, see Theme
Builder.
For example, the following code specifies the Hebrew Israel (he-IL) locale with right-to-
left direction enabled:
<html lang=he-IL dir="rtl">
You can find out more about localizing your application and adding bidirectional
support in Internationalizing and Localizing Oracle JET Applications.
Customer CSS:
.branding-header{color: red}
Your CSS:
.branding-header{background-color: blue}
14-4
Chapter 14
Best Practices for Using CSS and Themes
Don’t set The application should set the font family for text once on
Do not set the font family like this:
the font the root of the page which allows the application to change
family in the the font family as needed. In order to blend in with the font .acme-branding-header {
CSS family chosen by the application, do not set the font family in font-family: arial;
the CSS. }
Consider Consider Using REM for font sizes in order to blend in with
.acme-branding-header {
using REM the base font size the application has chosen.
font-size: 1.2rem;
for font
}
sizes
Add bi- Oracle JET applications are expected to set dir="rtl" for
html:not([dir="rtl"]) .acme-
directional right-to-left (RTL) languages as described in Setting Text
branding-header {
(BIDI) Direction. You can use this setting to support both left-to-
right: 0; }
styling right (LTR) and RTL languages in your CSS.
support
html[dir="rtl"] .acme-branding-
header {
left: 0; }
Use oj- When Oracle JET detects high contrast mode, it places the
.acme-branding-header {
hicontrast oj-hicontrast selector on the body element which you can
border: 1px;
for high use to change the CSS as needed. See Configuring High
}
contrast Contrast Mode.
styling .oj-hicontrast .acme-branding-
header {
border: 2px;
}
14-5
Chapter 14
Customizing Themes Using the Tooling
For example, enter the following command to create the skeleton theme files for a
custom theme named myTheme: ojet create theme myTheme.
The command creates a new folder with the custom theme name in the
application’s src directory. The web folder is expanded to show the .scss files that
you can modify. The myTheme.scss is the aggregating file for the given platform. The
android, ios, and windows folders contain similar files with the name of the platform
in the title: _myTheme.android.settings.scss, _myTheme.web.settings.scss, and so
on.
You’ll find out more about modifying these files in a later step.
The theme.json file contains the version number of the theme, starting with 0.0.1.
14-6
Chapter 14
Customizing Themes Using the Tooling
Note:
The file locations will vary if you modified your directory structure as
described in Modify the Web Application’s File Structure or Modify the
Hybrid Mobile Application’s File Structure.
3. Use ojet build with the --theme option to build a development version of your
application with the new theme.
ojet build --theme=themeName[:android|ios|web|windows] [--platform=android|ios|
web|windows]
For example, the following command builds a web application with the myTheme
custom theme.
ojet build --theme=myTheme
4. To build more than one theme, add the --themes option with the names of the
desired themes separated by commas:
ojet build --theme=myTheme --themes=myTheme,myTheme1,myTheme2
5. To run your application with live reload enabled, enter ojet serve with the theme
option and optional platform.
ojet serve --theme=themeName[:android|ios|web|windows] [--platform=android|ios|
web|windows]
For example, the following command serves a web application with the myTheme
custom theme in the default browser.
ojet serve --theme=myTheme
6. Edit the themeName.platform.settings.scss file to set variables as needed for your
application.
For detailed information about Oracle JET and SCSS variables, see SCSS
Variables.
For example, to turn off tag selectors, remove the comment from
the $allowTagSelectors variable if needed and set it to false.
$allowTagSelectors: false;
For information about Oracle JET's use of tag selectors, see Using Tag Selectors
or Classes.
When you use the Oracle JET CLI to serve to serve your application, live reload
will reload the page, and any changes you make to your theme files will display in
the browser.
7. To optimize performance, consider the use of variables to include only the CSS
that your application needs. For help, see Using Variables to Control CSS
Content.
Tip:
You can observe the effect of any changes immediately. This can be
handy if you’re not sure whether your application needs a given variable.
14-7
Chapter 14
Sass Files, Variables, and Tools
8. To exit the application, save your changes, and press Ctrl+C at the terminal
prompt.
You may need to press Ctrl+C a few times for the command to take effect.
Tip:
To make working with themes easier, Oracle JET provides Theme Builder,
an example application and tutorial. You can work with it online to see the
effect of changes before you apply them to your own application. You can
also download the sample and create your own themes offline for sharing.
Topics
• SCSS File Organization and Naming Convention
• SCSS Variables
• SCSS Tools
The following table lists the directory structure for the files contained within the alta
directory.
File Description
widgets/_oj.alta.formcontrol.inputnumber.scss Alta widget partial files
widgets/_oj.alta.table.scss
etc.
_oj.alta.variables.scss Contains the SCSS variables
_oj.alta.settings.scss Contains a commented version of the variables
file that you can remove the comments from and
modify
_oj.alta.widgets.scss Imports widget partial files
oj-alta.scss Generates the oj-alta.css and oj-alta-
min.css files
oj-alta-notag.scss Generates the oj-alta-notag.css and oj-alta-
notag-min.css files
14-8
Chapter 14
Sass Files, Variables, and Tools
SCSS Variables
The SCSS variables that Oracle JET uses to generate the application use the same
naming convention and are grouped together according to function. Oracle JET also
uses SCSS variables to default some component options, such as the ojButton’s
chroming option.
Using this convention, the following are all valid Oracle JET SCSS
variables: $buttonHeight, $sliderThumbWidth, $dataGridCellBgColorHover, $treeNodeBorde
rColorSelected
Variable Documentation
Variable names are typically self-documenting. In cases where additional
documentation is needed, the SCSS file will contain comments above the variable
definition.
Variable Categories
The following table lists the high level variable categories.
Category Description
Logistical Affects logistics, such as the path to an image
directory or text direction.
Performance Set these variables to include only the CSS your
application uses.
Palette Application’s color palette. Examples
include $interaction1Color, $background1Colo
r, and $border1Color.
Text Controls text appearance. Widget files use these
variables extensively. Examples
include $fontFamily, $rootFontSize, $textColo
r, and $linkTextColor.
General Sets general theme properties such as drag and
drop colors, border radius defaults, and
animation duration defaults.
14-9
Chapter 14
Sass Files, Variables, and Tools
Category Description
Widget Affects specific widgets or widget groups, such
as button or form control variables. Some
component default options are included here. For
example, you can change the ojButton
component’s chroming option
using $buttonChromingOptionDefault.
Some partials also allow you to control their content with a variable. For example, the
ojButton component has the $includeButtonClasses variable near the top of the
settings file.
$includeButtonClasses: $includeAllClasses !default;
To exclude the ojButton style classes, you can set $includeButtonClasses to false as
shown below.
$includeButtonClasses: false;
Oracle JET also uses several higher level groupings that let you control the CSS for a
logical group of components. The table lists the groups that are available for your use.
You can include or exclude classes and groups as shown in the following examples.
// Example: Exclude the dvt classes
$includeDvtClasses: false;
// Example: Include the chart and sunburst classes, exclude everything else
$includeAllClasses: false;
$includeChartClasses: true;
$includeSunburstClasses: true;
14-10
Chapter 14
Sass Files, Variables, and Tools
Note:
Some components depend on others. For example, the ojInputNumber uses
ojButton internally, so if you include the ojInputNumber classes, you will also
get the ojButton classes automatically.
Oracle JET does not support multiple directions on a page. The reason this is not
supported is that the proximity of elements in the document tree has no effect on the
CSS specificity, so switching directions multiple times in the page may not work the
way you might expect. The code sample below shows an example.
<style>
[dir=ltr] .foo {color: blue}
[dir=rtl] .foo {color: red}
</style>
<span dir="rtl">
<span dir="ltr">
<span class="foo">You might think I will be blue because dir=ltr is on,
a closer ancestor than dir=rtl. But css doesn't care
about proximity, so instead I am red
(because [dir=rtl] .foo {color: red} was defined last).
Isn't that surprising?
</span>
</span>
</span>
For additional information about Oracle JET and bidirectional support, see Enabling
Bidirectional (BiDi) Support in Oracle JET. For more information about CSS specificity,
see https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity.
Topics:
• Using Tag Selectors or Classes
• Using REM
• Using Normalize
14-11
Chapter 14
Sass Files, Variables, and Tools
a:visited {
color: $linkTextColorVisited;
}
/* headers */
h1, h2, h3, h4 {
color: $headerTextColor;
font-family: inherit;
font-style: normal;
font-weight: $headerFontWeight;
margin: 10px 0;
}
If you do not want to apply styles directly to the tags, you can specify that Oracle JET
use classes instead of tag selectors in your custom.scss file:
$allowTagSelectors: false;
// theme import
@import "../oj.alta/oj-alta";
The code below shows the classes that Oracle JET will use for the Alta theme's links
and headers when you set $allowTagSelectors to false. To use the class on your page,
specify the class in the tag element on your page (<a class=oj-link>).
/* links */
.oj-link {
color: $linkTextColor;
line-height: inherit;
text-decoration: none;
}
.oj-link:visited {
color: $linkTextColorVisited;
}
/* headers */
.oj-header {
color: $headerTextColor;
font-family: inherit;
font-style: normal;
font-weight: $headerFontWeight;
14-12
Chapter 14
Sass Files, Variables, and Tools
margin: 10px 0;
}
The following table lists the HTML tags with default Oracle JET tag styles and the
corresponding Oracle JET class.
If you also do not want to use the Oracle JET tag classes, you can
set $includeTagClasses to false in your custom.scss file as shown below.
$includeTagClasses:false;
Using REM
By default, Oracle JET uses REM (root em) for font sizing. This means that a specific
font size is set on the root (html) element, and other font sizes are defined relative to
that root size. The following example shows the default font sizing for the Alta theme.
// The browser usually uses 16px and the Alta default is 14px.
// Therefore set $rootFontValue to .875em to get 14px;
$rootFontSize: .875em !default; // 14px
To use REM, the font size must be set on the html element. If you want to use REM
but do not want to set a style directly on the html tag, you can reference the oj-html
class as described in Using Tag Selectors or Classes.
If you do not want to use REM, you can specify an alternate font size by modifying a
custom.scss file to use the units your application requires. The code sample below
shows how you could use pixels instead of rems to set font sizes.
// use px instead of rem for fonts
$rootFontSize: 12px;
$fontSize: $rootFontSize;
$smallestFontSize: $rootFontSize - 2px;
$smallFontSize: $rootFontSize - 1px;
$mediumFontSize: $rootFontSize + 2px;
$largeFontSize: $rootFontSize + 4px;
$largestFontSize: $rootFontSize + 6px;
14-13
Chapter 14
Working with Images
Using Normalize
By default, Oracle JET uses normalize.css to promote consistency when rendering
across browsers. If your application also uses normalize.css, add the import in your
custom.scss:
@import "../3rdparty/normalize/normalize";
If you do not want to use normalize, you can set the following variable to false in your
custom.scss:
$includeNormalize: false;
SCSS Tools
Oracle JET provides its own Sass mixins and doesn’t use Sass tools such as
Autoprefixer or Compass. This reduces the number of third-party tools required to
work with Oracle JET’s SCSS.
However, Oracle JET does support Sass source maps which can make it easier to
work with a large number of partial files. The toolkit includes source maps for the
default and no tag selector versions of the generated CSS for the Alta theme: oj-
alta.css.map, and oj-alta-notag.css.map.
Topics:
• Image Considerations
• Icon Fonts
• Image Files
14-14
Chapter 14
Working with Images
Image Considerations
There are a variety of ways to load icons, such as sprites, data URIs, icon fonts, and
so on. Factors to consider when choosing an image strategy include:
• Themable: Can you use CSS to change the image? Can you replace a single
image easily?
• High contrast mode: Does the image render properly in high contrast mode for
accessibility?
• High resolution support: Does the image look acceptable on high resolution
(retina) displays?
• Browser support: Do you require support for all browsers? Some browsers may
not support certain image formats, such as SVG.
• Image limitations: Are there limitations that impact your use case? For example,
icon fonts are a single color, and small SVG images often do not render well.
• Performance: Is image size a factor? Do you need alternate versions of an image
for different resolutions or states such as disabled, enabled, hover, and active?
Icon Fonts
Oracle JET uses icon fonts whenever possible because icon fonts have certain
advantages over other formats.
• Themable: You can use style classes to change their color instead of having to
replace the image, making them very easy to theme.
• High contrast mode: Icon fonts are optimal for high contrast mode as they are
considered text. However, keep in mind that you can't rely on color in high contrast
mode, and you may need to indicate state (active, hover, and so on) using another
visual indicator. For example, you can add a border or change the icon font's size.
For additional information about Oracle JET and high contrast mode, see
Configuring High Contrast Mode.
• High resolution: Icon fonts look good on a high resolution (retina) display without
providing alternate icons.
• Performance: You can change icon font colors using CSS so alternate icons are
not required to indicate state changes. Alternate images are also not required for
high resolution displays.
Icon fonts also have disadvantages. It can be difficult to replace a single image, and
they only show one color. You can use text shadows to provide some depth to the icon
font.
Oracle JET supports two generic classes for setting the icon font colors in
the $iconColorDefault, $iconColorHover, $iconColorActive, $iconColorSelected,
and $iconColorDisabled variables.
• oj-clickable-icon
• oj-clickable-icon-nocontext
These classes, when used in conjunction with an anchor tag and/or marker classes
like oj-default, oj-hover, oj-focus, oj-active, oj-selected, and oj-disabled, will use
the $iconColor* variables.
14-15
Chapter 14
Working with Images
<div class="oj-default">
<span class="oj-clickable-icon demo-icon-font demo-icon-gear"></span>
</div>
<a href="http://www.oracle.com">
<span class="oj-clickable-icon demo-icon-font demo-icon-gear"></span>
</a>
For an example that illustrates icon font classes on a link, see Icon Fonts.
Image Files
Oracle JET uses images when icon fonts can't be used. Informational images must
appear in high contrast mode for accessibility, as described in Configuring High
Contrast Mode.
To improve performance, the SVG images used by JET components are sprited. JET
does not support creating custom image sprites, and you must use a third-party tool to
create them. However, if you use Oracle JET tooling, you can overwrite some of the
default SVG images and use the tooling to combine them into a single sprite.svg file.
You can replace any of the SVG images in the following application directories. Note
that your new image must use the same name as the original image.
themes/alta/android/images
themes/alta/common/images
themes/alta/ios/images
themes/alta/web/images
themes/alta/windows/images
When you build your application using ojet build or ojet serve commands, JET will
automatically combine the SVG images into the sprite.svg file in the appropriate
themes/alta/*/images/sprites/ directory.
You can also use custom images that are not sprited using CSS, but the improved
performance gained by using sprited images will be lost. To do so, you must override
the image style classes with {background-position: 0% 0%;}.
Oracle JET provides Sass mixins that you can use to create CSS for your own icons.
For examples, see CSS Images.
14-16
15
Securing Applications
Oracle JET follows security best practices for Oracle JET components and provides
the oj.OAuth class to help you manage access to users' private data.
Topics:
• Typical Workflow for Securing Oracle JET Applications
• About Securing Oracle JET Applications
• Using oj.OAuth in Your Oracle JET Application
• About Securing Hybrid Mobile Applications
• Dealing With Cross-Origin Resource Sharing (CORS)
Topics:
• Oracle JET Components and Security
• Oracle JET Security and Developer Responsibilities
• Oracle JET Security Features
• Oracle JET Secure Response Headers
15-1
Chapter 15
About Securing Oracle JET Applications
Oracle JET includes components that follow best practices for security and provides
the oj.OAuth plugin for providing secure access to a user's private data. However, the
application developer is expected to perform tasks that are not included in Oracle JET.
15-2
Chapter 15
About Securing Oracle JET Applications
• Authorization server: Server that issues access tokens to the client after it
successfully authenticates the resource owner and obtains authorization.
Note:
The authorization server can be the same server as the resource server. In
addition, an authorization server can issue access tokens accepted by
multiple resource servers.
OAuth 2.0 Request for Comments (RFC) 6749 describes the interaction between the
four roles as an abstract flow.
1. The client requests authorization from the resource owner, either directly or
through the authorization server.
Note:
The RFC specifies that the authorization server is preferred.
15-3
Chapter 15
About Securing Oracle JET Applications
6. The resource server validates the access token and serves the request if
validated.
The access token is a unique identifier issued by the server and used by the client to
associate authenticated requests with the resource owner whose authorization is
requested or has been obtained by the client.
The Oracle JET oj.OAuth plugin provides functions for the following tasks:
15-4
Chapter 15
About Securing Oracle JET Applications
15-5
Chapter 15
About Securing Oracle JET Applications
Note:
Internet Explorer 11 supports only the sandbox attribute and uses x-content-
security-policy header instead of the standard content-security-policy
header.
Alternatively, you can also use the HTML meta tags to configure CSP. For example:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src
https://*; frame-src 'none';">
Note:
Some of the CSP directives do not work with the HTML meta tags, for
example frame-ancestors.
15-6
Chapter 15
About Securing Oracle JET Applications
The below table describes the out-of-box settings required by a JET web application to
run in its most secure mode without changing the JET functionality. The JET web
application may need to modify these settings for additional resource origins. The table
lists the different response header directives that can be used while enabling CSP
based on the two following scenarios:
• Co-Hosted: When the JET and the Application source codes are hosted on the
same server
• Content Delivery Network (CDN): When the JET code is from the JET CDN and
the Application code is from a different server
15-7
Chapter 15
About Securing Oracle JET Applications
Note:
When default-src is set to none, you must explicitly enable all the other
needed settings for specific resource origins.
15-8
Chapter 15
Using oj.OAuth in Your Oracle JET Application
The following example shows how to set up CSP if a website administrator wants to
allow content from a trusted domain and all its subdomains:
Content-Security-Policy: default-src 'self' *.trusted.com
The following example shows how to set up CSP if a website administrator wants to
allow users of a web application to include images from any origin in their own content,
but to restrict audio or video media to trusted providers, and all scripts only to a
specific server that hosts trusted code.
Content-Security-Policy: default-src 'self'; img-src *; media-src media1.com
media2.com; script-src userscripts.example.com
Topics:
• Initializing oj.OAuth
• Verifying oj.OAuth Initialization
• Obtaining the OAuth Header
• Using oj.OAuth with Oracle JET Common Model
• Integrating oj.OAuth with Oracle Identity Management (iDM) Server
Initializing oj.OAuth
You can create an instance of a specific oj.OAuth object using the oj.OAuth
constructor:
new OAuth(header, attributes)
The code sample below shows three examples for initializing oj.OAuth.
15-9
Chapter 15
Using oj.OAuth in Your Oracle JET Application
If you choose to initialize oj.OAuth manually, you can add the client credentials or
access/bearer token using methods shown in the following code sample.
// Initializing client credentials manually
myOAuth.setAccessTokenRequest({...Client Credentials ...});
myOAuth.clientCredentialGrant();
The OAuth API also includes methods for getting and cleaning the client credentials or
access tokens. For additional information, see the oj.OAuth API documentation.
// Access token
var myOAuth = new oj.OAuth('New-Header', {...Access/Bearer token...});
var myHeaders = myOAuth.getHeader();
Topics:
• Embedding oj.OAuth in Your Application's ViewModel
15-10
Chapter 15
Using oj.OAuth in Your Oracle JET Application
To embed the oj.OAuth object in your ViewModel and initialize it with a bearer/access
token:
function viewModel() {
var self = this;
...
self.myOAuth = new oj.OAuth('X-Authorization', {...Access/Bearer token...});
15-11
Chapter 15
Using oj.OAuth in Your Oracle JET Application
15-12
Chapter 15
About Securing Hybrid Mobile Applications
access_token. To supply the custom header, you must rewrite the OAuth header for
specific Authorization using the getHeader() method.
The code except below shows an example that adds the oj.OAuth object with a
modified header to the application's viewModel.
function viewModel() {
var self = this;
self.bearer = {
access_token: ...,
token_type: "Bearer",
expires_in: ...,
...
}
...
self.myOAuth = new oj.OAuth();
// Rewrite oAuth header for specific Authorization
self.myOAuth.getHeader = function() {
var headers = {};
headers['X-Authorization']=self.bearer.access_token;
return headers;
}
var idmModel = oj.Model.extend({... });
var myIDM = new idmModel();
...
var idmCollection = oj.Collection.extend ({
model: myIDM, oauth: self.myOAuth,
// using embedded feature
...
});
self.myIDMCol = new idmCollection();
...
self.myIDMCol.fetch({
success: function(collection, response, options) {
...
},
error: function(jqXHR, textStatus, errorThrown) {
...
// process errors or insert new access_token and re-fetch
}
});
}
15-13
Chapter 15
Dealing With Cross-Origin Resource Sharing (CORS)
For more information about using this plugin in your hybrid mobile app, see https://
github.com/oracle/cordova-plugin-oracle-idm-auth. This plugin can be used in any
Cordova-based hybrid mobile app, not just JET hybrid mobile apps.
15-14
Chapter 15
Dealing With Cross-Origin Resource Sharing (CORS)
Server-side administrators can specify the origins allowed to access their resources by
modifying the policy used by their remote server to allow cross-site requests from
trusted clients. For example, to access a remote service managed by Oracle’s Mobile
Cloud Service (MCS), an MCS administrator configures MCS’s Security_AllowOrigin
environment policy with a comma-separated list of URL patterns that identify the
remote services that serve resources from different domains.
If you serve your web or hybrid mobile application to the local browser for testing, you
may encounter CORS rejections. Some browsers provide options to disable CORS,
such as Chrome's --disable-web-security and Firefox's
security.fileuri.strict_origin_policy and some browsers support plugins that work
around CORS.
Only use these options when testing your application and ensure that you complete
further testing in a production-like environment without these options to be sure that
your application will not encounter CORS issues in production.
The default web views used by hybrid mobile applications do not implement CORS
and therefore hybrid mobile applications will not encounter CORS issues when run on
a device in the default web view. However, if you use an alternative web view, such as
WKWebView on iOS, you may encounter CORS issues. To work around this on iOS,
consider using the cordova-plugin-wkwebview-file-xhr plugin in your hybrid mobile
application as an alternative to WKWebView. For additional information, see Use a
Different Web View in your JET Hybrid Mobile App.
15-15
16
Configuring Data Cache and Offline
Support
Use the Oracle Offline Persistence Toolkit to enable data caching and offline support
within your Oracle JET application.
Topics
• About the Oracle Offline Persistence Toolkit
• Installing the Offline Persistence Toolkit
16-1
Chapter 16
Installing the Offline Persistence Toolkit
1. Change to your application’s top level directory and open a terminal window.
2. At the terminal prompt, enter the following command to install the toolkit: npm
install @oracle/offline-persistence-toolkit --save.
"offline-persistence-toolkit": {
"cdn": "",
"cwd": "node_modules/@oracle/offline-persistence-toolkit/dist",
"debug": {
"cwd": "debug",
"src": ["**"],
"path": "libs/offline-persistence-toolkit/debug",
"cdn": ""
},
"release": {
"cwd": "min",
16-2
Chapter 16
Installing the Offline Persistence Toolkit
"src": ["**"],
"path": "libs/offline-persistence-toolkit/min",
"cdn": ""
}
},
For information about using the toolkit once you have it installed in your Oracle JET
application, see the README.md and Wiki for the persistence toolkit on Github at https://
github.com/oracle/offline-persistence-toolkit.
16-3
17
Optimizing Performance
Oracle JET applications are client-side HTML5 applications. Most performance
optimization recommendations relating to client-side HTML applications also apply to
applications developed using Oracle JET or to Oracle JET components. In addition,
some Oracle JET components have performance recommendations that are specific
to the component.
Topics:
• Typical Workflow for Optimizing Performance of Oracle JET Applications
• About Performance and Oracle JET Applications
• Adding Performance Optimization to an Oracle JET Application
• Configuring the Application for Oracle CDN Optimization
• Understanding the Path Mapping Script File and Configuration Options
17-1
Chapter 17
Adding Performance Optimization to an Oracle JET Application
For additional information about using the RequireJS bootstrap file in your
Oracle JET application, see Use RequireJS to Manage Library, Link, and Script
References.
Minimize the number of trips to Oracle JET doesn't provide support for minimizing the number of trips, but
retrieve the JavaScript. RequireJS has an optimization tool that you can use to combine modules. For
additional detail, see r.js.
Use lazy loading for JavaScript You can lazy load content that is not needed on first render. For example, you
not needed on first render. can configure the oj-film-strip component to retrieve child node data only
when requested. For an example, see the Lazy Loading (oj-film-strip) Oracle
JET Cookbook example.
17-2
Chapter 17
Adding Performance Optimization to an Oracle JET Application
17-3
Chapter 17
Adding Performance Optimization to an Oracle JET Application
Image Optimization
17-4
Chapter 17
Configuring the Application for Oracle CDN Optimization
For additional performance tips, see the Google Developers documentation at: https://
developers.google.com/speed/docs/best-practices/rules_intro.
17-5
Chapter 17
Configuring the Application for Oracle CDN Optimization
• If you configure the path mappings to use CDN bundle loading, the tooling updates
the index.html file to execute the bundles configuration script file (bundles-
config.js) from the following script reference:
<body>
<script type="text/javascript" src="https://static.oracle.com/cdn/jet/v6.0.0/
default/js/bundles-config.js"></script>
..
</body>
The bundles configuration file specifies its own require block that the application
executes to load as a set of bundled modules and libraries from CDN. When you
configure the application this way, the main.js is updated by the tooling to display only
a URL list composed of third-party libraries. In the bundles configuration scenario, the
injected require block in main.js becomes a placeholder for any application-specific
libraries that you want to add to the list. Where URL duplications may occur between
the require block of the bundles configuration file and the application main.js, the
bundles configuration takes precedence to ensure bundle loading from CDN has the
priority.
Tip:
Configuring your application to reference the bundles configuration script file
on Oracle CDN is recommended because Oracle maintains the configuration
for each release. By pointing your application to the current bundles
configuration, you will ensure that your application runs with the latest
supported library and module versions.
To configure bundle loading of the libraries and modules using the bundles
configuration script, perform the following steps.
1. Open the path_mapping.json file in the js subfolder of your application and change
the use element to cdn:
"use": "cdn"
2. Leave the cdns element unchanged. It should show the following as the default
path definitions for Oracle JET and third-party libraries:
"cdns": {
"jet": {
"prefix": "https://static.oracle.com/cdn/jet/v6.0.0/default/js",
"config": "bundles-config.js"
},
"3rdparty": "https://static.oracle.com/cdn/jet/v6.0.0/3rdparty"
},
3. Optionally, in the case of bundle loading, for each third-party library, update the
cdn element from 3rdparty to jet. For example, this knockout library path definition
shows "cdn": "jet" to prevent the knockout URL from being injected into main.js:
"libs": {
"knockout": {
"cdn": "jet",
"cwd": "node_modules/knockout/build/output",
...
},
17-6
Chapter 17
Understanding the Path Mapping Script File and Configuration Options
Note: Setting "cdn": "jet" for each third-party library prevents these librarys’ URL
from being injected into the require block of main.js. This update is not necessary
to ensure bundle loading of third-party libraries since the bundles configuration
script overrides duplicate URL paths that appear in main.js.
4. Save the file and either build or serve application to complete the bundle loading
configuration.
To configure individual loading of the libraries and modules based on the require block
of the main.js (without the use of the bundles configuration script), perform the
following steps.
1. Open the path_mapping.json file in the js subfolder of your application and change
the use element to cdn.
"use": "cdn"
2. Edit the cdns element to remove the config element so cdns path definitions for
Oracle JET and third-party libraries are formatted as following:
"cdns": {
"jet": "https://static.oracle.com/cdn/jet/v6.0.0/default/js",
"3rdparty": "https://static.oracle.com/cdn/jet/v6.0.0/3rdparty"
}
3. Save the file and either build or serve application to complete the main.js require
block configuration.
Note:
When you need to make a change to the list of required libraries, for
example, to specify a different release version, do not edit the main.js; edit
instead the path_mapping.json file and, in the case of bundle loading, also edit
the bundles-config.js URL in your application’s index.html file. You will need
to rebuild the application to apply the changes. For details about the
path_mapping.json file and the configuration updates performed by the
tooling, see Understanding the Path Mapping Script File and Configuration
Options.
The path mapping use element, set to local by default, specifies location of these
require libraries:
• Core Oracle JET libraries (appear as ojs, ojL10n, ojtranslations)
• Third-party dependency libraries (for example, knockout, jquery, hammerjs, and
other)
When you build your application, by default, Oracle JET will load the libraries from the
local application as specified in the require block of the application's main.js. Each
17-7
Chapter 17
Understanding the Path Mapping Script File and Configuration Options
library URL is assembled from the baseUrl element and the path attribute of the lib
element as specified by the path mapping file.
For example, the path mapping definition for the knockout library shows the following
details.
"baseUrl": "js"
"use": "local"
"libs": {
...
"path": "libs/knockout/knockout-3.4.2.debug"
}
And, after build or serve, the main.js require block contains the following URI:
/js/libs/knockout/knockout-3.4.2.debug
When configured for Oracle Content Delivery Network (CDN), the main.js require
block is determined either entirely by the path mapping file local to the application or,
in the case of the bundle loading optimization, partially from the path mapping file and
partially from the require block of the bundles-config.js file maintained by Oracle on
Oracle CDN. Path injector markers in main.js indicate where the release specific URLs
appear.
CDN Scenario 1: To load libraries and modules as bundles from CDN, by default only
the path mappings for third-party libraries will appear in the URL library list in the
require block of main.js.
For example, the path mapping definition for the knockout library shows the following
details. Note that the config attribute specifies the name of the bundles configuration
script file as bundles-config.js.
"baseUrl": "js" <==ignored
"use": "cdn"
"cdns": {
"jet": {
"prefix": "https://static.oracle.com/cdn/jet/v6.0.0/default/js",
"config": "bundles-config.js"
},
"3rdparty": "https://static.oracle.com/cdn/jet/v6.0.0/3rdparty"
},
"libs": {
...
"cdnPath": "knockout/knockout-3.4.2"
}
And, after build or serve, the main.js require block contains a list of third-party library
URLs as a placeholder, and, as the following code snippet shows, the index.html file
references the script to load libraries and modules as bundles from CDN.
<body>
<script type="text/javascript" src="https://static.oracle.com/cdn/jet/v6.0.0/
default/js/bundles-config.js"></script>
..
</body>
17-8
Chapter 17
Understanding the Path Mapping Script File and Configuration Options
Note:
Loading libraries and module as specified in the require block of the bundles-
config.js file takes precedence over any duplicate libraries that may appear
in the main.js require block. However, if you prefer, you can configure the
third-party library path mapping so their URLs do not appear in the main.js
require block. To accomplish this, set "cdn": "3rdparty" in the
path_mapping.json file to show "cdn": "jet" for each third-party library path
definition.
CDN Scenario 2: To load the libraries individually from CDN using the path mapping
URLs to specify the location, the list of library URLs will appear entirely in the require
block of main.js.
For example, the path mapping definition for the knockout library shows the following
details after you edit the cdns element to remove the bundles configuration script
reference.
"baseUrl": "js" <==ignored
"use": "cdn"
"cdns": {
"jet": "https://static.oracle.com/cdn/jet/v6.0.0/default/js",
"3rdparty": "https://static.oracle.com/cdn/jet/v6.0.0/3rdparty"
}
"libs": {
...
"cdnPath": "knockout/knockout-3.4.2"
}
And, after build or serve, the main.js require block contains the following URL (along
with the URLs for all other base libraries and modules):
"knockout":"https://static.oracle.com/cdn/jet/v6.0.0/3rdparty/knockout/
knockout-3.4.2"
CDN Scenario 3: If your application needs to access libraries that reside on a non-
Oracle CDN, you can update the path-mapping.js file to specify your own CDN
endpoint and library definition.
Depending on whether you use the bundles configuration script, add your CDN name
and endpoint URI to the cdns definition as follows.
When using the bundles configuration script to load libraries and modules:
"cdns": {
"jet": {
"prefix": "https://static.oracle.com/cdn/jet/v6.0.0/default/js",
"config": "bundles-config.js"
},
"3rdparty": "https://static.oracle.com/cdn/jet/v6.0.0/3rdparty"
"yourCDN": "endPoint to your own CDN"
},
...
17-9
Chapter 17
Understanding the Path Mapping Script File and Configuration Options
Or, when loading libraries and modules individually (not using the bundles
configuration script):
"cdns": {
"jet": "https://static.oracle.com/cdn/jet/v6.0.0/default/js",
"3rdparty": "https://static.oracle.com/cdn/jet/v6.0.0/3rdparty",
"yourCDN": "endPoint to your own CDN"
},
...
Then, in the list of libraries, define your library entry similar to the following sample.
"yourLib": {
"cdn": "yourCDN",
"cwd": "node_modules/yourLib",
"debug": {
"src": "yourLib.js",
"path": "libs/yourLib/yourLib.js",
"cdnPath": "yourLib/yourLib.js"
},
"release": {
"src": "yourLib.min.js",
"path": "libs/yourLib/yourLib.min.js",
"cdnPath": "yourLib/yourLib.min.js"
}
},
17-10
18
Testing and Debugging
Test and debug Oracle JET web applications using your favorite testing and
debugging tools for client-side JavaScript applications. You can use a similar process
to debug hybrid mobile applications when run in the browser, and you can also test
and debug hybrid mobile applications on a simulator, emulator, or device.
Topics:
• Typical Workflow for Testing and Debugging an Oracle JET Application
• Testing Oracle JET Applications
• Debugging Oracle JET Applications
Topics:
• Testing Applications
• Testing Hybrid Mobile Applications
• Using oj.BusyContext API in Automated Testing
18-1
Chapter 18
Testing Oracle JET Applications
Testing Applications
You can use virtually any testing tool that tests client-side HTML applications written in
JavaScript for testing Oracle JET applications.
For internal development, Oracle JET uses the following tools for testing Oracle JET
components and toolkit features:
• QUnit: JavaScript unit testing framework capable of testing any generic JavaScript
project and used by the jQuery, jQuery UI, and jQuery Mobile projects.
QUnit requires configuration on your test page to include library and CSS
references. You must also add the HTML div element to your page. In the
example below, the highlighted code shows additions required by QUnit. The
actual paths will vary, depending upon where you install QUnit.
<!doctype html>
<html lang="en">
<head>
<link rel="stylesheet" href="../../../../code/css/libs/oj/v6.0.0/alta/oj-
alta-min.css"></link>
<link rel="stylesheet" href="../../../css/qunit.css">
<script type="text/javascript" src="../../../js/qunit.js"></script>
<script>
QUnit.config.autoload = false;
QUnit.config.autostart = false;
</script>
<script data-main="js/main" src="../../../../code/js/libs/require/
require.js"></script>
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture">
<oj-slider id="slider1"></oj-slider>
</div>
</body>
</html>
18-2
Chapter 18
Testing Oracle JET Applications
Depending upon your use case, you may need to add Cordova plugins to add
functionality to your mobile hybrid app. Many Cordova plugins provide mock data
when deploying to the browser platform. If, however, you add a Cordova plugin
that doesn’t have browser platform support, you can add objects that represent
mock data to cordovaMock.js in src/js.
• Testing in an emulator or simulator
You can invoke ojet serve with the --emulator option to test the functionality of
your application in the iOS Simulator, Windows emulator, or Android Virtual
Devices (AVDs) using the Android emulator. These methods can approximate the
look and feel of an actual device, but you won’t be able to test performance or
responsiveness to touch reliably. For additional information, see
– iOS Simulator
– Android Emulator
– Windows Emulator
• Testing on attached physical devices
You can invoke ojet serve with the destination=device option to test the
functionality on attached physical devices. This provides the most reliable form of
testing, but you may not have access to all the devices that your users might use
to run your application.
If you want to serve your application to an iOS device, you must take additional
steps as described in Packaging and Publishing Hybrid Mobile Applications.
• Working around cross-origin resource sharing (CORS) issues
Hybrid mobile applications that communicate with remote services may encounter
issues if they request resources that originate in difference domains. An example
includes a response such as the following:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
To work around these types of issue, your server-side administrator may need to
modify the policy used by the remote server to allow cross-site requests. For
example, to access a remote service managed by Oracle’s Mobile Cloud Service
(MCS), an MCS administrator configures MCS’s Security_AllowOrigin environment
policy with a comma separated list of URL patterns that identify the remote
services that serve a resource from different domains.
For additional information about testing hybrid mobile applications on specific
platforms, see the following resources:
• Android Testing
• iOS Testing
• Windows Debugging, testing, and performance
18-3
Chapter 18
Testing Oracle JET Applications
when you want to wait for an animation to complete, a JET page to load, or a data
fetch to complete.
Wait Scenarios
The Busy Context API will block until all the busy states resolve or a timeout period
lapses. There are four primary wait scenarios:
• Components that implement animation effects
• Components that fetch data from a REST endpoint
• Pages that load bootstrap files, such as the Oracle JET libraries loaded with
RequireJS
• Customer-defined scenarios that are not limited to Oracle JET, such as blocking
conditions associated with application domain logic
18-4
Chapter 18
Testing Oracle JET Applications
// The assumption with the page when ready script is that it will continue to
execute until a value is returned or
// reached the timeout period.
//
// There are three areas of concern:
// 1) Has the application opt'd in on the whenReady wait for bootstrap?
// 2) If the application has opt'd in on the jet whenReady strategy for bootstrap
"('oj_whenReady' in window)",
// wait until jet core is loaded and have a ready state.
// 3) If not opt-ing in on jet whenReady bootstrap, make the is ready check if jet
core has loaded. If jet core is
// not loaded, we assume it is not a jet page.
// Assumption is we must wait until jet core is loaded and the busy state is ready.
static private final String _WHEN_READY_WITH_BOOTSTRAP_EXP =
"(window['oj'] && window['oj']['Context'] &&
oj.Context.getPageContext().getBusyContext().isReady() ?" +
" 'ready' : '')";
// Assumption is the jet libraries have already been loaded. If they have not, it's
not a Jet page.
// Return jet missing in action "JetMIA" if jet core is not loaded.
static private final String _WHEN_READY_NO_BOOTSTRAP_EXP =
"(window['oj'] && window['oj']['Context'] ? " +
"(oj.Context.getPageContext().getBusyContext().isReady() ? 'ready' : '') :
'JetMIA')";
18-5
Chapter 18
Testing Oracle JET Applications
The following example shows how you can use whenReady() with QUnit.
// Utility function for creating a promise error handler
function getExceptionHandler(assert, done, busyContext)
{
return function (reason)
{
if (reason && reason['busyStates'])
{
// whenReady timeout
assert.ok(false, reason.toString());
}
else
{
// Unhandled JS Exception
var msg = reason ? reason.toString() : "Unknown Reason";
if (busyContext)
msg += "\n" + busyContext;
assert.ok(false, msg);
}
{
// default whenReady timeout used when argument is not provided
oj.Context.setBusyContextDefaultTimeout(18000);
popup.open("#showPopup1");
busyContext.whenReady().then(function ()
{
assert.ok(popup.isOpen(), "popup is open");
popup.close();
busyContext.whenReady().then(function ()
{
done();
}).catch(errorHandler);
}).catch(errorHandler);
});
18-6
Chapter 18
Debugging Oracle JET Applications
Note:
Busy Context dependency relationships are determined at the point the
first busy state is added. If the DOM node is re-parented after a busy
context was added, the context will maintain dependencies with any
parent DOM contexts.
3. Perform the operation that needs to be guarded with a busy state. These are
usually asynchronous operations that some other application flow depends on its
completion.
4. Resolve the busy state when the operation completes.
Important:
The application is responsible for releasing the busy state. The application
must manage a reference to the resolve function associated with a busy
state, and it must be called to release the busy state. If the DOM node that
the busy context is applied to is removed in the document before the busy
state is resolved, the busy state will be orphaned and will never resolve.
Topics:
• Debugging Web Applications
• Debugging Hybrid Mobile Applications
18-7
Chapter 18
Debugging Oracle JET Applications
Before you debug your application, you should verify that your application is using the
debug version of the Oracle JET libraries. If you used the tooling to build your
application in development mode, then your application should be using the debug
library. If, however, you are using one of the sample applications, it may be configured
to use the minified version of the Oracle JET libraries.
To verify that you are using the debug version of the library, check your RequireJS
bootstrap file (typically apphome/js/main.js) for the Oracle JET library path, highlighted
in bold below.
requirejs.config(
{
baseUrl: 'js',
'ojs': 'libs/oj/v6.0.0/min',
'ojL10n': 'libs/oj/v6.0.0/ojL10n',
'ojtranslations': 'libs/oj/v6.0.0/resources',
'text': 'libs/require/text',
'signals': 'libs/js-signals/signals'
}
... contents omitted
);
In this example, the path is pointing to the minified version of the Oracle JET libraries.
To change to the debug version, edit the bootstrap file and replace min with debug as
shown.
'ojs': 'libs/oj/v6.0.0/debug',
Debugging facilities vary by browser. For example, with Google Chrome you can:
• do normal debugging, including setting breakpoints and inspecting CSS using
Chrome Inspector (<a target="_blank" href="chrome://inspect">chrome://
inspect</a>) without having to install any 3rd party add-ons.
• add the Knockoutjs Context Debugger extension to examine the Knockout context
and data for a DOM node in a sidebar.
• add the Window Resizer extension to resize the browser window to emulate
common screen sizes or set your own. You can find the extensions and other
developer tools at the Chrome Web Store.
If you’re using an Integrated Development Environment (IDE) for development, you
can use debugging tools such as the one provided by the NetBeans IDE to set break
points and watches. For details, see Debugging and Testing JavaScript in an HTML5
Application.
18-8
Chapter 18
Debugging Oracle JET Applications
Once your app loads in the browser, you can use the browser’s developer tools to
view the source code, debug JavaScript using breakpoints and watches, or
change the source code in the browser, which can be especially useful when you
want to experiment with CSS changes that you intend to make in your app. For
more information about the Chrome browser’s developer tools, see the Chrome
DevTools documentation and for the iOS Safari browser’s developer tools, see the
Web Inspector Tutorial.
• Debugging in the emulator
You can run the app in the emulator which eliminates the need for an actual
device. However, emulators can run slowly and include limited support for
emulating native device services.
To run an Oracle JET hybrid mobile application in the default emulator, serve it
with the --emulator option.
ojet serve ios|android|windows --emulator
You can also specify a named emulator or simulator on Android or iOS devices:
ojet serve ios|android|windows --emulator=emulator-name
18-9
Chapter 18
Debugging Oracle JET Applications
Use the Chrome browser’s developer tools if your app runs on an Android emulator or
device. For the Android platform, once JET has served the app to the emulator or
device, enter chrome://inspect/ in the Chrome browser’s address bar to access the
developer tools. This renders the Inspect with Chrome Developer Tools page that lists
the connected devices and the applications on these devices that you can inspect
using the developer tools. Click the inspect link beside the app you want to inspect, as
illustrated by the following figure which shows the developer tools in the foreground for
the application running in the emulator.
For more information about debugging apps on an Android device using Chrome
DevTools, see Get Started with Remote Debugging Android Devices in Google’s
documentation.
Use Visual Studio to debug and inspect your application’s code if your application runs
on the Windows platform. Serve your application on your Windows machine and
attach the process for the application to the Visual Studio debugger, as described in
Microsoft’s documentation. JavaScript apps, such as your application, run in an
instance of the wwahost.exe process on Windows, so multiple instances of the
wwahost.exe process appear in the Attach to Process dialog if you have more than one
application running. Use the Title column of the Attach to Process dialog to select the
correct application to attach, as illustrated by the following image where an instance of
the FixItFast sample application is selected.
18-10
Chapter 18
Debugging Oracle JET Applications
Once you have attached your app, use the Visual Studio debugger, as described in
Microsoft’s documentation, to debug your application.
18-11
19
Packaging and Deploying Applications
If you used Oracle JET tooling to create your Oracle JET application, you can package
web applications for deployment to a web or application server and hybrid mobile
applications for deployment to the Google Play or Apple App stores.
Topics
• Typical Workflow for Packaging and Deploying Applications
• Packaging and Deploying Web Applications
• Packaging and Publishing Hybrid Mobile Applications
• Removing and Restoring Non-Source Files from Your JET Application
Topics
• Packaging Web Applications
• Deploying Web Applications
19-1
Chapter 19
Packaging and Publishing Hybrid Mobile Applications
2. To verify that the application still works as you expect, run ojet serve with the
release option.
The ojet serve --release command takes the same arguments that you used to
serve your web application in development mode.
ojet serve --release [--serverPort=server-port-number --serverOnly]
Tip:
For a complete list of options, type ojet help serve at the terminal
prompt.
Topics
• About Packaging and Publishing Hybrid Mobile Applications
• Packaging a Hybrid Mobile App on Android
• Packaging a Hybrid Mobile App on iOS
• Packaging a Hybrid Mobile App on Windows
19-2
Chapter 19
Packaging and Publishing Hybrid Mobile Applications
To sign an app that you want to release and publish through the Google Play Store or
some other distribution mechanism, you need a unique key. Your organization may
provide you with one, in which case you specify its location in the build configuration
file that you use to sign and package your app. If you don’t have a key, you can create
one that you use to sign your app. Subsequent updates to your app must be signed
using the same key that you create. You can create a self-signed key using the JDK’s
keytool utility or using the dialogs that Android Studio provides from its Build >
Generate Signed APK menu. For more information about the latter option, see the
“Generate a key and keystore” section in the Sign Your App page on the Android
developer’s site.
Once you have generated your key, specify the location of its store, its alias, and its
access credentials in your build configuration file, as shown by the following example.
{
"android": {
"release": {
"keystore": "/home/pathTo/keystore/android.jks",
"storePassword": "MyKeystorePassword",
"alias": "myAndroidKey",
"password" : "MyKeyPassword",
"keystoreType": ""
}
}
}
You can now package your app into an .APK file (the installation file type for apps on
Android devices).
At a terminal prompt, in your app’s top-level directory, enter the following command:
ojet build android --release --build-config=/pathTo/yourBuildConfig.json
On successful completion, the terminal window displays output similar to the following:
...
...
Oracle JET CLI
19-3
Chapter 19
Packaging and Publishing Hybrid Mobile Applications
Done.
The build command outputs the .APK file that packages your app to the following
location:
/appRootDir/hybrid/platforms/android/build/outputs/apk/android-release.apk.
Use this file to release your app through a public app marketplace, such as Google
Play, a private app store, or some other means. End users who install your app from a
location other than Google Play need to configure their device to opt in to install the
app from an unknown source. For information about all these options, see https://
developer.android.com/studio/publish/index.html#publishing-release.
Joining the Apple Developer Program is the first step to submit your iOS app to the
Apple App Store, to distribute an in-house app, or to sign an app that you distribute
outside the Apple App Store. As a member, you have access to the resources you
need to configure app services and to submit new apps and updates. For more
information on the Apple Developer Program, see Apple Developer Program in Apple’s
documentation.
Code signing your app allows users to trust that your app has been created by a
source known to Apple and that it hasn’t been tampered with. All apps must be code
signed and provisioned to deploy to an iOS device, to use certain Apple services, to be
distributed for testing on iOS devices, or to be uploaded to the Apple App Store.
Signing identities are used to sign your app or installer package. A signing identity
consists of a public-private key pair that Apple issues. The public-private key pair is
stored in your keychain, and is used by cryptographic functions to generate the
signature. A certificate stored in your developer account contains the associated public
key. An intermediate certificate is also required to be in your keychain to ensure that
your certificate is issued by a certificate authority.
Code signing requires that you have both the signing identity and the intermediate
certificate installed in your keychain. When you install Xcode, Apple’s intermediate
certificates are added to your keychain for you.
19-4
Chapter 19
Packaging and Publishing Hybrid Mobile Applications
You can now package your app into an .IPA file (the installation file type for apps on
iOS devices).
At a terminal prompt, in your app’s top-level directory, enter the following command:
ojet build ios --device --release --build-config=/pathTo/yourBuildConfig.json
19-5
Chapter 19
Packaging and Publishing Hybrid Mobile Applications
On successful completion, the terminal window displays output similar to the following:
...
** ARCHIVE SUCCEEDED **
** EXPORT SUCCEEDED **
Done.
This file can be deployed to an iOS device that matches the installed provisioning
profile using iTunes, or you can release your app through the public marketplace,
Apple App Store, a private app store, or some other means. For information about
submitting your app to the Apple App Store, see Distribute an app through the App
Store in Apple’s documentation.
To complete this task, you need access to a digital certificate (a .PFX file) to sign your
app. You must also create a build configuration file (build.json) where you specify the
location of the digital certificate and a number of other parameters. You use the
build.json file as an input parameter to the ojet build command that JET uses to
package your app for Windows.
How to Create the Build Configuration File to Package Your App on Windows
You need to create a build configuration file in JSON format that passes details, such
as the location of your PFX file, to the ojet build command that JET uses to package
your app for Windows.
The following example build configuration file shows sample entries that you can use
to package an app in debug and release mode.
{
"windows": {
"debug": {
"packageCertificateKeyFile": "C:\\AppRootDir\hybrid\platforms\windows
\CordovaApp_TemporaryKey.pfx"
},
"release": {
"packageCertificateKeyFile": "c:\\path-to-key\\keycert.pfx",
"packageThumbprint": "ABCABCABCABC123123123123",
"publisherId": "CN=Doc Example,OU=JET,O=Oracle,C=US"
}
}
}
19-6
Chapter 19
Packaging and Publishing Hybrid Mobile Applications
Create your build.json file using the previous example as a reference. For information
about creating and installing a PFX file, see Install a Personal Information Exchange
File in Your Computer’s Certificate Store. Obtain the values for the packageThumbprint
and the publisherID entries by running the following command in a PowerShell
command window:
Get-PfxCertificate -FilePath "c:\path-to-key\keycert.pfx"
PowerShell prompts you for the PFX file’s password if the file is password protected.
Output similar to the following then appears:
Thumbprint Subject
---------- -------
702F25BA3FED453A3F8ADCC13900A6353703AB54 CN=Doc Example, OU=JET, O=Oracle, C=US
On successful completion, the following directory contains the application package and
other resources for your app:
AppRootDir\hybrid\platforms\windows\AppPackages\
Submit the application package (.appx) in the directory to the Windows App Store or
distribute the contents to end users with Windows devices who want to install your
app. The directory includes a PowerShell script (Add-AppDevPackage.ps1) that end users
execute to install the application. In addition to the script, the directory contains
dependent packages and the certificate that signed the application. The following
example lists the contents:
Add-AppDevPackage.ps1
Add-AppDevPackage.resources
CordovaApp.Windows10_1.0.0.0_x64.appx
CordovaApp.Windows10_1.0.0.0_x64.cer
For information about how to submit your app to the Windows Store, see https://
developer.microsoft.com/en-us/store/publish-apps.
19-7
Chapter 19
Removing and Restoring Non-Source Files from Your JET Application
ojet clean
Use the ojet clean command to clean the build output of your JET application. Specify
the appropriate parameter with the ojet clean command to clean the build output on
the platform(s) that your JET application supports (android, ios, windows, and web). This
command can be useful when developing a hybrid mobile application that makes use
of staging files as you can make sure that all staging files are removed between builds.
These staging files will be regenerated the next time that you build or serve the hybrid
mobile application. Run the following command in your application’s top level directory
to clean the output of your application that targets the Android platform:
ojet clean android
Similarly, run ojet clean web to remove the contents of your application’s root
directory’s web directory.
ojet strip
Use ojet strip when you want to remove all non-source files from your JET
application. In addition to the build output removed by the ojet clean command, ojet
strip removes additional dependencies, such as Cordova plugins, and npm modules
installed into your project. A typical usage scenario for the ojet strip command is
when you want to distribute the source files of your JET application to a colleague and
you want to reduce the number of files to transmit to the minimum.
ojet restore
Use the ojet restore command to restore the dependencies, plugins, libraries, and
Web Components that the ojet strip command removes. After the ojet restore
command completes, use the ojet build and/or ojet serve commands to build and
serve your JET application.
For additional help with CLI commands, enter ojet help at a terminal prompt.
19-8
A
Troubleshooting
Follow the same procedure for troubleshooting your Oracle JET application that you
would follow for any client-side JavaScript application.
If you're having issues troubleshooting a specific Oracle JET component or toolkit
feature, see Oracle JET Support. Before requesting support, be sure to check the
product Release Notes.
A-1
B
Oracle JET v6.0.0 Tooling Migration
If you used Oracle JET tooling to scaffold your application with Oracle JET v5.x.0 , you
can migrate your application manually to v6.0.0.
Before you migrate your application, be sure to check the Oracle JET Release Notes
for any component, framework, or other change that could impact your application.
Important:
This process is not supported for Oracle JET releases prior to v5.0.0.
Beginning with 5.0.0, you no longer need to edit multiple files when adding new
libraries, and you no longer need to update paths manually in src/js/main.js or
scripts/config/oraclejet-build.js.
1. At a terminal prompt, enter the following command to clean the npm cache:
npm cache clean
2. Remove the existing version of the ojet-cli tooling package and install the latest
version.
As Administrator on Windows or using sudo as needed on Macintosh and Linux
systems, enter the following command in a terminal window:
[sudo] npm install -g @oracle/ojet-cli@~6.0.0
3. Enter the following commands to change to the application’s top level directory
and upgrade local npm dependencies:
cd appDir
npm uninstall @oracle/oraclejet @oracle/oraclejet-tooling
npm install @oracle/oraclejet@~6.0.0 @oracle/oraclejet-tooling@~6.0.0 --save
4. Review the Theming section in the release notes for SCSS variable or other
changes, and update your theming files manually, if needed.
5. In the application’s src directory, edit index.html and replace the existing css
reference with the v6.0.0 version.
<link rel="stylesheet" href="css/libs/oj/v6.0.0/alta/oj-alta-min.css" id="css" />
6. Update the Oracle JET library configuration paths to reference the v6.0.0 version
of Oracle libraries.
a. At a terminal prompt, create a temporary application to obtain the files that you
will copy as needed to your migrating application:
B-1
Appendix B
Migrating a v5.x.0 Application to v6.0.0
"my-library": {
"cdn": "3rdparty",
"cwd": "node_modules/my-library/build/output",
"debug": {
"src": "my-library.debug.js",
"path": "libs/my-library/my-library.debug.js",
"cdnPath": ""
},
"release": {
"src": "my-library.js",
"path": "libs/my-library/my-library.js",
"cdnPath": ""
}
},
...
7. Copy the tempApp/scripts/hooks directory to your migrating application’s scripts/
hooks directory.
8. To test the migration, run ojet build and ojet serve with appropriate options to
build and serve the application.
For a list of available options, enter the following command at a terminal prompt in
your application’s top level directory: ojet help.
If your application uses a custom theme, be sure to include the --theme option to
regenerate the CSS:
ojet build [options] --theme=myTheme
B-2
C
Oracle JET References
Oracle JET includes third-party libraries and tools that are referenced throughout the
guide. Oracle also provides optional tools and libraries to assist with application
development.
Topics:
• Oracle Libraries and Tools
• Third-Party Libraries and Tools
C-1
Appendix C
Third-Party Libraries and Tools
C-2