3 Understand VComponent-based Web Components
Oracle JET provides you with a web component API,
        VComponent, to create web components that use virtual DOM rendering. 
               
The web components that you create using VComponent use virtual DOM rendering. For those of you who previously used the Composite Component Architecture (CCA) to develop web components, you’ll see many differences. Those of you who are familiar with the Preact library that underpins the Oracle JET virtual DOM architecture will see some familiar concepts. This chapter attempts to introduce you to the concepts that you'll need to know to develop VComponent-based web components.
One difference to note is that unlike CCA-based web components,
            VComponent-based web components do not use Knockout or its built-in expression evaluator
            to evaluate expressions. Instead, VComponent-based web components use JET’s
                CspExpressionEvaluator to ensure that expressions you use comply
            with Content Security Policy. CspExpressionEvaluator supports a limited
            set of expressions to ensure compliance with Content Security Policy. Familiarize
            yourself with the syntax that JET's CspExpressionEvaluator supports
            when using expressions in your VComponent-based web component. See the 
            CspExpressionEvaluator API documentation. 
               
The JET tooling assists you with creating, packaging, and publishing web components. Usage of the JET tooling remains the same as for CCA-based web component development, but the output differs. We'll go through the creation of a standalone VComponent-based web component and a series of web components to include in a JET Pack in the next chapter.
For now, let’s look at usage of VComponent to create web components,
            assuming that you have already acquired the Prerequisite Knowledge that we described in the introductory chapter of this guide.
               
Note:
You can complement your reading of this chapter by also reading the VComponent entry in the API Reference for Oracle® JavaScript Extension Toolkit (Oracle JET) and the Oracle JET VComponent Tutorial.Hello VComponent, an Introduction
You write a VComponent-based web component as a TypeScript module in a file
                with the .tsx file extension. 
                  
The example that follows shows a VComponent class named
                                HelloWorld with a custom element name of
                                hello-world in a file named
                                hello-world.tsx. Note the following about the
                        entries in the hello-world.tsx file:
                  
- JSX elements express the content of the virtual DOM tree
                                        (<p>{props.message}</p>).
- The hfunction, imported from the preact module, turns the JSX elements into virtual DOM elements.
import { ExtendGlobalProps, registerCustomElement } from "ojs/ojvcomponent";
import { h, ComponentProps, ComponentType } from "preact";
import componentStrings = require("ojL10n!./resources/nls/hello-world-strings");
import "css!./hello-world-styles.css";
type Props = Readonly<{
  message?: string;
}>;
/**
 *
 * @ojmetadata version "1.0.0"
 * @ojmetadata displayName "A user friendly, translatable name of the component"
 * @ojmetadata description "A translatable high-level description for the component"
 *
 */
function HelloWorldImpl({ message = "Hello from  hello-world" }: Props) {
  return <p>{message}</p>;
}
export const HelloWorld: ComponentType<ExtendGlobalProps<ComponentProps<typeof HelloWorldImpl>>> 
                                           = registerCustomElement("hello-world", HelloWorldImpl);The Oracle JET tooling helps you create VComponent web components by
                        generating a template .tsx file plus additional files and
                        folders with resources to support the component. The example just shown with
                        the custom element name of hello-world was created by the
                        following command:
                  
ojet create component hello-world
If you want to create a VComponent-based web component in an app that
                        does not use the virtual DOM architecture, you need to include
                                --vcomponent in the command to create the component
                                (ojet create component hello-world
                                --vcomponent). The Oracle JET tooling also supports
                        the creation of class-based web components if you append the
                                class option to the --vcomponent
                        parameter (ojet create component hello-world
                                --vcomponent=class). The default behavior is to create
                        function-based VComponents.
                  
Irrespective of the type of VComponent that you create (class or
                        function), the tooling generates these files in the directory referenced by
                        the components property in the
                                appRootDir/oraclejetconfig.json file. By default,
                        the value of the components property is also
                                components. 
                  
appRootDir/components/hello-world/
|   loader.ts
|   hello-world-styles.css
|   hello-world.tsx
|   README.md
+---resources
+---themesReaders who previously developed CCA-based web components will
                        recognize the loader.ts file that the Oracle JET tooling
                        includes so that the component can be used by the Component Exchange, Oracle
                        Visual Builder, and the Oracle JET tooling itself. For a VComponent-based
                        web component, the loader.ts file includes an entry to
                        export the VComponent module, as in the following example:
                  
 export { HelloWorld } from "./hello-world";
Once you build a VComponent web component, you can import it into
                        the app where it is to be used. The following example demonstrates how you
                        import our example component into the content component of
                        an app that was scaffolded using the virtual DOM architecture starter
                        template:
                  
import { h } from "preact";
import { HelloWorld } from "hello-world/loader";
export function Content() {
  return (
    <div class="oj-web-applayout-max-width oj-web-applayout-content">
      <HelloWorld />
    </div>
  );
}
Metadata for VComponents
JET metadata expresses information that may be useful to both tools and consumers of the VComponent-based web components that you create.
You write metadata in the VComponent's module class. You’ll have seen
                        examples of this metadata in the HelloWorld VComponent that we introduced
                        earlier. Specifically, the HelloWorld VComponent included a TypeScript
                        decorator, @customElement("hello-world"), to add custom
                        element behavior to the VComponent at runtime, and it is also used at build
                        time as a source of the component’s “name” metadata. The
                        other example is the use of the @ojmetadata doc annotation
                        where a series of entries provide version, display name, and description
                        information, as in the following example:
                  
