Create the detail view in an Oracle JET virtual DOM app

Introduction

Oracle JavaScript Extension Toolkit (Oracle JET) components preserve the hierarchical relationship of master and detail data objects, as defined by the data source. In the previous tutorial, you created a data provider object using an instance of the MutableArrayDataProvider class. Using props, you passed the data provider object to the List View component in the ActivityContainer component and rendered the data items. Now, using the same JSON data store, you will create the detail view of your app by populating the ActivityItemContainer and ItemDetailContainer components, respectively, with the activity items of a given activity and the specific item details of an activity item by passing them data from a parent container using props.

Objectives

In this tutorial, you will read master-detail hierarchical data from a local JSON document and display the detail view in an Oracle JET virtual DOM app. You will data bind multiple items of the child data objects from a local JSON document and populate the rows of an Oracle JET List View component in the ActivityItemContainer component with that data, as well as Oracle JET Avatar and Chart components in the ItemDetailContainer component.

Prerequisites

Task 1: Create a Data Provider in Parent Container 2

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

  2. At the top of the file, add import statements for the MutableArrayDataProvider class and the JSON data in the store_data.json file.

    import MutableArrayDataProvider = require("ojs/ojmutablearraydataprovider");
    import * as storeData from "text!./store_data.json";
    
  3. Create Item and ActivityItem type aliases.

    type Item = {
      id: number;
      name: string;
      short_desc?: string;
      price?: number;
      quantity?: number;
      quantity_shipped?: number;
      quantity_instock?: number;
      activity_id?: number;
      image?: string;
    };
    
    type ActivityItem = {
      id: number;
      name: string;
      items: Array<Item>;
      short_desc: string;
      image: string;
    };
    
  4. Add an activityItemDP instance of a MutableArrayDataProvider, as well as two variables used to provide the data provider with the Baseball activity’s items from the JSON data source.

    const activityData = JSON.parse(storeData);
    let activityItemsArray = activityData[0].items
    
    // Create data provider instance for the array of activity items for the Baseball activity
    const activityItemDP = new MutableArrayDataProvider<ActivityItem["id"], ActivityItem>(activityItemsArray, {
      keyAttributes: "id",
    });
    
  5. In the ParentContainer2 function, add a data attribute to the ActivityItemContainer element to pass the data provider object to the ActivityItemContainer component using props.

    <ActivityItemContainer data={activityItemDP} />
    

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

