DEV Community

loading...

Auto binding Input() and Output() in Angular9+

endykaufman profile image ILshat Khamitov Updated on ・2 min read

Directives for auto binding Input() and Output() from host component to inner in Angular9+ application

Installation

npm i --save ngx-bind-io

Example

Without auto binding inputs and outputs

<component-name (start)="onStart()" [isLoading]="isLoading$ | async" [propA]="propA" [propB]="propB"> </component-name>

With auto binding inputs and outputs

<component-name [bindIO]> </component-name>

app.module.ts

import { NgxBindIOModule } from 'ngx-bind-io';
import { InnerComponent } from './inner.component';
import { HostComponent } from './host.component';

@NgModule({
  ...
  imports: [
    ...
    NgxBindIOModule.forRoot(), // in child modules import as "NgxBindIOModule"
    ...
  ]
  declarations: [
    ...
    InnerComponent,
    HostComponent
    ...
  ],
  ...
})
export class AppModule { }

inner.component.ts

import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';

@Component({
  selector: 'inner',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <div *ngIf="isLoading">Loading... (5s)</div>
    <button (click)="onStart()">Start</button> <br />
    {{ propA }} <br />
    {{ propB }}
  `
})
export class InnerComponent {
  @Input()
  isLoading: boolean;
  @Input()
  propA = 'Prop A: not defined';
  @Input()
  propB = 'Prop B: not defined';
  @Output()
  start = new EventEmitter();
  onStart() {
    this.start.next(true);
  }
}

base-host.component.ts

import { BehaviorSubject } from 'rxjs';

export class BaseHostComponent {
  isLoading$ = new BehaviorSubject(false);
  onStart() {
    this.isLoading$.next(true);
    setTimeout(() => this.isLoading$.next(false), 5000);
  }
}

host.component.ts

import { ChangeDetectionStrategy, Component } from '@angular/core';
import { BaseHostComponent } from './base-host.component';

@Component({
  selector: 'host',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <inner [bindIO]></inner>
  `
})
export class HostComponent extends BaseHostComponent {
  propA = 'Prop A: defined';
  propB = 'Prop B: defined';
}

Debug

For global debug all bindings

import { NgxBindIOModule } from 'ngx-bind-io';

@NgModule({
  ...
  imports: [
    ...
    NgxBindIOModule.forRoot({debug: true})
    ...
  ],
  ...
})
export class AppModule { }

For debug on one place

<comp-name [bindIO]="{debug:true}"></comp-name>

Rules for detect inputs and outputs

Custom rules for detect output method

my-ngx-bind-outputs.service.ts

import { Injectable } from '@angular/core';
import { IBindIO, NgxBindOutputsService } from 'ngx-bind-io';

@Injectable()
export class MyNgxBindOutputsService extends NgxBindOutputsService {
  checkKeyNameToOutputBind(directive: Partial<INgxBindIODirective>, hostKey: string, innerKey: string) {
    const outputs = directive.outputs;
    const keyWithFirstUpperLetter =
      innerKey.length > 0 ? innerKey.charAt(0).toUpperCase() + innerKey.substr(1) : innerKey;
    return (
      (hostKey === `on${keyWithFirstUpperLetter}` &&
        outputs.hostKeys.indexOf(`on${keyWithFirstUpperLetter}Click`) === -1 &&
        outputs.hostKeys.indexOf(`on${keyWithFirstUpperLetter}ButtonClick`) === -1) ||
      (hostKey === `on${keyWithFirstUpperLetter}Click` &&
        outputs.hostKeys.indexOf(`on${keyWithFirstUpperLetter}ButtonClick`) === -1) ||
      hostKey === `on${keyWithFirstUpperLetter}ButtonClick`
    );
  }
}

app.module.ts

import { NgxBindOutputsService, NgxBindIOModule } from 'ngx-bind-io';
import { MyNgxBindOutputsService } from './shared/utils/my-ngx-bind-outputs.service';
import { InnerComponent } from './inner.component';
import { HostComponent } from './host.component';

@NgModule({
  declarations: [AppComponent],
  imports: [
    ...
    NgxBindIOModule,
    ...
  ],
  declarations: [
    AppComponent,
    InnerComponent,
    HostComponent,
    ...
  ],
  providers: [
    ...
    { provide: NgxBindOutputsService, useClass: MyNgxBindOutputsService },
    ...
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

Links

Demo - Demo application with ngx-bind-io.

Stackblitz - Simply sample of usage on https://stackblitz.com

Discussion

pic
Editor guide