* @ojmetadata version "1.0.0"
* @ojmetadata displayName "A user friendly, translatable name of the component"
* @ojmetadata description "A translatable high-level description for the component"
You’ll notice that each @ojmetadata annotation
                        specifies a single name/value pair. The values must be valid JSON values. As
                        shown above, string values should be double-quoted. Object, array, and
                        primitive values can be specified directly within the annotation (without
                        quotes). You can also extend the metadata to append extra information in an
                        extension field, as shown by the following example.
                  
* @ojmetadata extension {
*    vbdt: {
*      someVisualBuilderDesignTimeField: true
*    }
* }For reference information about JET Metadata, see JET Metadata.
Nest VComponents
Custom element-based VComponents can be embedded directly into HTML.
This allows you to integrate VComponents into existing Oracle JET content,
                        including into composite components, oj-module content, or pages authored in
                        Oracle Visual Builder. In addition to being hosted within HTML, VComponents
                        can be nested inside of other VComponents. A parent VComponent can reference
                        a child VComponent using the component class name. In the following example,
                        a VComponent class, HelloParent, nests a child VComponent,
                                Hello.
                  
import { customElement, ExtendGlobalProps } from "ojs/ojvcomponent";
import { h, Component, ComponentChild } from "preact";
import { Hello } from "oj-greet/hello/loader";
type Props = {
  message?: string;
};
/**
 * @ojmetadata pack "oj-greet"
 * ...
 */
@customElement("oj-greet-hello-parent")
export class HelloParent extends Component<ExtendGlobalProps<Props>> {
  static defaultProps: Partial<Props> = {
    message: "Hello from oj-greet-hello-parent!",
  };
  render(props: Props): ComponentChild {
    return (
      <div>
        <p>{props.message}</p>
        <p>The HelloParent VComponent nests the Hello VComponent class in the
          next line:</p>
        <Hello />
      </div>
    );
  }
}The resulting content in the HTML is:
<div class="oj-web-applayout-max-width oj-web-applayout-content">
 <oj-greet-hello-parent class="oj-complete">
    <div>
      <p>Hello from oj-greet-hello-parent!</p>
        <p>The HelloParent VComponent nests the Hello VComponent class in the next line:</p>
          <oj-greet-hello class="oj-complete"><p>Hello from oj-greet-hello!</p></oj-greet-hello>
    </div>
 </oj-greet-hello-parent>
</div>An <oj-greet-hello> custom element ends up in
      the live DOM. 
                  
VComponent Properties
Properties are read-only arguments of a VComponent class that you pass into an instance of the VComponent.
Properties that you declare may also be passed to web components as HTML attributes. Essentially, the properties of a VComponent API component module are like function arguments in JSX and attributes in HTML usages.
Declare VComponent Properties
You declare a VComponent property through a type alias that is, by
                convention, named Props. 
                     
Each field in the type represents a single public component property. A
                        field specifies the property's name, type, and whether the value for the
                        field is optional or required. Default values are specified in the
                                static defaultProps field on the component
                        class.
                     
In the following example, we declare a single property
                                (preferredGreeting) of type string. TypeScript’s
                        optional indicator (?) identifies it as an optional
                        property, and the default value of Hello is specified in
                        the static defaultProps field.
                     
One subtle requirement that may be easy to miss: to associate the
                        properties class with the VComponent implementation, you need to specify the
                        class as the value of the VComponent's first type parameter (export
                                class WithProps extends
                                Component<ExtendGlobalProps<Props>>). 
                     
import { customElement, ExtendGlobalProps } from "ojs/ojvcomponent";
import { h, Component, ComponentChild } from "preact";
type Props = {
  preferredGreeting?: string;
};
/**
 * @ojmetadata pack "oj-greet"
 * ...
 */
@customElement("oj-greet-with-props")
export class WithProps extends Component<ExtendGlobalProps<Props>> {
  static defaultProps: Partial<Props> = {
    preferredGreeting: "Hello",
  };
  render(props: Props): ComponentChild {
    return <p>{props.preferredGreeting}, World!</p>;
  }
}Reference Properties in JSX
To work with the properties, VComponent requires that you first associate the property class with the VComponent instance implementation.
import { customElement, ExtendGlobalProps } from "ojs/ojvcomponent";
import { h, Component, ComponentChild } from "preact";
type Props = {
  preferredGreeting?: string;
};
/**
 * @ojmetadata pack "oj-greet"
 */
@customElement("oj-greet-with-props")
export class GreetWithProps extends Component<ExtendGlobalProps<Props>> {
  static defaultProps: Partial<Props> = {
    preferredGreeting: "Hello from oj-greet-with-props!"
  };
  render(props: Readonly<Props>): ComponentChild {
    return <p>{props.preferredGreeting}</p>;
  }
}
Access Properties
You can access declared properties in the VComponent API component
                        implementation through a special object: this.props. An
                        example of this usage can be found at line 12, where the
                                this.props field extracts the value of the
                                preferredGreeting property into the variable
                                greeting. This variable subsequently influences the
                        state of the rendered virtual DOM tree, where the value of the
                                preferredGreeting property gets embedded into the
                        virtual DOM at line 16.
                     
