File

src/lib/method-template.directive.ts

Implements

OnInit OnChanges

Metadata

Index

Methods
Inputs
Outputs
HostListeners

Constructor

constructor(template: TemplateRef<MethodTemplateDirectiveContext<ReturnType>>, viewContainerRef: ViewContainerRef, cdr: ChangeDetectorRef)
Parameters :
Name Type Optional
template TemplateRef<MethodTemplateDirectiveContext<ReturnType>> No
viewContainerRef ViewContainerRef No
cdr ChangeDetectorRef No

Inputs

immediately
Type : boolean
Default value : false
rxapMethodCall
Type : Method<ReturnType | Parameters>
rxapMethodError
Type : TemplateRef<MethodTemplateDirectiveErrorContext>
rxapMethodLoading
Type : TemplateRef<void>
rxapMethodParameters
Type : Parameters
rxapMethodWithoutParameters
Type : boolean
Default value : false

Outputs

embedded
Type : EventEmitter
executed
Type : EventEmitter
executing
Type : ToggleSubject

true - a remote method call is in progress

failure
Type : EventEmitter
successful
Type : EventEmitter

HostListeners

confirmed

Methods

Public Async execute
execute(parameters: Parameters | undefined)
Parameters :
Name Type Optional Default value
parameters Parameters | undefined No this.parameters
Returns : Promise<void>
Protected executed
executed(result: any)
Parameters :
Name Type Optional
result any No
Returns : void
Protected failure
failure(error: Error)
Parameters :
Name Type Optional
error Error No
Returns : void
Static ngTemplateContextGuard
ngTemplateContextGuard(dir: MethodTemplateDirective<T>, ctx: any)
Type parameters :
  • T
Parameters :
Name Type Optional
dir MethodTemplateDirective<T> No
ctx any No
Public onConfirmed
onConfirmed()
Decorators :
@HostListener('confirmed')
Returns : Promise<void>
Protected renderErrorTemplate
renderErrorTemplate(error: Error)
Parameters :
Name Type Optional
error Error No
Returns : void
Protected renderTemplate
renderTemplate(result: ReturnType)
Parameters :
Name Type Optional
result ReturnType No
Returns : void
Public setParameter
setParameter(parameterKey: Key, value)
Type parameters :
  • Key
Parameters :
Name Type Optional
parameterKey Key No
value No
Returns : void
Protected successful
successful(result: ReturnType)
Parameters :
Name Type Optional
result ReturnType No
Returns : void
Public updateParameters
updateParameters(parameters: Partial)
Parameters :
Name Type Optional
parameters Partial<Parameters> No
Returns : void
import {
  ChangeDetectorRef,
  Directive,
  EventEmitter,
  HostListener,
  Inject,
  Input,
  isDevMode,
  OnChanges,
  OnInit,
  Output,
  SimpleChanges,
  TemplateRef,
  ViewContainerRef,
} from '@angular/core';
import { ToggleSubject } from '@rxap/rxjs';
import { Method } from '@rxap/pattern';

export interface MethodTemplateDirectiveContext<ReturnType = any> {
  $implicit: ReturnType;
}

export interface MethodTemplateDirectiveErrorContext {
  $implicit: Error;
  message: string;
}

@Directive({
  selector: '[rxapMethod]',
  exportAs: 'rxapMethod',
  standalone: true,
})
export class MethodTemplateDirective<ReturnType = any, Parameters = any> implements OnInit, OnChanges {

  @Input('rxapMethodCall')
  public method!: Method<ReturnType, Parameters>;
  @Input('rxapMethodParameters')
  public parameters?: Parameters;
  @Input('rxapMethodError')
  public errorTemplate?: TemplateRef<MethodTemplateDirectiveErrorContext>;
  @Input('rxapMethodLoading')
  public loadingTemplate?: TemplateRef<void>;
  @Input('rxapMethodWithoutParameters')
  public withoutParameters = false;
  @Input()
  public immediately = false;
  @Output('executed')
  public executed$ = new EventEmitter();
  @Output('failure')
  public failure$ = new EventEmitter<Error>();
  @Output('successful')
  public successful$ = new EventEmitter<ReturnType>();
  /**
   * true - a remote method call is in progress
   */
  @Output('executing')
  public executing$ = new ToggleSubject();
  @Output()
  public embedded = new EventEmitter();

  constructor(
    @Inject(TemplateRef)
    private readonly template: TemplateRef<MethodTemplateDirectiveContext<ReturnType>>,
    @Inject(ViewContainerRef)
    private readonly viewContainerRef: ViewContainerRef,
    @Inject(ChangeDetectorRef)
    protected readonly cdr: ChangeDetectorRef,
  ) {
  }

  static ngTemplateContextGuard<T>(dir: MethodTemplateDirective<T>, ctx: any):
    ctx is MethodTemplateDirectiveContext<T> {
    return true;
  }

  public ngOnChanges(changes: SimpleChanges) {
    const parametersChanges = changes['parameters'];
    if (parametersChanges) {
      this.execute(parametersChanges.currentValue);
    }
  }

  public ngOnInit() {
    this.renderLoadingTemplate();
    if (this.withoutParameters) {
      this.execute();
    }
  }

  @HostListener('confirmed')
  public onConfirmed() {
    return this.execute();
  }

  public async execute(parameters: Parameters | undefined = this.parameters): Promise<void> {
    this.executing$.enable();
    try {
      const result = await this.method.call(parameters);
      this.executed(result);
      this.renderTemplate(result);
      this.successful(result);
    } catch (error: any) {
      if (isDevMode()) {
        console.error(`Method directive execution failed:`, error.message);
      }
      this.renderErrorTemplate(error);
      this.failure(error);
    } finally {
      this.executing$.disable();
    }
  }

  public setParameter<Key extends keyof Parameters>(parameterKey: Key, value: Parameters[Key]): void {
    this.updateParameters({ [parameterKey]: value } as any);
  }

  public updateParameters(parameters: Partial<Parameters>): void {
    this.parameters = { ...(this.parameters ?? {}), ...parameters } as any;
  }

  protected executed(result: any) {
    this.executed$.emit(result);
  }

  protected failure(error: Error) {
    this.failure$.emit(error);
  }

  protected successful(result: ReturnType) {
    this.successful$.emit(result);
  }

  protected renderErrorTemplate(error: Error) {
    if (this.errorTemplate) {
      this.viewContainerRef.clear();
      if (this.errorTemplate) {
        this.viewContainerRef.createEmbeddedView(this.errorTemplate,
          {
            $implicit: error,
            message: error.message,
          },
        );
      }
    }
  }

  protected renderTemplate(result: ReturnType) {

    this.viewContainerRef.clear();

    try {
      this.viewContainerRef.createEmbeddedView(this.template, { $implicit: result });
    } catch (error: any) {
      this.renderErrorTemplate(error);
      console.error(error.message);
      throw error;
    }

    this.embedded.emit();

    this.cdr.detectChanges();

  }

  private renderLoadingTemplate() {
    if (this.loadingTemplate) {
      this.viewContainerRef.clear();
      this.viewContainerRef.createEmbeddedView(this.loadingTemplate);
    }
  }

}


results matching ""

    No results matching ""