Adding content to existing pages by using extension points

Every component that makes up a page contains 2 extension points (top and bottom) for inserting custom code into any existing page. Use the differential customization approach to add content to Order Hub existing pages by using these extension points.

Consider using this differential customization approach for your customizations over the customization by overrides approach, as your updates are preserved when Order Hub releases updates to the same pages that you customized. No code merge is required; simply recompile with the monthly DTK code.

Before you begin

Identify the Order Hub page areas that you want to add your content to by using the Ctrl + D keyboard shortcut.
  1. Go to the Order Hub page that you want to add content to.
    For example, the order details page:
    Order details page
  2. Show the extension points available for you to add content to on the page.
    1. Press Ctrl + D to show the available extension points on the page.
      Each page component contains extension points denoted by the following label and naming convention:
      Optional custom component can be placd here. Component name: ComponentName Identifier: ID
      For example, the order details page extension points:
      Order details page extensions
    2. Make note of the identifiers of the extension points that you want to add content to.
    3. Press Ctrl + Shift + D to hide the available extension points on the page.

Then, make your updates to add content to existing pages by using extension points:

Procedure

  1. Identify the extension directive that you will use to insert your customizations. The directive can be found in each component in the source code. Each directive contains a unique ID. These IDs are defined in a constants file in each route application in the packages/src/app/features/component/extension.constants.ts file.
  2. Create a new component in the src-custom folder and add it in NgModule declaration in the app-customization.impl.ts file
  3. Create a new service that extends from ExtensionService from @buc/commmon-components.
  4. In the app-customization.impl.ts file, import the ExtensionModule from @buc/common-components. In the NgModule imports array, provide the directive in the ExtensionModule.forRoots method.
    For example:
    
      ExtensionModule.forRoot([
       {
         id: Unique directive id mentioned in step 1,
         component: Component created in step 2,
         service: Service created in step 3.
       },
    
  5. If the new component has inputs and event emitters, update the new service properties userInputs and userOutputs and implement the createInput and handleOutput methods.

    For example:

    1. Update the userInputs to include the all the @Inputs() properties required by the component. For example, if the component has @Input name that depends on the parent property name, the following changes are required:
      
      userInputs = { name: ''}
      createInput(){
          // update only when there is change in parent property.
          if (this.parentContext.name !== this.userInputs.name) {
          this.userInputs.name = this.parentContext.name;
          }
      }
      
    2. Update the userOutputs to handle all the event emitters from the component. Any component event changes can be handled in these handler methods. For example, if the component emits a buttonClicked event, the userOutputs is written in the following way:
      
       handleOutput() {
          this.userOutputs = { buttonClicked: this.onButtonClicked.bind(this) }
       }
       onButtonClicked(event){
              // handle changes emitted from the customer component
       }
      

Results

As a reference, the following tutorial adds content to existing pages by using extension points: Tutorial: Customizing the Schedule order action.

Example

To add content to the order details page by using extension points:
  1. Identify the extension directive that will be used to insert your customizations. Each default Angular component will have extension directives at the beginning and end of the component. For example:
    
    <ng-template bucExtension [componentName]="componentName" [placement]="'top'" [id]="id" [parent]="this"> </ng-template>
    
    Each of these templates will have unique IDs defined in the packages/order-details/src/app/features/order/order-details/order-details.component.ts file:
    
       EXTENSION = {
          TOP: ExtensionConstants.ORDER_DETAILS_TOP,
          BOTTOM: ExtensionConstants.ORDER_DETAILS_BOTTOM
       };
    
    These constants are defined in the packages/order-details/src/app/features/order/extension.constants.ts file:
    
     export class ExtensionConstants {
       static readonly ORDER_DETAILS_TOP = 'order_details_extension_top';
       static readonly ORDER_DETAILS_BOTTOM = 'order_details_extension_bottom';
     }
    
  2. Create a new component that has to be injected. For example:
    
    import { Component, EventEmitter, Input, Output } from '@angular/core'; 
    @Component({ 
    selector: 'custom-orderdetails-extension', 
    template: '' 
    }) 
    export class CustomOrderDetailsExtensionComponent{ 
    } 
    
    Update the declarations array in the packages/order-details/src-custom/app/app-customization.impl.ts file:
    
    import { CustomOrderDetailsExtensionComponent } from "./features/order/custom-order-details-extension/custom-order-details-extension.component"
    
    
    ...
    
    
    static readonly components = [CustomOrderDetailsExtensionComponent];
    
  3. Create a new service to handle the parent-child communication:
    
    import { Injectable, OnDestroy } from "@angular/core";
    import { BucNotificationModel, BucNotificationService } from '@buc/common-components';
    import { ExtensionService } from "./extension.service";
    
    @Injectable()
    
    export class CustomOrderDetailsExtensionService extends ExtensionService implements OnDestroy {
    
        userInputs = { name: ''}
    
        constructor(  public bucNotificationService: BucNotificationService) {
            super();
        }
    
        createInput() {
            // check only the necessary properties for update
            if (this.parentContext.name !== this.userInputs.name) {
                this.userInputs.name = this.parentContext.name;
            }
            this.userInputObs$.next(this.userInputs);
        }
    
        handleOutput() {
            this.userOutputs = {
                changesEmitted: this.onChangesEmitted.bind(this)
            }
            this.userOutputObs$.next(this.userOutputs);
        }
    
        onChangesEmitted(event: any) {
            this.bucNotificationService.send([
                new BucNotificationModel({
                    statusType: 'success',
                    statusContent: `Message emitted from dynamic component: ${event.message}`
                })
            ]);
        }
    
    }
    
  4. Update the app-customization.impl.ts file as follows:
    
    ...
    
    import { CustomOrderDetailsExtensionService } from "./services/custom-order-details-extension-service"
    import { ExtensionConstants } from "./features/order/extension.constants";
    import { ExtensionModule } from "@buc/common-components";
    
    
    ...
    
    
        static readonly imports = [
            ExtensionModule.forRoot( [{
                id: ExtensionConstants.DETAILS_TAB_BOTTOM,
                component: CustomOrderDetailsExtensionComponent,
                service: CustomOrderDetailsExtensionService
            }])
        ];
    
    ...