HCL Commerce Version 9.1.11.0 or later

Creating a custom widget

A widget is a frame that displays a specific type of store content, such as ads, product recommendations, or navigational links. This document explains the steps to be followed to create a custom widget.

About this task

In this document, a new widget called example-widget is created with two properties given below:
Table 1. Widget Properties
Property Description
Title This text input is the title that is given to the widget.
Height The radio buttons are given with two options, Tall and Short.

Procedure

  1. Add a widget definition in the tooling.
    • Adding a widget definition in the tooling depends on the widget definitions given in the PLWIDGETDEF table. Before making any change in the tooling user interface, widget definitions must be inserted into the table. In this example, a widget definition for store ID (STOREENT) 0 is added:
      INSERT INTO PLWIDGETDEF(PLWIDGETDEF_ID, STOREENT_ID, IDENTIFIER, VENDOR, WIDGETTYPE, JSPPATH, DEFINITIONXML, STATE, CREATEDATE, LASTUPDATE) 
      VALUES(-100000, 0, 'example_widget', 'hcl', 1, '', '<Definition> </Definition>', 1, CURRENT DATE, CURRENT DATE);
      Note: The value for PLWIDGETDEF_ID must be unique. Do not pick a value that is close to the default values. This will prevent conflicts with the future widget definitions provided by HCL Commerce.
      Query: SELECT * FROM plwidgetdef ORDER BY PLWIDGETDEF_ID
      Table 2. PLWIDGETDEF table
      PLWIDGETDEF_ID STOREENT_ID IDENTIFIER UI_OBJECT_NAME VENDOR WIDGETTYPE DEFENITIONXML
      1504 11501 'SubCategoryPageContainer' NULL 'ibm' 2 'Container/SubCategoryPageContainer.jsp'
      1505 11501 'SearchLandingPageContainer' NULL 'ibm' 2 'Container/LandingPageContainer.jsp'
      1506 11501 'StaticContentContainer' NULL 'ibm' 2 'Container/StaticContentContainer.jsp'
      1507 11501 'SearchResultPageContainer' NULL 'ibm' 2 'Container/SubCategoryPageContainerWithTabs.jsp'
      1508 11501 'ProductPageContainer' NULL 'ibm' 2 'Container/ProductPageContainer.jsp'
      1509 11501 'ProductPageContainer(2)' NULL 'ibm' 2 'Container/ProductPageContainerFullWidth.jsp'
      -100000 11501 'example_widget' NULL hcl' 1 ''
  2. Add the created PLWIDGETDEF_ID to the PLSTOREWIDGET table for all the stores you want to enable this widget for. In this example, enabling the widget for EmeraldCAS store. The store ID is 12501).

    INSERT INTO WCS.PLSTOREWIDGET 
    
    (PLSTOREWIDGET_ID, STOREENT_ID, PLWIDGETDEF_ID, STATE, DEFINITIONXML, OPTCOUNTER) 
    
    VALUES(-100000, 12501, -100000, 1, NULL, 0);
  3. After updating the database with a new widget definition (ID - 100000 ), update the tooling user interface. Two components handle the business logic of widget definitions and their management:
    • Layout widget details
    • Layout widget, which is under the page-builder components directory. This directory can be found at commerce-tooling/src/app/features/page-builder/components.
  4. Update the layout-widgets.json file in the Layout-widget component.
    The Layout-widget component is responsible for listing widgets in the Layout tab of the Page Composer tool. The Layout-widget component consists of the following files:
    • layout-widgets.component.ts
    • layout-widgets.component.html
    • layout-widgets.component.scss
    • layout-widgets.json
  5. Add the following lines of code in the layout-widgets.json file.
    {
    "id": "-100000",
    "name": "example-widget",
    "title": "PAGE_BUILDER.EXAMPLE_WIDGET",
    "description": "PAGE_BUILDER.EXAMPLE_WIDGET_DESCRIPTION",
    "properties": [
    {
    "name": "title",
    "value": "Title!"
    },
    {
    "name": "height",
    "value": "short"
    }
  6. Save and close the file.
  7. After updating the code, add new translation strings in the translation files. To display the Name and Description fields accurately, update the en-US.json file available at the commerce-tooling/assets/i18 location:
    "EXAMPLE_WIDGET": "Example Widget",
    "EXAMPLE_WIDGET_DESCRIPTION": "Example widget description!",
    "TITLE": "Title",
    "HEIGHT": "Height",
    "SHORT": "Short",
    "TALL": "Tall",
    "INVALID_TITLE": "Invalid title",
  8. Save and close the file. The Example-widget is displayed in the Widgets tab of the Layout section as shown below:
  9. After updating the name and description of the widget, modify the layout-widget-details. Follow Widget properties and content to modify and update the widget properties.
  10. The layout-widget-details is composed of four files:
    • layout-widget-details.json: This file has the widget details defined and listed.
      • Update the layout-widget-details.json file with the following lines of code:
        {
        "id": "-100000",
        "title": "PAGE_BUILDER.EXAMPLE_WIDGET",
        "exampleWidgetType: true
        }
        Note: As it is a custom widget, the example-widget-type flag is added because the parameters it contains are different from the other widgets.
    • layout-widget-details.component.html: This is the template of the layout-widget-details component. Based on the widget type, this file should be updated for the text input and a radiobox input with a flag that toggles custom fields on or off.
      • Update the layout-widget-details.component.html file with the following lines of code:
        <ng-container *ngIf="widgetType.exampleWidgetType">
        <mat-form-field appearance="outline">
        <mat-label>{{'PAGE_BUILDER.TITLE' | translate}}</mat-label>
        <input matInput id="titleInput" formControlName="titleInput" [maxlength]="256" autocomplete="off">
        <mat-error *ngIf="titleInput.errors">
        {{'PAGE_BUILDER.INVALID_TITLE' | translate }}
        </mat-error>
        </mat-form-field>
        <div class="hc-type-label">{{ 'PAGE_BUILDER.HEIGHT' | translate }}</div>
        <mat-radio-group formControlName="height">
        <mat-radio-button value="short">{{'PAGE_BUILDER.SHORT'| translate}}</mat-radio-button>
        <mat-radio-button value="tall">{{'PAGE_BUILDER.TALL'| translate}}</mat-radio-button>
        </mat-radio-group>
        </ng-container>
    • layout-widget-details.component.ts: This file acts as a controller of the component. Following are the few functions that can be managed by updating this file:
      1. Loading the widget details from the layout-widget-details.json file.
      2. Setting the necessary flags.
      3. Controlling the changes made to the widget.
      4. Persisting the changes made to widget.
      • Add two form controls, titleInput and height. Update the file with the following lines of code:
        widgetTypes: Array<any> = (LayoutWidgetTypes as any).default;
        widgetType: any = null;
        widgetForm: FormGroup;
        name: FormControl;
        wrap: FormControl;
        autoSlideshow: FormControl;
        interval: FormControl;
        playDirection: FormControl;
        populationChoice: FormControl;
        emsType: FormControl;
        emsName: FormControl;
        titleInput: FormControl;
        height: FormControl;
        
        @ViewChild("nameInput") nameInput: ElementRef<HTMLInputElement>;
      • Define and initialize the titleInput and height controls. Change the createFormControls() and createForm() methods to define and initialize the controls.
      • Update the file with the following lines of code:
        this.height.setValue(widgetProperties.height);
        if (this.widgetType?.exampleWidgetType) {
        this.titleInput.enable();
        this.height.enable();
        } else {
        this.titleInput.disable();
        this.height.disable();
        }
        private createForm() {
        this.widgetForm = new FormGroup({
        name: this.name,
        wrap: this.wrap,
        autoSlideshow: this.autoSlideshow,
        interval: this.interval,
        playDirection: this.playDirection,
        populationChoice: this.populationChoice,
        emsType: this.emsType,
        emsName: this.emsName,
        titleInput: this.titleInput,
        height: this.height
        });
        }
        }
      • Implement the initial loading logic for custom fields in the initialize() method. Update the file with the following lines of code:
        this.emsName.setValue(widgetProperties.emsName);
        this.titleInput.setValue(widgetProperties.title);
        this.height.setValue(widgetProperties.height);
        if (this.widgetType?.exampleWidgetType) {
        	this.titleInput.enable();
        	this.height.enable();
        } else {
        	this.titleInput.disable();
        	this.height.disable();				
        }
        if (this.widgetType?.selectMarketingSpot && widgetProperties.emsType === "local") {
        	this.emsName.enable();
        } else {
        	this.emsName.disable();
        }
        if (this.widgetType?.carousel) {
        	this.interval.enable();
        } else {
        	this.interval.disable();
        }
        } else {
        	this.name.setValue("");
        	this.wrap.setValue(false);
        	this.autoSlideshow.setValue(false);
        	this.interval.setValue(5000);
        	this.playDirection.setValue("forward");
        	this.populationChoice.setValue(null);
        	this.emsType.setValue(null);
        	this.emsName.setValue("");
        	this.titleInput.setValue("");
        	this.height.setValue(null);
        }
        }
      • Implement the logic for persisting the widget details in the save() method. Update the file with the following lines of code:
        if (this.widgetType?.exampleWidgetType) {
        		this.updateProperty("titleInput", this.titleInput.value);
        		this.updateProperty("height", this.height.value);
        	}
      • Save and close the file. The widget details pane appears in the Widget tab of the Layout section as shown in the image below:

Results

The newly customized Example-Widget is listed in the widget list and can be edited or updated as required.