Server Side Web Socket Data Binding

Client Side Basic Data Binding is now capable of updating dynamic data without having a manual refresh in the client side. But assume, what happen if these dynamic data is available in the server side and time to time that server emitting a new dynamic stream of data, can we bind these kind of data to the dropdown? In these type of situations, Server side data binding is introduced.

What You Will See

Select
import { Component, OnDestroy, OnInit } from '@angular/core';

import { interval, Subscription } from 'rxjs';

import {
  GlobalRefService,
  DropdownWebsocketDataFetchService,
  DropdownDataBindCallback
} from 'ornamentum';

import { ExampleData } from 'helper-models';
import { DataFetchService } from 'helper-services';

/**
 * Server side web socket data binding example component.
 */
@Component({
  selector: 'app-server-side-web-socket-usage',
  templateUrl: './server-side-web-socket-usage.component.html'
})
export class ServerSideWebSocketUsageComponent implements OnInit, OnDestroy {
  public onDataBind: DropdownDataBindCallback;
  public intervalSubscription: Subscription;

  constructor(private dataFetchService: DataFetchService,
              private globalRefService: GlobalRefService,
              private dropdownWebSocketDataFetchService: DropdownWebsocketDataFetchService<ExampleData>) {
  }

  /**
   * Component initialize lifecycle event handler.
   */
  public ngOnInit(): void {
    // Create web-socket connection on browser environment only to support server side rendering.
    if (this.globalRefService.isBrowser) {
      this.dropdownWebSocketDataFetchService.init({
        url: `wss://${window.location.hostname}` // web-socket endpoint
      });

      this.onDataBind = this.dropdownWebSocketDataFetchService.onDataBind();

      // Keep the socket connection alive with a hart beat ping
      this.intervalSubscription = interval(40000).subscribe(() => {
        this.dropdownWebSocketDataFetchService.socketStream.next({
          type: 'keep-alive'
        } as any);
      });
    }
  }

  /**
   * Component destroy lifecycle event handler.
   */
  public ngOnDestroy(): void {
    this.dropdownWebSocketDataFetchService.dispose();

    if (this.intervalSubscription) {
      this.intervalSubscription.unsubscribe();
    }
  }
}

Basic Usage

Server side data binding is handled via onDataBind callback function. It allow users to customize the data binding logic and will be triggers on dropdown initialization and on dynamic data dropdown operations. This function will receive DropdownRequestParams argument and expect to return RxJS Observable query result DropdownQueryResult<T> object stream. Data returned from this callback function will be directly bound to dropdown assuming that it matches the on demand request context.

For use of Server side web socket data binding operations it is mandatory to import DropdownResourceModule to your application module.

  <ng-dropdown
    ...
    [loadOnScroll]="true"
    [onDataBind]="onDataBind">
  </ng-dropdown>

Let's see how it uses a simple socket connection to emit data in an time interval and how it could listened from the client side in step by step.

In the very first of component initialization, you can creates a web socket connection for browser environment. refer the below code snippet for Global Ref Service isBrowser method.

  public get isBrowser(): boolean {
    return typeof window !== 'undefined';
  }

Then, if window is not undefined, Dropdown Web Socket Data Fetch Service init method will triggers with web socket endpoint. refer the below code snippet.

  public init(config?: WebSocketSubjectConfig<DropdownQueryResult<T[]>>): void {
    this.socket = webSocket<any>(config);
    this.subject = new Subject<DropdownQueryResult<T[]>>();
  }

Then DropdownDataBindCallback data can be bind into onDataBind property.

  public onDataBind(mapper?: <Q>(response: Observable<Q>) => Observable<DropdownQueryResult<T[]>>): DropdownDataBindCallback {
    if (!this.socket) {
      throw Error('Initialize socket data source before data bind.');
    }

    this.socketSubscription = this.socket.subscribe(this.subject);

    return (params?: DropdownRequestParams): Observable<DropdownQueryResult<T[]>> => {
      if (params) {
        this.socket.next({
          type: 'data-fetch',
          filter: params.filter,
          offset: params.offset,
          limit: params.limit
        } as any);

        if (mapper) {
          return mapper(this.subject);
        }

        return this.subject;
      }
    };
  }

After all socket connection will keep alive with a RxJS Interval and it will emit a data set for Dropdown Web Socket Data Fetch Service socketStream method with a object which contains keep-alive tag in every defined interval.

  public get socketStream(): WebSocketSubject<DropdownQueryResult<T[]>> {
    return this.socket;
  }

After all done, OnDestroy Angular life cycle hook automatically triggers and invoke Dropdown Web Socket Data Fetch Service dispose method and unsubscribe all the subscriptions and complete the web socket connection operations.

  public dispose(): void {
    if (this.socket) {
      this.socket.complete();
    }

    if (this.socketSubscription) {
      this.socketSubscription.unsubscribe();
    }

    if (this.subject) {
      this.subject.unsubscribe();
    }
  }

The complete set of code snippet for the server side web socket data binding via onDataBind call back function can be seen in What You Will See section example.

Client Side Basic Data Binding
Client Side Stream Data Binding
Server Side Basic Data Binding
API Doc for Server Side Web Socket Data Binding