One point to keep in mind is that the property values in
                                this.props are always defined by the consumer of
                        the VComponent API component. In the HTML case, this.props
                        is populated based on attribute/property values specified on the custom
                        element by the application. In the case where the VComponent API component
                        is used within a parent VComponent API component, the property values are
                        provided by the parent component. A VComponent API component implementation
                        can read these property values, but must never mutate the
                                this.props object.
                     
Reference Properties of a Child Component in JSX
VComponent API custom elements can also be embedded inside of other parent VComponent API custom elements.
As was described in Nest VComponents, a VComponent API component parent can refer to a child using the VComponent API component’s implementation class name directly. Inside of JSX, always specify component properties using their camelCase property names.
Here is an example of a VComponent,
                                GreetWithPropsParent, that demonstrates this:
                     
import { h, Component } from "preact";
import { customElement, ExtendGlobalProps } from "ojs/ojvcomponent";
import { GreetWithProps } from "oj-greet/with-props/loader";
/**
 * @ojmetadata pack "oj-greet"
 * ...
 */
@customElement("oj-greet-with-props-parent")
export class GreetWithPropsParent extends Component<ExtendGlobalProps<Props>> {
  render() {
    return (
      <div>
        <GreetWithProps preferredGreeting="Hola" />
      </div>
    );
  }
}Note how the sample uses the preferredGreeting
                        property name, not the preferred-greeting attribute
                        name.
                     
Type-Checking Support
Although the use of different naming conventions between HTML and JSX markup appears confusing at first, it is important to use only property names within JSX to maintain type checking.
When specifying a JSX element like this:
<oj-greet-with-props preferredGreeting="Hey there"/>Or this:
<GreetWithProps preferredGreeting="Hola"/>The properties on each JSX element populate a props object that
                        eventually ends up populating the child component's
                                this.props field. The type of this
                                props object is based on the child VComponent API
                        component instance's props type parameter. So using the
                        property names as declared by the VComponent API component instance's
                        property type, ensures type checking (and catching errors) happens in the
                        parent component's JSX.
                     
Global HTML Attributes
The naming convention of camelCase supports referencing component properties
                from within JSX. Ideally, this same convention can work for global HTML attributes,
                such as id or tabIndex. However, not all global
                HTML attributes are exposed as properties. For example, aria- and
                        data- attributes do not have property
                equivalents.
                     
This leads to the following rules for working with global HTML properties/attributes:
- If the global HTML attribute is available as a property, use the property name.
- If the global HTML attribute is not available as a property, use the attribute name.
In many cases, global HTML attribute names will be identical to the property name
                        (such as id, title, and
                                style). However, there are some cases where the
                        attribute and property name differ, or where the property name requires a
                        specific case-folding. For example, since attributes are case insensitive,
                        HTML allows any capitalization of the tabindex attribute.
                        However, JSX requires that you use the actual property name
                                tabIndex:
                     
protected render() {
  // While "tabindex" is a valid way to specify the tab index
  // in an HTML document, in JSX, the property name "tabIndex"
  // must be used.
  return <div tabIndex="0" />
 }
There is one exception to the rule that governs property name
                        references. Although the property name for specifying style classes is
                                className, this name is not commonly known.
                                VComponent allows use of the more familiar
                        attribute name class:
                     
protected render() {
  // Use "class" instead of "className"
  return <div class="awesome-class" />
}Children and Slot Content
In addition to exposing properties, components can also allow children to be passed in. With VComponent API custom elements, children are specified in one of two ways:
- As direct children, with no slotattribute. This is also known as the default slot.
- As a named slot, with the name set through the
                                                slotattribute.
Components can leverage both of these approaches. For example, the
                        following oj-c-collapsible element is configured both with
                        default slot content, as well as content in the header
                        named slot: 
                  
<oj-c-collapsible>
  <h3 slot='header'>This is named slot content</h3>
     <span>This is default slot content</span>
    </oj-c-collapsible>The VComponent API supports authoring of custom elements that expose default slots, named slots, or both.
Default Slots
VComponent API favors the use of code constructs over external metadata for defining a component’s public API. There is no need to declare a VComponent API custom element children/slot contract through JSON metadata; instead default slots are added by writing code.
The children/slot contract for the VComponent API custom element is defined
                        by adding fields to a Props class. In particular, you
                        indicate that a component can accept default slot content by declaring a
                        children property of type ComponentChildren: 
                     
import { h, Component, ComponentChildren } from 'preact';
     type Props = {
      preferredGreeting?: string;
      children?: ComponentChildren;
    }And, you can then associate the Props class with
                        the VComponent through the Props type parameter: 
                     
@customElement(‘oj-greet-with-children’)
   export class GreetWithChildren extends Component<ExtendGlobal<Props>> {
  }Once this is done, any default slot children will be made available
                        to the VComponent API component implementation through
                                props.children. This is true regardless of whether
                        the component implementation is used as a custom element within an HTML
                        document, a custom element within JSX, or through the VComponent component
                        implementation class within JSX. 
                     
The VComponent component implementation is free to place the default
                        slot children anywhere within the component’s virtual DOM tree. For example,
                        a VComponent API button likely would place these children inside of an HTML
                                <button> element: 
                     
protected render() {
      return <button> { props.children } </button>;
 }Named Slots
Like the default slot, named slots are also declared as fields on the
      props class.
                     
Named slot declarations must adhere to two conventions:
- The named slot field must use the Slottype.
- The name of the field must match the slot name.
The declaration for a slot named startIcon looks
      like this: 
                     