Task 2: Create the Activity Item Container’s List View Component

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

  2. At the top of the file, delete the import statements for the ResponsiveUtils module and the useRef, useState, and useEffect hooks. Also delete the sm_md_view variable.

  3. Inside the ActivityItemContainer function, remove code that makes use of hooks and state and that enables the conditional display of content in the return statement. Your function should look similar to the following code.

    const ActivityItemContainer = () => {
    
      return (
        <div
          id="activityItemsContainer"
          class="oj-flex-item oj-bg-success-20 oj-sm-padding-4x-start oj-md-6 oj-sm-12">
          <div id="container" class="item-display no-wrap">
            <h3>Activity Items</h3>
            <ul>
              <li class="li-item">Louisville Slugger Bat</li>
              . . .
            </ul>
          </div>
        </div>
      );
    };
    
  4. Delete the ul element from the return statement, and in the div element where id="container", remove the class attribute. The ActivityItemContainer function should look similar to the following code.

    const ActivityItemContainer = () => {
    
      return (
        <div
          id="activityItemsContainer"
          class="oj-flex-item oj-bg-success-20 oj-sm-padding-4x-start oj-md-6 oj-sm-12">
          <div id="container">
            <h3>Activity Items</h3>
          </div>
        </div>
      );
    };
    
  5. Add the following import statements for the Oracle JET List View component and MutableArrayDataProvider class to the top of the file.

    import "ojs/ojlistview";
    import { ojListView } from "ojs/ojlistview";
    import MutableArrayDataProvider = require("ojs/ojmutablearraydataprovider");
    
  6. Add Props, ActivityItem, and Item type aliases below the block of import statements.

    type Props = {
      data?: MutableArrayDataProvider<ActivityItem["id"], ActivityItem>;
      value?: string;
    };
    
    type Item = {
      id: number;
      name: string;
      short_desc?: string;
      price?: number;
      quantity?: number;
      quantity_shipped?: number;
      quantity_instock?: number;
      activity_id?: number;
      image?: string;
    };
    
    type ActivityItem = {
      id: number;
      name: string;
      items: Array<Item>;
      short_desc: string;
      image: string;
    };
    
  7. Create the listItemRenderer function that renders each list item and includes span and div elements to populate the List View component’s rows with a background image, name, and short description.

    const listItemRenderer = (item: ojListView.ItemTemplateContext) => {
      const image = item.data.image.replace("css", "styles");
      return (
        <div class="oj-flex no-wrap">
          <span
            class="demo-thumbnail oj-flex-item"
            style={"background-image:url(" + image + ")"}></span>
          <div class="demo-content oj-flex-item">
            <div>
              <strong>{item.data.name}</strong>
            </div>
            <span class="demo-metadata">{item.data.short_desc}</span>
          </div>
        </div>
      );
    };
    
  8. Underneath the listItemRenderer function, add a ListViewProps type alias and two variables to define the gridline and scroll properties for the List View component.

    type ListViewProps = ComponentProps<"oj-list-view">;
    const gridlinesItemVisible: ListViewProps["gridlines"] = { item: "visible" };
    const scrollPolicyOpts: ListViewProps["scrollPolicyOptions"] = { fetchSize: 5 };
    
  9. Add props to the ActivityItemContainer function definition.

    const ActivityItemContainer = (props: Props) => {
    
  10. Inside the ActivityItemContainer function’s return statement, add an Oracle JET List View component, where the data provider object that is passed down from ParentContainer2 is accessed via props in the List View’s data attribute.

    return (
      <div
        id="activityItemsContainer"
        class="oj-flex-item oj-bg-success-20 oj-sm-padding-4x-start oj-md-6 oj-sm-12">
        <div id="container">
          <h3>Activity Items</h3>
          <oj-list-view
            id="itemsList"
            class="item-display"
            aria-labelledby="activitiesHeader"
            data={props.data}
            gridlines={gridlinesItemVisible}
            selectionMode="single"
            scrollPolicy="loadMoreOnScroll"
            scrollPolicyOptions={scrollPolicyOpts}>
          </oj-list-view>
        </div>
      </div>
     );
    
  11. Within the List View component, add a template element with a slot attribute to render each item in the list.

    . . .
        <template slot="itemTemplate" render={listItemRenderer}></template>
    </oj-list-view>
    

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

Task 3: Run the Virtual DOM App

  1. In the terminal window, navigate to the JET_Virtual_DOM_app directory and run the app.

    npx ojet serve
    

    The app runs in the web browser, and the Activity Items list displays the Baseball activity items with their short descriptions and thumbnail images.

    Activities and Activity Items list information

    Description of the illustration activity_items_displayed.png

  2. Leave the terminal window and the browser that displays your app open.

Task 4: Create the Item Detail Chart and Avatar Components

Pass the item details of the SureCatch Baseball Glove activity item from the ParentContainer2 component to the ItemDetailContainer component via props, binding the data to the Oracle JET Chart and Avatar components, as well as to multiple text fields.

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

  2. Below the activityItemDP instance, add a specificItem variable to hold the item details data for the SureCatch Baseball Glove activity item.

    // Create an object variable holding the item details for the SureCatch Baseball Glove 
    const specificItem: Item = activityData[0].items[0]
    
  3. Add an item attribute to the ItemDetailContainer element to pass down the item details to the child component via props.

    <ItemDetailContainer item={specificItem} />
    

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

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

  5. Remove these import statements from the top of the file.

    
    import { useState, useCallback } from "preact/hooks";
    import "ojs/ojlabel";
    import "ojs/ojselectsingle";
    import { ojSelectSingle } from "ojs/ojselectsingle";
    import * as storeData from "text!../store_data.json";
    import "ojs/ojlistview";
    import { ojListView } from "ojs/ojlistview";
    import "ojs/ojlistitemlayout";
    
  6. Add an import statement for the ojavatar module.

    import "ojs/ojavatar";
    
  7. Remove the ChartType, ListViewProps, and Activity type aliases; the chartTypeData, gridlinesItemVisible, and chartData variables; and the activityDataProvider, chartTypesDP and chartDataProvider data provider instances.

  8. Add Props and Item type aliases.

    type Props = {
      item?: Item | null;
    };
    
    type Item = {
      id: number;
      name: string;
      short_desc?: string;
      price?: number;
      quantity?: number;
      quantity_shipped?: number;
      quantity_instock?: number;
      activity_id?: number;
      image?: string;
    };
    
  9. Inside the ItemDetailContainer function, delete the following lines of code.

    const [val, setVal] = useState("bar" as ChartProps['type']);
    
    const valChangeHandler = useCallback(
      (event: ojSelectSingle.valueChanged<ChartType['value'], ChartType>) => {
        setVal(event.detail.value as ChartProps['type']);
      },
      [val, setVal]
    );
    
  10. Replace the chartItem template variable.

    const chartItem = (
      item: ojChart.ItemTemplateContext<ChartItem["id"], ChartItem>
    ) => {
      return (
        <oj-chart-item
          value={item.data.value}
          groupId={[0]}
          seriesId={item.data.series}
        ></oj-chart-item>
      );
    };
    
  11. Add props to the ItemDetailContainer function definition

    const ItemDetailContainer = (props: Props) => {
    
  12. Add a pieDataProvider instance of a MutableArrayDataProvider before the return statement.

    const pieDataProvider: MutableArrayDataProvider<ChartItem["id"], ChartItem> = new MutableArrayDataProvider(
      [
        { series: "Quantity in Stock", value: props.item?.quantity_instock },
        { series: "Quantity shipped", value: props.item?.quantity_shipped },
      ],
      { keyAttributes: "id" }
    );
    
  13. Inside the return statement, beneath the h3 heading for Item Details, delete the oj-label and oj-select-single elements. Replace that code with a horizontal rule, oj-avatar element, and div elements to display databound item details.

    <hr class="hr-margin" />
    <oj-avatar role="img" size="lg" aria-label={"product image for" + props.item?.name} 
        src={props.item?.image?.replace("css", "styles")} class="float-right"></oj-avatar>
    <div id="itemName" class="data-name">{props.item?.name}</div>
    <div id="itemDesc" class="data-desc">{props.item?.short_desc}</div>
    <div id="itemPrice">{"Price: " + props.item?.price + " each"}</div>
    <div id="itemId">{"Item Id: " + props.item?.id}</div>
    
  14. Replace the id, type and data attributes in the oj-chart element. Also add a div element surrounding the oj-chart element.

    <div>
      <oj-chart id="pieChart" type="pie" data={pieDataProvider} animationOnDisplay="auto"
                animationOnDataChange="auto" hoverBehavior="dim" class="chartStyle">
        <template slot="itemTemplate" render={chartItem}></template>
        </oj-chart>
       </div>
      </div>
     );
    };
    

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

  15. Navigate to the JET-Virtual-DOM-app/src/styles directory and open the app.css file in an editor.

  16. Add the following style classes to the file that are used to format the item details.

    .hr-margin {
      margin-top: 12px;
    }
    
    .data-name {
      font-size: 20px;
      font-weight: bolder;
    }
    
    .data-desc {
      font-size: 14px;
      font-weight: 400;
      font-style: italic;
      margin-bottom: 10px;
    }
    
    .float-right {
      float: right;
    }
    

    Save the file. Your code should look similar to app.css.

Task 5: View the Changes in the App

  1. Return to the browser to view the changes in your virtual DOM app.

    In the Item Detail Container, databound text fields and the Oracle JET Avatar and Chart components display the data for the SureCatch Baseball Glove item. However, the lists in the Activity Item Container and Activity Container do not respond to selection. In the next tutorial, you will add selection functionality.

    Item Detail Container updated

    Description of the illustration item_detail_container_updated.png

  2. Close the browser window or tab that displays your running app.

  3. 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 next tutorial in this learning path, 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.