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
- A development environment set up to create Oracle JET virtual DOM apps that includes an installation of Node.js
- Completion of the previous tutorial in this learning path, Create a Form to Create Data Records in an Oracle JET Virtual DOM App
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.
-
Navigate to the
JET-Virtual-DOM-app/src/components/ActivityItem
directory, create a newEditItemDialog.tsx
file, and open it in an editor. -
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.
-
Navigate to the
JET-Virtual-DOM-app/src/components/ActivityItem
directory and open theItemActionsContainer.tsx
file. -
At the top of the file, import the
useState
anduseEffect
Preact hooks, define the additional properties in aProps
type alias that you require to use theEditItemDialog
component, and then define anItem
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; };
-
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 (
-
In the
return
statement, add a newoj-button
element with anonojAction
attribute that references theedit
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 toItemActionsContainer.tsx.txt
-
At the top of the
EditItemDialog.tsx
file, import the modules for the Oracle JET Dialog component, as well as theuseRef
,useEffect
,MutableRef
, anduseState
Preact hooks.import { h } from "preact"; import { useRef, useEffect, useState, MutableRef } from "preact/hooks"; import "ojs/ojdialog"; import { ojDialog } from "ojs/ojdialog";
-
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>; };
-
In the
return
statement, replace the existingdiv
element with aspan
element that wraps theoj-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> );
-
Before the
return
statement, add the methods that the attribute values in the child custom elements of theoj-dialog
custom element reference. For example, theonChangeHandler
method is called when anoj-input-text
component detects a change through itsonvalueChanged
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 (
-
Before the declaration of the
EditItemDialog
function, define anItem
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 toEditItemDialog.tsx.txt
Task 3: Consume the EditItemDialog Component
-
Open the
ActivityItemContainer.tsx
file and import theEditItemDialog
component that you created in the last task plus Preact’sMutableRef
hook and the Oracle JET module for the Dialog component.import { useState, useCallback, MutableRef, useRef } from "preact/hooks"; import EditItemDialog from "./EditItemDialog";
-
In the
Props
type alias, update the entry fordata
to support theany
type forRESTDataProvider
and delete or comment out theActivityItem
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; // };
-
In the
return
statement, update theItemActionsContainer
element withitemSelected
andedit
attribute values. After theCreateNewItemDialog
element, add a new element for theEditItemDialog
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} /> . . .
-
Before the
return
statement, add theopenEditDialog
function to open the edit dialog and theeditItem
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 toActivityItemContainer.tsx.txt
Task 4: Test the Code and Update a Record
-
In the terminal window, change to the
JET-Virtual-DOM-app
directory and run the virtual DOM app.npx ojet serve
-
In the browser, view the dynamic changes in your virtual DOM app.
-
In the virtual DOM app, click the Baseball activity, and then click the SureFire Ball (Set of 4) item.
-
Click the Update button.
The Update Item Details dialog pops up.
-
Change the price from
20.5
to21
and click Submit.The section refreshes and the price of the item has been updated.
-
Close the browser window or tab that displays your running virtual DOM 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 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.
Update data records in an Oracle JET virtual DOM app
F70629-03
July 2024