import { customElement, ExtendGlobalProps } from "ojs/ojvcomponent";
import { h, Component, ComponentChild } from "preact";
import "oj-c/avatar";
import { GreetWithChildren } from 'oj-greet/with-children/loader'
type Props = {
    startIcon?: Slot;
}
/**
 * @ojmetadata pack "oj-greet"
 * @ojmetadata dependencies {
 *   "oj-greet-with-children": "^1.0.0"
 * }
 */
@customElement('oj-greet-with-children-parent')
export class GreetWithChildrenParent extends Component<ExtendGlobalProps<Props>> {
  render() {
    return (
      <div>
        <p>This child is rendered as a VComponent class:</p>
        <GreetWithChildren startIcon={<oj-c-avatar initials="HW" size="xs" />}>
          World
          </GreetWithChildren>
      </div>
    );
  }When the VComponent is referenced through its class, named slot content is provided by specifying virtual DOM nodes directly as values for slot properties, as demonstrated in the following parent VComponent.
import { customElement, ExtendGlobalProps } from "ojs/ojvcomponent";
import { h, Component, ComponentChild } from "preact";
import "oj-c/avatar";
import { GreetWithChildren } from "oj-greet/with-children/loader";
/**
 * @ojmetadata pack "oj-greet"
 * @ojmetadata dependencies {
 *   "oj-greet-with-children": "^1.0.0"
 * }
 */
@customElement("oj-greet-with-children-parent")
export class GreetWithChildrenParent extends Component<ExtendGlobalProps<Props>> {
  render() {
    return (
      <div>
        <p>This child is rendered as a VComponent class:</p>
        <GreetWithChildren startIcon={<oj-c-avatar initials="HW" size="xs" />}>
          World
        </GreetWithChildren>
      </div>
    );
  }
}Refresh Custom Elements with Dynamic Children and Slot Content
Virtual DOM architecture provides a Remounter component to make sure that a custom element re-renders correctly when its children or slot content changes dynamically.
A concrete example demonstrates when to use the Remounter component. In the
      following example, the oj-c-button element’s display of a start icon depends
      on the value of the showStartIcon property. We want to ensure that the
        oj-c-button renders the start icon in the correct location when the
        showStartIcon property changes.
                     
type Props = {
  showStartIcon?: boolean
}
 
function ButtonStartIconSometimes(props: Props) {
  return (
    <oj-c-button label="Click Me!">
      { props.showStartIcon && <span slot="startIcon" class="some-icon-class" /> }
    </oj-c-button>
  )
}A more appropriate approach for the VDOM architecture is to assign a unique key that reflects the oj-c-button
                        showStartIcon property’s different states. In the above example, assigning a
      unique key is straightforward, as there are only two possible states. However, in some cases
      producing a unique key for all possible children states is more challenging. For these more
      challenging cases, use the Remounter component to wrap a single custom element child and
      generate a unique key based on the current set of children. The following revised example
      demonstrates how you use the Remounter component to ensure that the
        oj-c-button re-renders with the start icon in the correct location:
                     
import { h } from 'preact';
import { Remounter } from 'ojs/ojvcomponent-remounter';
import "oj-c/button";
 
type Props = {
  showStartIcon?: boolean
}
 
function ButtonStartIconSometimes(props: Props) {
  return (
    <Remounter>
      <oj-c-button label="Click Me!">
        { props.showStartIcon && <span slot="startIcon">Start Icon</span> }
      </oj-c-button>
    </Remounter>
  )
}
 
export { ButtonStartIconSometimes };Note that you only need to use the Remounter component when configuring custom elements where the number of types of children change across renders.
Template Slots
In addition to simple, non-contextual slots, JET components also support slots that can receive context. These are called template slots.
Template slots are typically found in collection components that iterate
                        over a data set, stamping out content for each item or row. For example,
                                <oj-list-view> exposes an
                                itemTemplate slot that controls how the content of
                        each list item renders. Within HTML, a template element
                        with a slot attribute specifies a template slot, as in the
                        following example: 
                  
<oj-list-view  data="[[ items ]]">
  <template slot="itemTemplate" data-oj-as="item">
    <div>
      <oj-bind-text value="[[item.data.value]]"></oj-bind-text>
    </div>
  </template>
</oj-list-view>VComponent API custom elements can also expose template slots. To understand how this works, consider a greeting component that takes an array of names to greet and renders a greeting for each name. The property declaration might look like this:
class Props {
  names: Array<string>
}
It is possible to just iterate over the names and render the content for each item:
protected render() {
  return (
    <div>
      { this.props.names.map(name => <div>Hello, {name}!</div>) }
    </div>
  );
}  
With the above approach, the decision about how to render each greeting would be hardcoded into the component implementation. Instead, this can be made more flexible by exposing a template slot that allows the app to customize how each greeting is rendered.
Similar to simple, non-contextual slots, template slots are declared
                        as properties with a well known type: TemplateSlot. Let's
                        take a look at this type alias:
                  
export type TemplateSlot<Data> = (data: Data) => Slot;The TemplateSlot is a generic function type that
                        accepts a single argument: the data to use when rendering a specific
                        instance of the template. The type of this data is defined through the
                                Data type parameter, which must be specified when
                        the TemplateSlot property is declared. 
                  
greetingTemplate slot:
                        oj-greet/hello-many.tsx:    
1    import { h, Component } from 'preact';
2    import { customElement, ExtendGlobalProps, TemplateSlot } from 'ojs/ojvcomponent';
3
4    export type GreetingContext = {
5      name: string;
6    }
7
8    type Props = {
9      names: Array<string>;
10      greetingTemplate?: TemplateSlot<GreetingContext>;
11    }
12
13    /**
14     * @ojmetadata pack "oj-greet"
15     */
16    @customElement('oj-greet-hello-many')
17    export class GreetHelloMany extends Component<ExtendGlobalProps<Props>> {
18      render() {
19        return (
20          <div>
21            {
22              this.props.names.map((name) => {
23                return this.props.greetingTemplate?.({ name }) ||
24                         <div>Hello, { name }!</div>
25              })
26            }
27          </div>
28        );
29      }
30    }This sample declares the greetingTemplate slot at
                        line 10. Note that the Data type parameter must be an
                        object type. The sample uses the GreetingContext type as
                        declared at line 4. 
                  
The template slot (if non-null) is invoked for each item in the names
                        array at line 23. The sample passes in an object of type
                                GreetingContext with each invocation.
                        Alternatively, if no slot is provided, it returns the default content at
                        line 24. 
                  
Provide Template Slot Content within HTML
After you expose the template slot in the VComponent implementation, then
    within HTML, you provide slot content the same as any JET custom element: by specifying a
      <template> element with a slot attribute. Within the
      template element, you use JET binding expressions and elements to render the
    desired greeting: 
                     
<oj-greet-hello-many names="[[ ['Joel', 'Mike', 'Jonah' ] ]]">
  <template slot="greetingTemplate" data-oj-as="greeting">
    <div>
      Hi, <oj-bind-text value="[[ greeting.name ]]"></oj-bind-text>!
    </div>
  </template>
</oj-greet-hello-many>    
Template Slots in JSX
When rendering a component in JSX through its VComponent API component class
                (such as <GreetHelloMany>), template slots are passed in as
                functions that adhere to the TemplateSlot contract. This means you must implement
                template slots as functions that take some data, and return either a single virtual
                DOM node or an array of nodes. 
                     
This might look something like:
<GreetHelloMany names={names}
    greetingTemplate={ (data) => <div>Hello, { data.name}!</div> } Of course, you can also reference the GreetHelloMany component
                        by using its custom element tag name.
                     
As the previous HTML sample shows, custom element template slots are specified
                        using JET binding expressions (such as value="[[ greeting.name
                                ]]") and elements (such as oj-bind-text)
                        inside a <template> element. While this approach fits in
                        nicely within an HTML document alongside other content that is configured
                        using JET bindings, it doesn't fit well inside of a JSX render function.
                        Within JSX, rather than configuring template slot content using JET binding
                        syntax, JSX syntax is preferred. 
                     
