Sliding dialog with Angular 8

Standard
Spread the love

Angular material comes with rich set of components. Slider, toggle, dialog, card etc. are set of components required for common usage in web development. While each of these components are feature rich, it is also possible to extend their features. In this article, we are going to extend Portal component to create our very own sliding dialog.

Angular Sliding Dialog

Project Setup

First, we start off with creation of Angular project using Angular’s command line interface (ng cli)

ng new sliding-demo

Above command should create a brand new Angular project and install its dependencies too. Next we proceed with installation of Angular Material

ng add @angular/material

Our project is now ready and we should be good to proceed with creation of our sliding dialog. First create a new service using following command

ng g s service\slide-dialog

This should create a new service inside src\app\service folder. Open the SlideDialogService and inject following dependencies in the constructor

constructor(private overlay: Overlay, private injector: Injector) { }

As per the Angular documentation, Overlay component helps to create floating panels on the screen. The Overlay also comes with its own styles. If you with to use it independently in your project without a need of @angular/material package you can do so. But in that case, you must manually import the stylesheet. In our case, since Material design is already integrated, we can skip that step.

Create a new method ‘open’ with following signatures

 public open<T>(component: ComponentType<T>, config?: DialogConfig): DialogRef<T> {
}

Both Overlay and ComponentType are imported from @angular/cdk/overlay package

DialogConfig is the custom interface defined with following properties.

export interface DialogConfig {  
title?: string;  
width?: string;  
disableClose?: boolean;  
autoFocus?: boolean;
}

The interface captures default properties to be used for dialog layout. Next we start defining position for our dialog.

config = config || { width: 'auto', title: '' };
const positionStrategy = this.overlay.position()
      .global().top('0').right('0');

Note the top and right position parameters. It indicates the relative position of dialog with respect to overall display area.

Next we create overlay with display properties like width and height

const overlay = this.overlay.create({
      hasBackdrop: true,
      positionStrategy,
      panelClass: 'dialog-container',
      width: config.width,
      maxWidth: '90vw',
      height: '100vh'
 });

Since its a sliding dialog that appears in view from left to right, it is better to set the height of dialog to entire screen viewport height.

Dialog Container

Before we attach Dialog Component to overlay, we must add a wrapper component. This component will act as parent container for dialog and will provide default look & feel (e.g. close button at top right corner, white background color etc.)

const dialogPreview = new ComponentPortal(DialogContainerComponent);
const dialogContainerRef = overlay.attach(dialogPreview);
dialogContainerRef.instance.dialogTitle = config.title;
const dialogRef = new DialogRef&lt;T>(overlay);

The definition of DialogContainerComponent looks like below



@core.Component({
  selector: 'app-dialog-container',
  template: `
  <div style="display:flex;justify-content:space-between;padding:0.5rem;width:100%">
    <span class="title">{{ dialogTitle }}</span>
    <button (click)="closeDialog()">X</button>
  </div>
  <ng-template cdkPortalOutlet #portal > </ng-template>
  `,
  encapsulation: core.ViewEncapsulation.None,
  styles: ['app-dialog-container {width: 100%;display: grid;grid-template-rows: 40px 1fr;padding: 0 10px;background-color:#fff}',
    '.title { font-weight: bold; }']
})
export class DialogContainerComponent<T> implements core.OnInit {

  @core.Input()
  dialogTitle = '';

  @core.Input()
  comp: ComponentPortal<T>;

  @core.Output()
  containerEvent = new core.EventEmitter<{ key: 'CLOSE' }>();

  @core.ViewChild('portal', { read: CdkPortalOutlet, static: true })
  portal: CdkPortalOutlet;

  @core.Input()
  selectedPortal: Portal<T>;

  constructor() { }

  ngOnInit() {
  }

  attach() {
    const c = this.portal.attach(this.selectedPortal);
    return c.instance;
  }

  closeDialog() {
    this.containerEvent.emit({ key: 'CLOSE' });
  }

}

As of now, there is no complex logic in the Dialog Container. It adds a close button to close the dialog. It also uses ng-template tag to replace dynamic content.

Next we attach the actual component that has been passed as first argument to open function.

const injector = this.createInjector(dialogRef);
const c = new ComponentPortal(component, null, injector);
dialogContainerRef.instance.selectedPortal = c;
const componentRef = dialogContainerRef.instance.attach();
dialogRef.componentInstance = componentRef;

Note we also create an injector map, containing reference of dialog. The function looks like below. You can leverage this map to add your own dependencies.

private createInjector&lt;T>(dialogRef: DialogRef&lt;T>) {
    const injectorMap = new WeakMap();
    injectorMap.set(DialogRef, dialogRef);
    return new PortalInjector(this.injector, injectorMap);
}

And finally, we can apply custom properties to the dialog. This is very handy in case, you wish to apply common event handlers and provide add-on features which are not available in default material dialog (e.g. Dialog Title).

this.applyDialogProperties(dialogContainerRef, overlay, config);
return dialogRef;

The applyDialogProperties function looks like below.

private applyDialogProperties(componentRef: core.ComponentRef&lt;any>, overlayRef: OverlayRef, config: DialogConfig) {
    componentRef.instance.containerEvent.subscribe((e) => {
      if (e.key === 'CLOSE') {
        overlayRef.dispose();
      }
    });
    if (!config.disableClose) {
      overlayRef.backdropClick().subscribe(() => overlayRef.dispose());
    }
}

Notice how we refer to the backdrop and capture the click event. This can be very handy to disable/enable closure of dialog on click of backdrop. Here is how our sliding dialog looks like.

Sliding dialog example

Here is the working example with source code in CodeSandbox

Leave a Reply