Create a form to create 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 create a data record and submit it to a REST service.

Objectives

In this tutorial, you will learn how to write data to a REST service.

Prerequisites

Task 1: Create Components to Manage Form Information

Create new components that will display a button used to invoke the functionality to create a new record. An ItemActionsContainer component holds the button that allows virtual DOM app users to invoke the CreateNewItemDialog component that contains the functionality to create a new record.

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

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

    import { h } from 'preact';
    
    type Props = {
      message?: string;
    };
    
    const CreateNewItemDialog = (props: Props) => {
      return (
        <div class="oj-web-applayout-max-width oj-web-applayout-content">
          <p>content</p>
        </div>
      );
    };
    
    export default CreateNewItemDialog;
    
  3. In the same directory, create a ItemActionsContainer.tsx file and open it in an editor.

  4. Add the placeholder entries that define a function name (ItemActionsContainer) for the new component.

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

Task 2: Handle Opening the Dialog

Import the Oracle JET modules and declare the functions that will enable your Oracle JET virtual DOM app to successfully open a dialog.

  1. At the top of the open ItemActionsContainer.tsx file, import the Oracle JET module for the Button component.

    import { h } from 'preact';
    import 'ojs/ojbutton';
    
  2. Define a create property in the Props type alias to manage opening a create dialog.

    import { h } from 'preact';
    import 'ojs/ojbutton';
    
    type Props = {
      create: () => void;
    };
    
  3. In the return statement, replace the existing div element with a new div element that renders an oj-button element with an onojAction attribute that references the create property.

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

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

  4. At the top of the open CreateNewItemDialog.tsx file, import the Oracle JET modules for the Dialog component and MutableRef hook.

    import { h } from 'preact';
    import 'ojs/ojdialog';
    import { ojDialog } from 'ojs/ojdialog';
    import { MutableRef } from 'preact/hooks';
    
  5. Define isOpened and closeDialog properties in the Props type alias.

    type Props = {
      isOpened: boolean;
      closeDialog: (ref: MutableRef<ojDialog>, type: string) => void;
    };
    
  6. 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="createDialog" ref={createDialogRef} dialogTitle="Create New Item" onojClose={closeDialog} cancelBehavior="icon">
          <div slot="body">
          <p>dialog open</p>
          </div>
       </oj-dialog>
    </span>
    );
    
  7. At the top of the CreateNewItemDialog.tsx file, import the useRef and useEffect Preact hooks.

    import { h } from 'preact';
    import { useRef, useEffect } from 'preact/hooks';
    import 'ojs/ojdialog';
    import { ojDialog } from 'ojs/ojdialog';
    
  8. Before the return statement, declare the createDialogRef and closeDialog variables that will hold the reference that the useRef Preact hook retrieves.

    const CreateNewItemDialog = (props: Props) => {
    
    	  const createDialogRef = useRef<ojDialog>(null);
    
    	  const closeDialog = () => {
    		 props.closeDialog(createDialogRef as MutableRef<ojDialog>, "create");
    	   }
    
    	return (
    
    
  9. Also before the return statement, use the useEffect hook to write an expression that sets a value for the isOpened property.

    	const CreateNewItemDialog = (props: Props) => {
    		const createDialogRef = useRef<ojDialog>(null);
    
    		const closeDialog = () => {
    		props.closeDialog(createDialogRef as MutableRef<ojDialog>, "create");
    		}
    
    		useEffect(() => {
          props.isOpened
             ? createDialogRef.current?.open()
             : createDialogRef.current?.close();
    		}, [props.isOpened]);
    
    	return (
    
    

    Save the CreateNewItemDialog.tsx file. Your code should be similar to CreateNewItemDialog-1.tsx.txt

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

  11. At the top of ActivityItemContainer.tsx, import the ItemActionsContainer and CreateNewItemDialog components that you just created and also import the modules for the Oracle JET Form Layout and Input Text components.

    import ItemActionsContainer from "./ItemActionsContainer";
    import CreateNewItemDialog from "./CreateNewItemDialog";
    import "ojs/ojformlayout";
    import "ojs/ojinputtext";
    import { ojDialog } from "ojs/ojdialog";
    import { MutableRef} from "preact/hooks"
    
  12. After the ActivityItemContainer function declaration, create variables that use Preact’s useState hook and a function (openCreateDialog) to open the dialog.

    We also include an entry that manages the open state of an Edit dialog that we create in a later tutorial.

    const ActivityItemContainer = (props: Props) => {
     const [isCreateOpened, setIsCreateOpened] = useState<boolean>(false);
     const [isEditOpened, setIsEditOpened] = useState<boolean>(false);
    
      const openCreateDialog = () => {
         console.log("CreateNewItemDialog called");
         setIsCreateOpened(true);
      };
    
  13. Before the return statement, also include a function that closes an open dialog.

    const handleDialogClose = (ref: MutableRef<ojDialog>, type: string) => {
       type === "create" ? setIsCreateOpened(false) : setIsEditOpened(false);
       ref.current.close();
    };
    
    return (
       <div id="activityItemsContainer" class=. . .>
    
  14. In the return statement, include the newly-created ItemActionsContainer and CreateNewItemDialog components.

    return (
    <div id="activityItemsContainer" . . .>
     <div id="container">
       <h3>Activity Items</h3>
        <ItemActionsContainer create={openCreateDialog} />
        <CreateNewItemDialog isOpened={isCreateOpened} closeDialog={handleDialogClose} />
    

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

Task 3: Handle Submitting the Dialog Input

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

  2. At the top of the open CreateNewItemDialog.tsx file, import Preact’s useState hook.

    import { ojDialog } from 'ojs/ojdialog';
    import { MutableRef, useRef, useEffect, useState } from "preact/hooks"
    
  3. In the Props type alias, define a createNewItem property.

    type Props = {
    		isOpened: boolean;
    		closeDialog: (ref: MutableRef<ojDialog>, type: string) => void;
    		createNewItem: (data: Partial<Item>, ref: MutableRef<ojDialog>) => void;
    	};
    
  4. Define an Item type alias that includes fields for the data that you send to the REST service.

    type Item = {
      name?: string | undefined;
      short_desc?: string;
      price?: number;
      quantity_shipped?: number;
      quantity_instock?: number;
    };
    
  5. Find the oj-dialog custom element in the return statement and replace the content of <div slot="body"> with an oj-form-layout element and oj-input-text elements for the input fields to create a new item. Also include a <div slot="footer"> with an oj-button element.

    <oj-dialog id="createDialog" ref={createDialogRef} dialogTitle="Create New Item" onojClose={closeDialog} cancelBehavior="icon">
    	   <div slot="body">
    		  <oj-form-layout>
    		  <oj-input-text id="name" labelHint="Name" onvalueChanged={onChangeHandler}></oj-input-text>
    		  <oj-input-text id="price" labelHint="Price" onvalueChanged={onChangeHandler}></oj-input-text>
    		  <oj-input-text id="short_desc" labelHint="Description" onvalueChanged={onChangeHandler}></oj-input-text>
    		  <oj-input-text id="quantity_instock" labelHint="Quantity: In-Stock" onvalueChanged={onChangeHandler}></oj-input-text>
    		  <oj-input-text id="quantity_shipped" labelHint="Quantity: Shipped" onvalueChanged={onChangeHandler}></oj-input-text>
    		  </oj-form-layout>
    	   </div>
    	   <div slot="footer">
    		  <oj-button id="submitBtn" onojAction={createItem}>Submit</oj-button>
    	   </div>
    	</oj-dialog>
    
  6. Before the return statement, make use of the useState hook and include the onChangeHander and createItem functions that the Oracle JET elements reference.

    const [formData, setFormData] = useState<Partial<Item>>({});
    
    	const onChangeHandler = (event: any) => {
    	   setFormData({
    		  ...formData,
    		  [event.currentTarget.id]: event.detail.value,
    	   });
    	}
    
    	const createItem = () => {
    	   console.log("data: " + JSON.stringify(formData));
    	   props.createNewItem(formData, createDialogRef as MutableRef<ojDialog>);
    	};
    
    	return (
    	  <span>. . .
    

    Save the CreateNewItemDialog.tsx file. Your code should be similar to CreateNewItemDialog-2.tsx.txt

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

  8. In the return statement, update the CreateNewItemDialog class to include the createNewItem method that you defined in the CreateNewItemDialog component.

    <CreateNewItemDialog isOpened={isCreateOpened} createNewItem={createItem} closeDialog={handleDialogClose} />
    
  9. Before the return statement, include a new createItem function that creates a new item and sends it to the backend REST service.

    const createItem = async (data: Partial<Item>, createDialogRef: MutableRef<ojDialog>) => {
    	 //process create command and close dialog on success
    	 if (data?.name) {
    		let quantity = Number(data.quantity_instock) + Number(data.quantity_shipped);
    		const row = {
    		  name: data.name,
    		  short_desc: data.short_desc,
    		  price: data.price,
    		  quantity_instock: data.quantity_instock,
    		  quantity_shipped: data.quantity_shipped,
    		  quantity: quantity,
    		  activity_id: props.selectedActivity?.id,
    		  image: "css/images/product_images/jet_logo_256.png",
    		};
    
    		// Create and send request to REST service to add row
    		const request = new Request(restServerURLItems, {
    		  headers: new Headers({
    			"Content-type": "application/json; charset=UTF-8",
    		  }),
    		  body: JSON.stringify(row),
    		  method: "POST",
    		});
    
    		const response = await fetch(request);
    		const addedRow = await response.json();
    
    		activityItemDataProvider?.refresh();
    		// Close dialog
    		console.log("Created new item");
    		createDialogRef.current.close();
    	  }
    	};
    
  10. After the ActivityItemContainer function declaration, define a variable that references the URL to use to send the item to the REST service.

    const ActivityItemContainer = (props: Props) => {
    	const activityItemDataProvider = props.data;
        const restServerURLItems = "https://apex.oracle.com/pls/apex/oraclejet/lp/activities/" + props.selectedActivity?.id + "/items/";
    
  11. Save the ActivityItemContainer.tsx file. Your code should be similar to ActivityItemContainer-2.tsx.txt

Task 4: Test the Code and Create 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 on the Baseball activity.

  4. Click Create.

    The Create New Item dialog opens.

  5. Fill in details for a new item.

    • Name: SureFire Ball (Set of 4)
    • Price: 20.5
    • Description: Canvas balls for practice
    • Quantity: In-Stock: 35
    • Quantity: Shipped: 61
  6. Click Submit.

    The section refreshes and the item is part of the list of Baseball activity items.

  7. Click the SureFire Ball (Set of 4) item in the list and view its details.

    Create New Item dialog

    Description of the illustration create_record.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.

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.