To allow template slot content to be specified using JSX-based render
                        functions, VComponent API introduces a special, VComponent-specific property
                        on the <template> element: the
                                render property. The type of this property is
                                TemplateSlot. 
                     
This allows us to configure template slots on custom elements using JSX-based render functions, for example:
<oj-greet-hello-many names={names}>
    <template slot="greetingTemplate"
       render={ (data) => <div>Hello, { data.name}!</div> }/>
  </oj-greet-hello-many>Note that you still need to specify a
                                <template> element with a
                                slot attribute. Rather than configuring the
                        template slot with JET's binding syntax, instead specify a
                                TemplateSlot function that returns virtual DOM. 
                     
A more complete parent component shows this:
oj-greet/hello-many-parent.tsx:
    
1     import { h, Component } from 'preact';
2     import { customElement, GlobalProps } from 'ojs/ojvcomponent';
3     import "oj-c/avatar";
4     import { GreetHelloMany, GreetingContext } from 'oj-greet/hello-many/loader';
5
6     /**
7      * @ojmetadata pack "oj-greet"
8      */
9     @customElement('oj-greet-hello-many-parent')
10    export class GreetHelloManyParent extends Component<ExtendGlobalProps<Props>> {
11      render() {
12
13        const names = [ 'Joel', 'Mike', 'Jonah' ];
14
15        return (
16          <div>
17            <p>This child is rendered as a custom element:</p>
18            <oj-greet-hello-many names={names}>
19              <template slot="greetingTemplate" render={ this.renderGreeting }/>
20           </oj-greet-hello-many>
21           <br />
22            <p>This child is rendered as a VComponent class:</p>
23            <GreetHelloMany names={names} greetingTemplate={ this.renderGreeting }/>
24          </div>
25        );
26      }
27
28      private renderGreeting(data: GreetingContext) {
29        const name = data.name;
30        const firstInitial = name.charAt(0);
31        const greeting = name.length < 5 ? 'Hey' : 'Hi';
32
33        return (
34          <p class="centerAlignVertical">
35            <oj-c-avatar size="xxs" initials={ firstInitial } />
36            {greeting}, { name }!
37          </p>
38        );
39      }
40    }In the above example, the render property provides
                        JSX-based content for the <oj-greet-hello-many>
                        custom element, which happens to be implemented with VComponent API.
                        However, this property can also be used when configuring template slot
                        content for any JET component. For example, you can configure the
                                <oj-list-view> itemTemplate slot as follows,
                        even though this custom element is not implemented with the VComponent API: 
                     
