Handle selection events in an Oracle JET virtual DOM app

Introduction

In your Oracle JavaScript Extension Toolkit (Oracle JET) virtual DOM app, you can use a combination of resources from Preact, including state, props, and hooks, to create change listeners and event handlers that respond to selection and deselection in Oracle JET List View components. When the user selects an activity or activity item in the app’s master or detail list views, the change listener triggers your event handler, which creates the Oracle JET data provider object that populates the List View components in the Activity and Activity Item containers.

In the parent container components, implement a conditional rendering of the child components so that if certain selected attributes of the Oracle JET List View components are not empty, then a 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 child components render with no detail view.

Objectives

In this tutorial, you will update the user interface of an Oracle JET virtual DOM app so that you can display master-detail data that responds to Oracle JET List View component selection events.

Prerequisites

Task 1: Modify Parent Container 1 for Selection

  1. Navigate to the JET-Virtual-DOM-app/src/components directory and open the ParentContainer1.tsx file in an editor.

  2. Add an import statement for the useState hook to the top of the file.

    import { useState } from "preact/hooks";
    
  3. Create an Item type alias and an INIT_SELECTEDACTIVITY variable.

    type Item = {
      id: number;
      name: string;
      short_desc?: string;
      price?: number;
      quantity?: number;
      quantity_shipped?: number;
      quantity_instock?: number;
      activity_id?: number;
      image?: string;
    };
    
    let INIT_SELECTEDACTIVITY: Item | null = null;
    
  4. Before the return statement in the ParentContainer1 function, add the following code.

    const [selectedActivity, setSelectedActivity] = useState(
      INIT_SELECTEDACTIVITY
    );
    
    const showActivityItems = () => {
      return selectedActivity != null ? true : false;
    };
    
    const activityChangedHandler = (value: Item) => {
      setSelectedActivity(value);
    };
    
  5. Add the onActivityChanged attribute to the ActivityContainer element, and replace the ParentContainer2 element with a pair of conditional statements used either to display the ParentContainer2 element with a selectedActivity prop value sourced from the ActivityContainer component, or to display a header with instructions to select an activity. Also delete the oj-bg-warning-20 and oj-panel style classes from the div element to remove the yellow background color and border from the container.

    return (
      <div id="parentContainer1" class="oj-flex oj-flex-init">
        <ActivityContainer data={activityDataProvider} onActivityChanged={activityChangedHandler} />
        {showActivityItems() && (
          <ParentContainer2 activity={selectedActivity} />
        )}
        {!showActivityItems() && (
          <h4 class="oj-typography-subheading-sm">
            Select activity to view items
          </h4>
        )}
      </div>
    );
    

    Save the file. Your code should look similar to parent-container1-1-tsx.txt

Task 2: Modify the Activity Container for Selection

  1. Navigate to the JET-Virtual-DOM-app/src/components/Activity directory and open the ActivityContainer.tsx file in an editor.

  2. Add the following import statements to the top of the file.

    import { KeySetImpl, KeySet } from "ojs/ojkeyset";
    import { useMemo } from "preact/hooks";
    
  3. Add the onActivityChanged property to the Props type alias. Also create an Item type alias.

    type Props = {
      data?: MutableArrayDataProvider<Activity["id"], Activity>;
      value?: string;
      onActivityChanged: (value: Item) => void;
    };
    
    type Item = {
      id: number;
      name: string;
      short_desc?: string;
      price?: number;
      quantity?: number;
      quantity_shipped?: number;
      quantity_instock?: number;
      activity_id?: number;
      image?: string;
    };
    
  4. Before the return statement in the ActivityContainer function, add the following code.

    const selectedActivityChanged = (
      event: ojListView.firstSelectedItemChanged<Item["id"], Item>
    ) => {
      props.onActivityChanged(event.detail.value.data);
    };
    
    const activityValue = useMemo(() => {
      return new KeySetImpl([props.value]) as KeySet<Activity["name"]>;
    }, [props.value]);
    
  5. Add the selected and onfirstSelectedItemChanged attributes to the Oracle JET List View component. Also delete the oj-bg-info-30 from the div element to remove the blue background color from the container.

    selected={activityValue}
    onfirstSelectedItemChanged={selectedActivityChanged}
    

    Save the file. Your code should look similar to activity-container-1-tsx.txt.

