Update data records in an Oracle JET virtual DOM app

Introduction

This tutorial shows you how to use your Oracle JavaScript Extension Toolkit (Oracle JET) virtual DOM app to update an existing data record and submit it to a REST service.

Objectives

In this tutorial, you will learn how to update an existing data record and submit it to a REST service.

Prerequisites

Task 1: Create Components to Manage Form Information

Create a new component that will display a dialog to invoke the functionality to update a record.

  1. Navigate to the JET-Virtual-DOM-app/src/components/ActivityItem directory, create a new EditItemDialog.tsx file, and open it in an editor.

  2. Add the placeholder entries that define a function name (EditItemDialog) for the new component.

    import { h } from "preact";
    
    type Props = {
      message?: string;
    };
    
    const EditItemDialog = (props: Props) => {
      return (
        <div class="oj-web-applayout-max-width oj-web-applayout-content">
          <p>content</p>
        </div>
      );
    };
    
    export default EditItemDialog;
    

Task 2: Handle Opening the Dialog

Declare the type and function in the ItemActionsContainer component to invoke the EditItemDialog component that contains the functionality to update a record.

  1. Navigate to the JET-Virtual-DOM-app/src/components/ActivityItem directory and open the ItemActionsContainer.tsx file.

  2. At the top of the file, import the useState and useEffect Preact hooks, define the additional properties in a Props type alias that you require to use the EditItemDialog component, and then define an Item type alias.

    	import { h } from "preact";
    	import "ojs/ojbutton";
    	import { useState, useEffect } from "preact/hooks";
    
    	type Props = {
    		create: () => void;
    		edit: () => void;
    		itemSelected: Partial<Item>;
    	};
    
    	type Item = {
    		id: number;
    		name: string;
    		short_desc?: string;
    		price?: number;
    		quantity?: number;
    		quantity_shipped?: number;
    		quantity_instock?: number;
    		activity_id?: number;
    		image?: string;
    	};
    
  3. Before the return statement, use the Preact hooks that you imported to determine if an activity item is selected.

    	const ItemActionsContainer = (props: Props) => {
    		const [hideActions, setHideActions] = useState<boolean>(true);
    
    			if (props.itemSelected?.id) {
    		console.log("Selected: " + JSON.stringify(props.itemSelected));
    		}
    
    		useEffect(() => {
    		if (props.itemSelected?.id) {
    			setHideActions(false);
    		} else {
    			setHideActions(true);
    		}
    		}, [props.itemSelected]);
    
    		return (
    
    
  4. In the return statement, add a new oj-button element with an onojAction attribute that references the edit property.

    const ItemActionsContainer = (props: Props) => {
     return (
       <div>
         <oj-button id="createButton" onojAction={props.create}>Create</oj-button>
         <oj-button id="updateButton" disabled={hideActions} onojAction={props.edit}>Update</oj-button>
       </div>
    

    Save the ItemActionsContainer.tsx file. Your code should be similar to ItemActionsContainer.tsx.txt

  5. At the top of the EditItemDialog.tsx file, import the modules for the Oracle JET Dialog component, as well as the useRef, useEffect, MutableRef, and useState Preact hooks.

    import { h } from "preact";
    import { useRef, useEffect, useState, MutableRef  } from "preact/hooks";
    import "ojs/ojdialog";
    import { ojDialog } from "ojs/ojdialog";
    
  6. In the Props type alias, create the following properties.

    type Props = {
    	  isOpened: boolean;
    	  closeDialog: (ref: MutableRef<ojDialog>, type: string) => void;
    	  editItem: (data: Partial<Item>, ref: MutableRef<ojDialog>) => void;
    	  itemData: Partial<Item>;
    	};
    
  7. In the return statement, replace the existing div element with a span element that wraps the oj-dialog custom element.

    return (
         <span>
         <oj-dialog id="editDialog" ref={editDialogRef as MutableRef<ojDialog>} 
                            dialogTitle="Update Item Details" onojClose={closeDialog} cancelBehavior="icon">
              <div slot="body">
              <oj-label-value labelEdge="inside">
                   <oj-label for="itemid" slot="label">
                   Item ID
                   </oj-label>
                   <div id="itemid" slot="value" class="slot-line">
                   {editFormData?.id}
                   </div>
              </oj-label-value>
         <oj-form-layout>
              <oj-input-text id="name" labelHint="Name" onvalueChanged={onChangeHandler} value={editFormData?.name}></oj-input-text>
              <oj-input-text id="price" labelHint="Price" onvalueChanged={onChangeHandler} value={editFormData?.price}></oj-input-text>
              <oj-input-text id="short_desc" labelHint="Description" 
                                     onvalueChanged={onChangeHandler} value={editFormData?.short_desc}></oj-input-text>
              </oj-form-layout>
              </div>
              <div slot="footer">
              <oj-button id="submitBtn" onojAction={editItem}>
                   Submit
              </oj-button>
              </div>
         </oj-dialog>
    </span>
    );
    
  8. Before the return statement, add the methods that the attribute values in the child custom elements of the oj-dialog custom element reference. For example, the onChangeHandler method is called when an oj-input-text component detects a change through its onvalueChanged attribute.

    	const EditItemDialog = (props: Props) => {
    		const editDialogRef = useRef<ojDialog>();
    		const [editFormData, setEditFormData] = useState<Partial<Item>>({});
    
    		const onChangeHandler = (event: any) => {
    		if (event.detail.updatedFrom === "internal") {
    			setEditFormData({
    			...editFormData,
    			[event.currentTarget.id]: event.detail.value,
    			});
    		}
    		};
    
    		const closeDialog = () => {
    		props.closeDialog(editDialogRef as MutableRef<ojDialog>, "edit");
    		};
    
    		const editItem = () => {
    		console.log("data: " + JSON.stringify(editFormData));
    		props.editItem(editFormData, editDialogRef as MutableRef<ojDialog>);
    		};
    
    		useEffect(() => {
    		setEditFormData(props.itemData);
    		props.isOpened ? editDialogRef.current?.open() : editDialogRef.current?.close();
    		}, [props.isOpened]);
    
    		return (
    
    
  9. Before the declaration of the EditItemDialog function, define an Item type alias that includes fields for the data that you update and send to the REST service.

    type Item = {
      id: number;
      name: string | undefined;
      short_desc?: string;
      price?: number;
      quantity?: number;
      quantity_shipped?: number;
      quantity_instock?: number;
      activity_id?: number;
      image?: string;
     };
    
     const EditItemDialog = (props: Props) => {
    

    Save the EditItemDialog.tsx file. Your code should be similar to EditItemDialog.tsx.txt

Task 3: Consume the EditItemDialog Component

  1. Open the ActivityItemContainer.tsx file and import the EditItemDialog component that you created in the last task plus Preact’s MutableRef hook and the Oracle JET module for the Dialog component.

    import { useState, useCallback, MutableRef, useRef } from "preact/hooks";
    import EditItemDialog from "./EditItemDialog";
    
  2. In the Props type alias, update the entry for data to support the any type for RESTDataProvider and delete or comment out the ActivityItem type that the component no longer uses.

    	type Props = {
      data?: RESTDataProvider<any, any>;
      selectedActivity: Item | null;
    	  onItemChanged: (item: Item) => void;
    };
    
    // type ActivityItem = {
    //   id: number;
    //   name: string;
    //   items: Array<Item>;
    //   short_desc: string;
    //   image: string;
    // };
    
  3. In the return statement, update the ItemActionsContainer element with itemSelected and edit attribute values. After the CreateNewItemDialog element, add a new element for the EditItemDialog component that you imported.

    	<div id="container">
        <h3>Activity Items</h3>
          <ItemActionsContainer create={openCreateDialog} itemSelected={activityItemValue} edit={openEditDialog} />
          . . . 
    </div>
    	<CreateNewItemDialog isOpened={isCreateOpened} createNewItem={createItem} closeDialog={handleDialogClose} />
    	<EditItemDialog isOpened={isEditOpened} editItem={editItem} closeDialog={handleDialogClose} itemData={itemData} />
     . . . 
    
  4. Before the return statement, add the openEditDialog function to open the edit dialog and the editItem function to send the updated activity item to the REST service.

    	const openEditDialog = () => {
    		console.log("Item: " + JSON.stringify(itemData));
    		setIsEditOpened(true);
    		console.log("Edit dialog opened");
    		};
    
    	const editItem = async (newItemData:Partial<Item>, editDialogRef = useRef<ojDialog>()) => {
    		if (newItemData != null) {
    		const row = {
    			itemId: newItemData.id,
    			name: newItemData.name,
    			price: newItemData.price,
    			short_desc: newItemData.short_desc,
    		};
    
    		// Create and send request to update row on rest service
    		const request = new Request(`${restServerURLItems}${itemData.id}`, {
    			headers: new Headers({
    			"Content-type": "application/json; charset=UTF-8",
    			}),
    			body: JSON.stringify(row),
    			method: "PUT",
    		});
    		const response = await fetch(request);
    		const updatedRow = await response.json();
    
    		// Create update mutate event and call mutate method
    		// to notify dataprovider consumers that a row has been
    		// updated
    		const updatedRowKey = itemData.id;
    		const updatedRowMetaData = { key: updatedRowKey };
    		props.data?.mutate({
    			update: {
    			data: [updatedRow],
    			keys: new Set([updatedRowKey]),
    			metadata: [updatedRowMetaData],
    			},
    		});
    		} // End if statement
    		console.log("Edited item");
    		editDialogRef.current?.close();
    		};
    
    

    Save the ActivityItemContainer.tsx file. Your code should be similar to ActivityItemContainer.tsx.txt

Task 4: Test the Code and Update a Record

  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 browser, view the dynamic changes in your virtual DOM app.

  3. In the virtual DOM app, click the Baseball activity, and then click the SureFire Ball (Set of 4) item.

  4. Click the Update button.

    The Update Item Details dialog pops up.

  5. Change the price from 20.5 to 21 and click Submit.

    The section refreshes and the price of the item has been updated.

    Update Item Details

    Description of the illustration update_record.png

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

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