Handle selection events in an Oracle JET web app
Introduction
You can use the Oracle JavaScript Extension Toolkit (Oracle JET) API in the viewModel of the Oracle JET web app to create event handlers that respond to the Oracle JET List View component selected
attribute change listener. When the user selects or deselects an item in the list, the change listener triggers your event handler. You can use the event handler to populate the Oracle JET data provider, and you can bind the data provider to observables that the view component can use. The event handler can also set the boolean variable that the Oracle JET If Binding component uses to conditionally render its nested HTML elements in the view. If the selected
attribute of the Oracle JET List View component for the master list isn’t empty, then the variable is set to true
, and the view renders the databound detail list. If the selected
attribute is empty due to a deselection event, then the variable is set to false, and the view renders a container in the view without the databound detail list.
Objectives
In this tutorial, you will update the user interface of an Oracle JET web app so that you can display master-detail data. You learn how to create a JavaScript event handler by using an Oracle JET property change listener. You also learn how to use an Oracle JET If Binding component to conditionally display the detail list to handle list selections.
Prerequisites
- A development environment set up to create Oracle JET web apps that includes an installation of Node.js
- Completion of the previous tutorial in this learning path, Create the Detail View in an Oracle JET Web Application
- The completed app jet_web_application_masterdetail_final.zip optionally downloaded
Task 1: Set Selection Behavior in the View
Update the view to customize the List View components to handle a row selection in the Activities list and Activity Items list. The Oracle JET List View component defines the first-selected-item
attribute that the component populates with the data items of the user’s row selection and that you can read into an observable by using a two-way binding. Use the component’s on-selection-changed
attribute to catch and process list selection/deselection events by binding an event listener that you define in the viewModel.
-
Navigate to the
JET_Web_Application/src/ts/views
directory and open thedashboard.html
file in an editor. -
Below the Activities heading, find the
oj-list-view
custom HTML element whereid="activitiesList"
, and then add the selection behavior attributes after thegridlines.item
attribute.<h3 id="activitiesHeader">Activities</h3> <oj-list-view id="activitiesList" class="item-display" aria-labelledby="activitiesHeader" data="[[activityDataProvider]]" gridlines.item="visible" selection-mode="single" selected="{{selectedActivity}}" on-first-selected-item-changed="[[selectedActivityChanged]]" first-selected-item="{{firstSelectedActivity}}" scroll-policy="loadMoreOnScroll" scroll-policy-options.fetch-size="5"> <template slot="itemTemplate"> . . .
-
Below the Activity Items heading, find the
oj-list-view
custom HTML element whereid="itemsList"
, and then add the selection behavior attributes after thegridlines.item
attribute.<h3 id="itemsListHeader">Activity Items</h3> <oj-list-view id="itemsList" class="item-display" data="[[itemsDataProvider]]" aria-labelledby="itemsListHeader" gridlines.item="visible" selection-mode="single" selected="{{selectedItem}}" on-first-selected-item-changed="[[selectedItemChanged]]" first-selected-item="{{firstSelectedItem}}" scroll-policy="loadMoreOnScroll" scroll-policy-options.fetch-size="5"> <template slot="itemTemplate"> . . .
-
Save the
dashboard.html
file.Your file should look similar to event-task1-dashboard-html.txt.
Task 2: Create Event Handlers in the ViewModel
Update the viewModel to add event handlers for the List View components to respond to selections in the Activities list and the Activity Items list. The Oracle JET List View component defines a selected
attribute, on which the Oracle JET List View API defines a property change listener. Your event handler sets the selectedActivity
and selectedItem
observables when the user makes a list selection and the value of the selected
attribute changes.
-
Navigate to the
JET_Web_Application/src/ts/viewModels
directory and open thedashboard.ts
file in an editor. -
Below the
pieSeriesValue
observable definition, before theconstructor()
method, add observables for the Activities List selection and the Activity Items list selection.class DashboardViewModel { . . . pieSeriesValue: ko.ObservableArray; // Observables for Activities selectedActivity = new ObservableKeySet(); activitySelected = ko.observable(false); // Controls display of Activity Items firstSelectedActivity = ko.observable(); selectedActivityIds = ko.observable(); // Observables for Activity Items itemSelected = ko.observable(false); selectedItem = ko.observable(); firstSelectedItem = ko.observable(); constructor() { . . .
-
At the top of the
dashboard.ts
file, import theObservableKeySet
class from theojs/ojknockout-keyset
module and theojListView
class from theojs/ojlistview
module.import * as ko from "knockout"; . . . import "ojs/ojavatar"; import { ObservableKeySet } from "ojs/ojknockout-keyset"; import { ojListView } from "ojs/ojlistview";
-
Before the
DashboardViewModel
class, add theActivityItems
type alias.. . . type ActivityItems = { id: number; name: string; items: Array<Item>; short_desc: string; image: string; }; class DashboardViewModel { . . .
-
After the
DashboardViewModel
classconstructor()
method declaration, add theselectedActivityChanged
event handler with test conditions to handle the selection and deselection events.} // End of constructor function selectedActivityChanged = (event: ojListView.firstSelectedItemChanged<ActivityItems["id"], ActivityItems>) => { /** * If no items are selected then the firstSelectedItem property returns an object * with both key and data properties set to null. */ let itemContext = event.detail.value.data; if (itemContext != null) { // If selection, populate and display list } else { // If deselection, hide list } };
Implement this event handler in the next steps.
-
Within the
if
statement of theselectedActivityChanged
event handler, populate theitemsDataProvider
observable by using theitemsArray
variable, and then set theactivitySelected
anditemSelected
selection state observables totrue
for the selection event.selectedActivityChanged = (event: ojListView.firstSelectedItemChanged<ActivityItems["id"], ActivityItems>) => { /** * If no items are selected, then this property firstSelectedItem * will return an object with both key and data properties set to null. */ let itemContext = event.detail.value.data; if (itemContext != null) { // If selection, populate and display list // Hide currently-selected activity item this.activitySelected(false); let itemsArray = itemContext.items; this.itemsDataProvider.data = itemsArray; // Set List View properties this.activitySelected(true); this.itemSelected(false); this.selectedItem(); this.itemData(); } else { // If deselection, hide list } };
-
Within the
else
statement of theselectedActivityChanged
event handler, set theactivitySelected
anditemSelected
selection state observables tofalse
for the deselection event.selectedActivityChanged = (event: ojListView.firstSelectedItemChanged<ActivityItems["id"], ActivityItems>) => { /** * If no items are selected then this property firstSelectedItem will return an * object with both key and data properties set to null. */ let itemContext = event.detail.value.data; if (itemContext != null) { . . . } else { // If deselection, hide list this.activitySelected(false); this.itemSelected(false); } };
-
After the
selectedActivityChanged
event handler, add aselectedItemChanged
event handler with test conditions to handle the selection and deselection events.selectedActivityChanged = (event: ojListView.firstSelectedItemChanged<ActivityItems["id"], ActivityItems>) => { . . . }; /** * Handle selection from Activity Items list */ selectedItemChanged = (event: ojListView.firstSelectedItemChanged<Item["id"], Item>) => { let isClicked = event.detail.value.data; if (isClicked != null) { // If selection, populate and display list } else { // If deselection, hide list } };
Implement this event handler in the next steps.
-
Within the
if
statement of theselectedItemChanged
event handler, populate theitemData
observable, populate thepieSeriesValue
observable by using thepieSeries
array variable, and then set theitemSelected
selection state observable totrue
for the selection event.selectedItemChanged = (event: ojListView.firstSelectedItemChanged<Item["id"], Item>) => { let isClicked = event.detail.value.data; if (isClicked != null) { // If selection, populate and display list this.itemData(event.detail.value.data); // Create variable and get attributes of the items list to set pie chart values let pieSeries = [ { name: "Quantity in Stock", items: [this.itemData().quantity_instock] }, { name: "Quantity Shipped", items: [this.itemData().quantity_shipped] } ]; // Update the pie chart with the data this.pieSeriesValue(pieSeries); this.itemSelected(true); } else { // If deselection, hide list } };
-
Within the
else
statement of theselectedItemChanged
event handler, set theitemSelected
selection state observable tofalse
for the deselection event.selectedItemChanged = (event: ojListView.firstSelectedItemChanged<Item["id"], Item>) => { if (isClicked != null) { . . . } else { // If deselection, hide list this.itemSelected(false); } };
-
Save the
dashboard.ts
file.Your file should look similar to final-event-dashboard-ts.txt.
Task 3: Conditionalize List Rendering in the View
Update the view using Oracle JET If Binding components to conditionally render the detail list. The Oracle JET If Binding component takes a boolean variable on its test
attribute. Within one If Binding component, nest the Activity Items container and, in another If Binding component, nest the Item Details container. Then use the If Binding components to test the state of the observables activitySelected
and itemSelected
. The nested content of your If Binding component renders if the test condition is true
. Display the databound list if the observable is true
, as set by the list event handler. Use another If Binding component to test if the observable is false
, and then display a container with a message directing the user to make a list selection.
-
Navigate to the
JET_Web_Application/src/ts/views
directory and open thedashboard.html
file in an editor. -
Find the
div
element whereid="parentContainer2"
. Above it, add the opening tag of theoj-bind-if
custom HTML element, with thetest
attribute set to the state of theactivitySelected
observable.. . . </oj-list-view> </div> <oj-bind-if test="[[activitySelected()]]"> <div id="parentContainer2" class="oj-flex oj-flex-item oj-panel oj-bg-danger-30 oj-lg-padding-6x oj-md-8 oj-sm-12"> <div id="activityItemsContainer" class="oj-flex-item oj-md-6 oj-sm-12"> <h3 id="itemsListHeader">Activity Items</h3> . . .
When the user selects an activity from the Activities list, the viewModel Activity Changed event handler sets the value of the
activitySelected
observable totrue
. In this case, theoj-bind-if
test condition is satisfied, and the app renders the Activity Items container for the activity selection. The()
notation on the observable property is the Knockout function convention to obtain the value of the observable instead of obtaining the instance of the observable object. -
Find the
div
element whereid="itemDetailsContainer"
, and above it add the opening tag of theoj-bind-if
custom HTML element, with thetest
attribute set to the state of theitemSelected
observable.. . . </oj-list-view> </div> <oj-bind-if test="[[itemSelected()]]"> <div id="itemDetailsContainer" class="oj-flex-item oj-panel oj-bg-neutral-30 oj-md-6 oj-sm-12"> <h3>Item Details</h3> . . .
When the user selects an item from the Activity Items list, the viewModel Item Changed event handler sets the
itemSelected
observable totrue
. In this case, theoj-bind-if
test condition is satisfied, and the app renders the Item Details container for the activity item selection. -
At the bottom of the
dashboard.html
file, count two closing</div>
tags up, and then add the closing</oj-bind-if>
tag to match the opening<oj-bind-if test ="[[activitySelected()]]">
tag. Count up one more closing</div>
tag, and then add the closing</oj-bind-if>
tag for the opening<oj-bind-if test ="[[itemSelected()]]">
tag.. . . </oj-chart> </div> </div> </oj-bind-if> </div> </oj-bind-if> </div> </div>
-
Below the closing
</oj-bind-if>
tag that you added, closest to the end of the file, insert anoj-bind-if test="[[!activitySelected()]]"
custom HTML element that contains adiv
element with Oracle JEToj-flex-item oj-sm-6
flex layout helper classes.. . . </oj-bind-if> </div> </oj-bind-if> <oj-bind-if test="[[!activitySelected()]]"> <div class="oj-flex-item oj-sm-6"> <p>Select an Activity to see Items</p> </div> </oj-bind-if> </div> </div>
The Oracle JET
oj-sm-6
helper class specifies that the container for theSelect an Activity to see Items
heading occupies six container columns for small and larger screen sizes.Until the user selects an activity, the value of
activitySelected
observable isfalse
. Likewise, if the user presses the Ctrl key and clicks an already selected activity, the viewModel treats this event as a deselection, and the Activity Changed event handler sets theactivitySelected
observable tofalse
. In both cases, theoj-bind-if
test condition is satisfied by the booleanfalse
condition, and the app renders theSelect an Activity to see Items
heading. -
Below the first closing
</oj-bind-if>
tag in the file, add anoj-bind-if test ="[[!itemSelected()]]"
custom HTML element that contains adiv
element with Oracle JEToj-flex-item oj-sm-12 oj-md-6 flex
layout helper classes.. . . </oj-chart> </div> </div> </oj-bind-if> <oj-bind-if test="[[!itemSelected()]]"> <div class="oj-flex-item oj-sm-12 oj-md-6"> <p>Select an Item to see details</p> </div> </oj-bind-if> </div> </oj-bind-if> <oj-bind-if test="[[!activitySelected()]]"> <div class="oj-flex-item oj-sm-6"> <p>Select an Activity to see Items</p> </div> </oj-bind-if> </div> </div>
The Oracle JET
oj-sm-12
andoj-md-6
helper classes specify that the container for theSelect an Item
to see details heading occupies six container columns for medium and larger screen sizes or occupies twelve container columns for small screen sizes.Until the user selects an activity item, the value of the
itemSelected
observable isfalse
. Likewise, if the user presses the Ctrl key and clicks an already selected activity item, the viewModel treats this event as a deselection, and the Item Changed event handler sets theitemSelected
observable tofalse
. In both cases, theoj-bind-if
test condition is satisfied by the booleanfalse
condition, and the app renders theSelect an Item to see details
heading. -
Save the
dashboard.html
file.Your file should look similar to event-task3-dashboard-html.txt.
Task 4: Remove Redundant Style Classes
In earlier tutorials of these learning path series, we added Oracle JET style classes to make a visual contrast between the containers that we added to the web app. Now that we have completed the implementation of the master-detail pattern, we can remove these style classes.
-
Navigate to the
JET_Web_Application/src/ts/views
directory and open thedashboard.html
file in an editor. -
Find and delete the following style classes that are used in the
dashboard.html
file.oj-bg-danger-30 oj-bg-info-30 oj-bg-neutral-30 oj-bg-warning-20 oj-panel
-
Save the
dashboard.html
file.Your file should look similar to final-event-dashboard-html.txt.
Task 5: Test the Master and Detail Lists
-
In the terminal window, change to the
JET_Web_Application
directory and run the web app.npx ojet serve
-
In the web browser, click the Baseball activity in your app.
The Activities list selection triggers the
selectedActivityChanged
event handler. The Activity Items container is rendered for the selected activity. -
In the Activity Items list, click SureCatch Baseball Glove.
The Activity Items list selection triggers the
selectedItemChanged
event handler. The app renders the Item Details container for the selected item. -
In the Activity Items list, press Ctrl and click SureCatch Baseball Glove to deselect it.
The Activity Items list deselection triggers the
selectedItemChanged
event handler. The Item Details container is hidden. -
Resize the browser, or press Ctrl+Shift+I to bring up the Chrome DevTools and select a smaller screen size, such as Pixel 7, from the screen-size emulator.
The containers are arranged according to the screen size.
Description of the illustration resized_master_detail_list.png
-
Close the browser window or tab that displays your running web app.
-
In the terminal window, press Ctrl+C, and if prompted, enter
y
to exit the Oracle JET tooling batch job.
Task 5: (Optional) Run a Web App from a Restored App
If you want to run the completed Oracle JET web app from the supplied code, you can restore the app from the downloaded archive file. To work with a “stripped and zipped” Oracle JET web app, you must restore project dependencies, including Oracle JET tooling and the required libraries and modules, within the extracted app.
-
Download the
jet_web_application_masterdetail_final.zip
file and extract the contents of the completed app to thejet_web_application_masterdetail_final
folder. -
In the terminal window, navigate to the
jet_web_application_masterdetail_final
folder and restore the Oracle JET web app by installing the NPM packages that it requires.npm install
-
Wait for a confirmation message similar to the following.
. . . added 284 packages in 59s
The web app is ready to run.
-
Run the web app and test it in the browser.
npx ojet serve
-
Close the browser window or tab that displays your running web app.
-
In the terminal window, press Ctrl+C, and if prompted, enter
y
to exit the Oracle JET tooling batch job.
Next Step
To proceed to the first tutorial in the next learning path in this series, click here.
More Learning Resources
Explore other labs on docs.oracle.com/learn or access more free learning content on the Oracle Learning YouTube channel. Additionally, visit education.oracle.com/learning-explorer to become an Oracle Learning Explorer.
For product documentation, visit Oracle Help Center.
Handle selection events in an Oracle JET web app
F11923-09
March 2024
Copyright © 2022, 2024, Oracle and/or its affiliates.