Task 3: Modify Parent Container 2 for Selection

  1. Navigate to the JET-Virtual-DOM-app/src/components directory and open the ParentContainer2.tsx file in an editor.

  2. Add an import statement for the following Preact hooks to the top of the file.

    import { useState, useEffect, useCallback } from "preact/hooks";
    
  3. Add a Props type alias.

    type Props = {
      activity: Item | null;
    };
    
  4. Rename const activityItemDP as const INIT_DATAPROVIDER and delete the variable const specificItem.

  5. Add props to the ParentContainer2 function definition

    const ParentContainer2 = (props: Props) => {
    
  6. Add state and Preact hooks before the function’s return statement.

    const [selectedItemVal, setSelectedItemVal] = useState<Item | null>(null);
    const [activityItemDP, setactivityItemDP] = useState(INIT_DATAPROVIDER);
    
    const activityItemChangeHandler = useCallback(
      (item: Item) => {
        setSelectedItemVal(item);
      },
      [selectedItemVal]
    );
    
    const showItems = useCallback(() => {
      return selectedItemVal === null ? false : true;
    },[selectedItemVal]);
    
  7. Add a useEffect hook before the return statement.

    useEffect(() => {
      let actID = (props.activity!.id) - 1;
      let activityItemsArray = activityData[actID].items;
      setactivityItemDP(
        new MutableArrayDataProvider<ActivityItem["id"], ActivityItem>(activityItemsArray, {
          keyAttributes: "id",
        })
      );
    }, [props.activity]);
    
  8. Add the selectedActivity and onItemChanged data attributes to the ActivityItemContainer element. Replace the ItemDetailContainer element with a pair of conditional statements to either display the ItemDetailContainer with its selected activity item value or display a header with instructions to select an activity item. Also delete the oj-panel and oj-bg-danger-30 style classes from the surrounding div element to remove the red background color and border from the container.

    <div id="parentContainer2" class="oj-flex oj-flex-item oj-lg-padding-6x-horizontal oj-md-8 oj-sm-12">
      <ActivityItemContainer selectedActivity={props.activity} data={activityItemDP} onItemChanged={activityItemChangeHandler} />
      {showItems() && (
        <ItemDetailContainer item={selectedItemVal} />
      )}
      {!showItems() && (
        <h4 class="oj-typography-subheading-sm">
          Select activity item to see details
        </h4>
      )}
    </div>
    

    Save the file. Your code should look similar to parent-container2-1-tsx.txt

Task 4: Modify the Activity Item Container for Selection

  1. Navigate to the JET-Virtual-DOM-app/src/components/ActivityItem directory and open the ActivityItemContainer.tsx file in an editor.

  2. Add an import statement for the following Preact hooks to the top of the file.

    import { useState, useCallback } from "preact/hooks";
    
  3. Replace the Props type alias.

    type Props = {
      data?: MutableArrayDataProvider<ActivityItem["id"], ActivityItem>;
      selectedActivity: Item | null;
      onItemChanged: (item: Item) => void;
    };
    
  4. Add a DEFAULT_ACTIVITY_ITEM_STATE variable.

    const DEFAULT_ACTIVITY_ITEM_STATE: Partial<Item> = {};
    
  5. Add the following code before the return statement in the ActivityItemContainer function definition.

    const activityItemDataProvider = props.data;
    
    const [activityItemValue, setActivityItemValue] = useState(
      DEFAULT_ACTIVITY_ITEM_STATE
    );
    
    const [itemData, setItemData] = useState<Item>(props.selectedActivity!);
    
    const selectedActivityItemChanged = useCallback(
      (event: ojListView.firstSelectedItemChanged<Item["id"], Item>) => {
        let tempItem = event.detail.value.data;
        props.onItemChanged(tempItem);
        setActivityItemValue(tempItem);
        setItemData(tempItem);
      },
      [activityItemValue]
    );
    
  6. In the Oracle JET List View element, add an onFirstSelectedItemChanged attribute and replace the data attribute. Also delete the oj-bg-success-20 style class from the div element where id="activityItemsContainer" to remove the green background color from the container.

    <div
      id="activityItemsContainer"
      class="oj-flex-item oj-sm-padding-4x-start oj-md-6 oj-sm-12">
      <div id="container">
        <h3>Activity Items</h3>
        <oj-list-view
          id="activitiesList"
          class="item-display"
          aria-labelledby="activitiesHeader"
          data={activityItemDataProvider}
          gridlines={gridlinesItemVisible}
          selectionMode="single"
          onfirstSelectedItemChanged={selectedActivityItemChanged}
          scrollPolicy="loadMoreOnScroll"
          scrollPolicyOptions={scrollPolicyOpts}>
          <template slot="itemTemplate" render={listItemRenderer}></template>
        </oj-list-view>
    

    Save the file. Your code should look similar to activity-item-container-tsx.txt

  7. Navigate to the JET-Virtual-DOM-app/src/components/ItemDetail directory and open ItemDetailContainer.tsx in an editor.

  8. In the return statement of the ItemDetailContainer function, delete the oj-bg-neutral-30 style class from the div element to remove the gray background color from the container. Save the file.

Task 5: Test Selection Events in the Virtual DOM App

  1. In the terminal window, change to the JET-Virtual-DOM-app directory and run the virtual DOM app.

    npx ojet serve
    
  2. In the virtual DOM app, click the Baseball activity.

    The Activities list selection triggers the selectedActivityChanged event handler. The virtual DOM app renders the activity item data of the selected activity.

    Activity items rendered for the Baseball activity

    Description of the illustration master_detail_list.png

  3. In the Activity Items list, click the SureCatch Baseball Glove.

    The Activity Items list selection triggers the selectedActivityItemChanged event handler. The virtual DOM app renders the Item Detail Container with the data from the selected activity item.

    Item details rendered for the SureCatch Baseball Glove

    Description of the illustration master_detail_item.png

  4. 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 Detail Container is hidden.

    Activity items rendered for the Baseball activity

    Description of the illustration master_detail_list.png

  5. Press Ctrl+Shift+I or right-click the page and select Inspect to bring up the page view in the developer tools.

  6. In the Chrome DevTools toolbar, click Toggle device toolbar button to switch to the device mode.

  7. From the Dimensions dropdown menu in the screen emulator, select a device with a small screen size, such as the Pixel 7, to verify the content changes.

    Activity items rendered for the Baseball activity

    Description of the illustration resized_master_detail_list.png

  8. Close the browser window or tab that displays your running virtual DOM app.

  9. In the terminal window, press Ctrl+C, and if prompted, enter y to exit the Oracle JET tooling batch job.

Task 6: (Optional) Run a Virtual DOM App from a Restored App

If you want to run the completed Oracle JET virtual DOM app from the supplied code, you can restore the virtual DOM app from the downloaded archive file. To work with a “stripped and zipped” Oracle JET virtual DOM app, you must restore project dependencies, including Oracle JET tooling and the required libraries and modules, within the extracted virtual DOM app.

  1. Download the jet-virtual-dom-app-temp.zip file, rename it as JET-Virtual-DOM-app.zip, and extract the contents to the JET-Virtual-DOM-app directory.

  2. In the terminal window, navigate to the JET-Virtual-DOM-app directory and restore the Oracle JET virtual DOM app.

    npm install
    
  3. Wait for confirmation.

    Success: Restore complete
    

    The virtual DOM app is ready to run.

  4. Run the virtual DOM app and test it in the browser.

    npx ojet serve
    
  5. Close the browser window or tab that displays your running virtual DOM app.

  6. 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.