protected render() {
  <oj-list-view  data={ this.props.items }>
    <template slot="itemTemplate"
     render={ ( item ) => { return <div>{ item.data.value }</div> } } />
  </oj-list-view>  Understand Events and Actions
Two terms seem interchangeable at first, but in VComponent API,
                        event and action have two distinct
                meanings: 
                  
- eventspecifically refers to DOM Events that are dispatched by calling to- dispatchEvent.
- actionis a higher-level abstraction for event-like APIs, which may or may not actually involve dispatching an Event at the DOM level.
This distinction arises due to the fact that VComponent API component instances can be used in two ways:
- As a custom element, using the string tag name.
- As a VComponent API component, using the component implementation class.
When a VComponent API component is used as a custom element, invoking an action results in the dispatch of a DOM event.
However, when referencing a VComponent API component through its implementation class, no DOM event is created or dispatched. Instead, the action callback provided by the parent component is invoked directly.
To support these different usage models, a higher level abstraction than DOM events is required. VComponent API actions provide that abstraction.
For simplicity, usage of the term action refers to the general behavior by which VComponent API component instances notify the outside world of activity. Whereas usage of the term event is reserved specifically for DOM events that are dispatched by custom (or plain old HTML) elements.
Listeners
Event listeners are functions that take a DOM event
                and have no return value. VComponents can listen for and respond to standard HTML
                events plus custom events on custom elements.
                  
The naming convention that you use for the event listener differs depending on whether you listen for a standard HTML event or custom event.
For standard HTML events, such as click,
                        change, mouseover, add a property name
                        that uses the naming convention:
                                on<UpperCaseStandardEventName>. The following
                        example shows you how to register an event listener for a
                                click event.
                  
render() {
  return <div onClick={this._handleClick}>Click Me!</div>
}For custom events such as <oj-c-button>'s
                                ojAction event, use the
                                on<customEventName> naming convention. The
                        following example shows you how to register an event listener for an
                                ojAction event.
                  
protected render() {
   return <oj-c-button label="Click Me!" onojAction={this._handleAction}>
                <oj-c-button>
  }Note how the first character of the custom event name is not capitalized compared
                        to the standard event (onojAction versus
                                onClick).
                  
There are a number of ways to enable event listener access to a VComponent
                        instance. You can, for example, explicitly call bind(this)
                        on the event listener function or, alternatively, use one of the following
                        approaches: 
                  
- Define and use an arrow function inline in the render()method.
- A class method can be bound and saved away in the constructor.
- An arrow function can be declared and stored in a class field.
The last two options avoid creating a new function on each call to the
                                render() method and, by using the same function
                        instance across all render() methods, avoid virtual DOM
                        diffs that cause DOM addEventListener and
                                removeEventListener calls on each call to the
                                render() method. The class field approach
                                (private _handleEvent), demonstrated in the
                        following example, is slightly more concise.
                  
import { customElement, ExtendGlobalProps } from "ojs/ojvcomponent";
import { Component } from "preact";
import "oj-c/button";
type Props = Readonly<{
  message?: string;
}>;
@customElement("oj-greet-with-listeners")
export class GreetWithListeners extends Component<ExtendGlobalProps<Props>> {
  render() {
    return (
      <div>
        <div onClick={this._handleEvent}>
          <p>Hello, World!</p>
        </div>
        <oj-c-button label="Click Me!" onojAction={this._handleEvent}></oj-c-button>
      </div>
    );
  }
  private _handleEvent = (event: Event) => {
    console.log(`Received ${event.type} event`);
  };
}Actions
We need to make a distinction between event and
                        action because of the different behavior that occurs when
                you use a VComponent as a custom element or a component class. 
                  
In a VComponent, an event refers to DOM Events that
                        are dispatched through a call to dispatchEvent while
                        an action is a higher-level abstraction for event-like
                        APIs, which may or may not actually involve dispatching an event at the DOM
                        level. When you use a VComponent as a custom element, invoking an action
                        results in the dispatch of a DOM event while no DOM event is created or
                        dispatched when you use the component class. Instead, for the latter case,
                        the action callback provided by the parent VComponet is invoked directly.
                        VComponent action provides a higher-level abstraction than DOM events that
                        is needed to support these two usage models. 
                  
Declare Actions
You need to be able to define actions to dispatch in response to VComponents-defined events.
The following example demonstrates how you add a
                                responseDetected event action to a VComponent that
                        is dispatched in response to a user action, such as a click. You add a field
                        to the Props class. The field that you add must follow the
                        standard event listener property naming convention
                                (on<UpperCaseEventName>) and it must use the
                        Action type defined by the ojs/ojvcomponent module.
                     
import { customElement, ExtendGlobalProps, Action } from 'ojs/ojvcomponent';
    type Props = {
      preferredGreeting?: string;
      // This is an action declaration:
      onResponseDetected?: Action;
    }
Dispatch Actions
VComponent’s Action type is a callback function.
export type Action<Detail extends object = {}> = (detail?: Detail) => void;To dispatch an action, the VComponent invokes the Action-typed property as a
                        function. Actions are typically dispatched in response to some underlying
                        event. In the following example, the VComponent instance dispatches the
                                responseDetected action in response to a click:
                     
private _handleClick = (event: MouseEvent) => {
   this.props.onResponseDetected?.();
 }Note that the consumer of the component is not required to provide a value for
                                onResponseDetected. As a result, we need to guard
                        against a null value for this.props.onResponseDetected. To
                        do this, we can take advantage of TypeScript's support for the optional
                        chaining operator (?.). This allows us to invoke the action
                        callback if it is provided but short-circuit if not, without the need for a
                        more verbose null check.
                     
Respond to Actions
The response to an invoked action depends on usage.
If the VComponent is used as a custom element (either within an HTML
                        document or within JSX in a parent VComponent), the VComponent framework
                        creates a DOM CustomEvent that it dispatches through the custom element. The
                                event type is derived from the
                        name of the Action property by removing the on prefix and lower casing the
                        first letter. For example, the onResponseDetected action
                        results in the dispatch of a responseDetected DOM event
                        type.
                     
If the VComponent is being used by a parent VComponent and is referenced by its class name rather than the custom element name, no DOM event is created. If the parent VComponent provides a value for the Action property, this is invoked directly.
In JSX, action callbacks are always specified using the Action property name. This is true regardless of whether the parent references the child VComponent by its custom element name of class:
<p>This child is rendered as a custom element:</p>
<oj-greet-with-actions onresponseDetected={this.handleResponse}/>
<p>This child is rendered as a VComponent class:</p>
<GreetWithActions onResponseDetected={this.handleResponse}
However, if the custom element lives within an HTML document, event listeners are typically registered with JET's event binding syntax. This might look something like:
<oj-greet-with-actions on-response-detected="[[ expressionPointingToEventHandler ]]">…      
            </oj-greet-with-actions>
Though it is also possible to call the DOM addEventListener API directly.
Action Payloads
You may have noticed that Action is a generic type with a Detail type parameter. The Detail type parameter is useful when the action needs to deliver additional information beyond just the action type.
For example, our Greeting component may want to include a flag along with the
                                responseDetected action to indicate urgency. This
                        would be declared using the Detail type parameter as follows:
                     
type Props = {
  preferredGreeting?: string;
  onResponseDetected?: Action<{
  urgent: boolean;
 }>;
}
When invoking the action, the detail payload is passed in as an argument to the action callback, as in the following example:
private _handleClick = (event: MouseEvent) => {
   // Pass in a detail payload.  Determine urgency based on
   // number of clicks.
   this.props.onResponseDetected?.({
     urgent: event.detail > 1
  });
}
The Detail type parameter can also be specified using a type
                        alias, as in the following example:
                     
export type ResponseDetectedDetail = {
  urgent: boolean;
};
type Props = {
  preferredGreeting?: string;
  onResponseDetected?: Action<ResponseDetectedDetail>;
};
 On the consuming side, there is one subtlely in how the detail payloads are accessed. In the custom element case, the action callback is registered as a DOM EventListener. That is, when using the following form:
<oj-greet-with-actions onresponseDetected={this.handleEventResponse}/>
The callback acts as a true DOM event listener, and, as such, receives a single
                                event argument of type
                                CustomEvent<Detail>. However, when using the
                        VComponent class form:
                     
<GreetWithActions onResponseDetected={this.handleActionResponse}/>
The callback will again receive a single argument, but of type
                                Detail rather than
                                CustomEvent<Detail>. The difference between
                        custom element usage and VComponent class usage is admittedly non-obvious.
                        Our recommendation for you is to use the VComponent class form when that is
                        available. 
                     
Manage State Properties
Components may track internal state that is not reflected through their properties. VComponent API provides a state mechanism to support this.
A VComponent API custom element can determine what content to render based exclusively on properties that are passed into the component by the parent component. This is useful for VComponent API components that are fully controlled by the parent component. However, some components may benefit from their own internal state properties that are not passed in, but rather exist locally in the component and render content based on state changes. VComponent authors can leverage Preact's local state mechanism for these cases.
Declare State
The process of declaring local state fields is very similar to the way that you define VComponent API properties. In both cases, start by declaring a type.
This example updates our component to display a goodbye message in response to the user clicking a Done button. The sample uses a boolean local state field done to track whether this state is reached.
type State = {
   done: boolean;
   // Other state fields go here
};As with the properties type, we associate the state type with the VComponent implementation by leveraging generics. The VComponent API component class exposes two type parameters:
- Props: the first type parameter specifies the properties object type
- State: the second type parameter specifies the State object type
The new declaration with both type parameters looks like this:
export class GreetWithState extends Component<ExtendGlobalProps<Props>, State> {
    }Each VComponent API component has access to the local state through the
                                this.state field. This field must be initialized at
                        construction time. Initialization can be done in one of two ways.
                     
If your VComponent API component instance has a constructor, initialize local state there:
export class GreetWithState extends Component<ExtendGlobalProps<Props>, State> {
      constructor() {
        this.state = {
          done: false
        };
        // Do other construction-time work here
      }Alternatively, TypeScript supports inline initialization of class fields. This slightly more compact form works well if you do not otherwise need a constructor:
export class GreetWithState extends Component<ExtendGlobalProps<Props>, State> {
      state = {
        done: false
      };
      // Component implementation goes here
   }Once local state has been declared and initialized, it can be referenced from within the render function to adjust how the virtual DOM content is rendered. For example, this sample shows our greeting component rendering a different message when the conversation is "done":
render(props: Props, state: State) {
        // Derive greeting message off of the "done" state field
        const greeting = state.done ?
          'Goodbye' :
          props.preferredGreeting;
        return (
          <div onClick={this._handleClick}>
            <p>{greeting}, World!</p>
          </div>
        );
      }Update State
Updating local state has an important side effect: it triggers re-rendering of the component.
Note that this.state is declared as a
                                Readonly type. Other than the initial assignment to
                                this.state in the constructor (or class field
                        initialization), neither this.state nor fields on
                                this.state should be mutated directly.
                     
Instead, state updates are performed by calling the Preact
      Component's setState method. This method has two forms. The first form simply
      takes an object representing the new state. For example, we can update our
        done state field by calling: 
                     
this.setState({ done: true });This call queues the state update, which will trigger an (asynchronous) re-render of the component with the new state.
Although our sample only has a single state field, components can
      have an arbitrary number of fields. The setState() method accepts sparsely
      populated objects; you are not required to provide values for all state fields. Any new values
      that are provided will be merged on top of the current state.
                     
The following version of our greeting component has been modified to use a numeric, enum-based counter to track how engaged the end user is. After three clicks, the greeting component ends the conversation.
oj-greet/with-state/with-state.tsx:
import { h, Component } from "preact";
import { customElement, ExtendGlobalProps } from "ojs/ojvcomponent";
type Props = {
  preferredGreeting?: string;
};
enum EngagementLevel {
  Interested,
  Bored,
  Impatient,
  Done,
}
type State = {
  engagement: EngagementLevel;
};
/**
 * @ojmetadata pack "oj-greet"
 */
@customElement("oj-greet-with-state")
export class GreetWithState extends Component<ExtendGlobalProps<Props>, State> {
  state = {
    engagement: EngagementLevel.Interested,
  };
  render() {
    const greeting = this.getGreeting();
    return (
      <div onClick={this._handleClick}>
        <p>{greeting}, World!</p>
      </div>
    );
  }
  private getGreeting() {
    let greeting;
    switch (this.state.engagement) {
      case EngagementLevel.Bored:
        greeting = "Okay";
        break;
      case EngagementLevel.Impatient:
        greeting = "Whatever";
        break;
      case EngagementLevel.Done:
        greeting = "Later";
        break;
      default:
        greeting = this.props.preferredGreeting;
        break;
    }
    return greeting;
  }
  private _handleClick = (event: MouseEvent) => {
    this.setState((state: Readonly<State>) => {
      // Once we have reached the Done state, we return null
      // to indicate that no state update is needed.
      return state.engagement === EngagementLevel.Done
        ? null
        : { engagement: state.engagement + 1 };
    });
  };
  static defaultProps: Partial<Props> = {
    preferredGreeting: "Hello",
  };
}
Understand the State Mechanism
One potential problem with the object-based form of
                        setState() arises when the new value for a state field is
                derived from the previous value. Given the asynchronous nature of this method,
                simply inspecting this.state may not be sufficient to determine
                what the next value should be. If there is an outstanding call to
                        setState() that has not yet been fully processed,
                        this.state might not reflect the pending
                update.
                     
To better support cases where a state field's next value is dependent on the
                        previous value, setState() supports a callback form. Rather
                        than passing in an object representing the new state, callers pass in a
                        function that takes two arguments: the current state and properties. This
                        callback function can inspect the state and props and return one of the
                        following values:
                     
- A sparsely populated object representing any state updates to apply
                                        
                           or: 
- null, if no state updates are required.
When multiple calls to setState() are issued, the
                        state updates are chained. That is, the results of one call (whether object
                        or callback form) are fed into the subsequent callback. This ensures the
                        callback always sees the most up to date values, and that it can use this
                        information to correctly produce the next value.
                     
Reference Child VComponents by Value
You can reference a VComponent child component from within JSX in two ways:
// Use intrinsic element name
function Parent() {
   return <some-comp />;
}
// Use value-based element to reference 
// the VComponent class or function value
function Parent() {
   return <SomeComp />
}We recommend that, whenever possible, you use the value-based element because:
- It is slightly more efficient as we are able to do more rendering in virtual DOM even before the VComponent's DOM element is created.
- It provides a more React/Preact-centric approach to use certain APIs, such as slots and listeners. (More on this below.)
- When working within a single project (that is, where both the VComponent and consuming code is in the same project), you have access to the VComponent class type information directly in your project source. You are not dependent on a build to produce a type definition for the class.
Elaborating on point 2, when you reference a VComponent through its intrinsic element name, you are limited to using this syntax for slots, as in the following example:
function Parent() {
 <some-comp>
  // This is a plain slot:
  <img src="foo.png" slot="startIcon" />
  // This is a template slot:
  <template slot="itemTemplate render={ renderItem } />
 </some-comp>
}With a value-based element approach, you can achieve the same outcome more concisely, and in a form that is more familiar to app developers with a React/Preact background:
function Parent() {
   <SomeComp startIcon={ <img src="foo.png" } itemTemplate={ renderItem }>
}References to event listeners when using the value-based element form also aligns better with what a React/Preact developer would expect. For example, for intrinsic elements, we need to follow Preact's custom event naming conventions. So, for an event name of someCustomEvent, we end up with this listener prop:
function Parent() {
 <some-comp onsomeCustomEvent={ handleSomeCustomEvent }>
}When referencing the value-based element, this is:
function Parent() {
 <SomeComp onSomeCustomEvent={ handleSomeCustomEvent }>
}To conclude, use the value-based element whenever possible. For cases where you need to interact directly with the DOM element, use the intrinsic element form and then obtain a reference to it, as in this example:
function Parent() {
    const someRef = useRef(null);
    return <some-comp ref={ someRef } />;
}