DEV Community

ILshat Khamitov
ILshat Khamitov

Posted on • Edited on

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

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

Installation

npm i --save ngx-bind-io
Enter fullscreen mode Exit fullscreen mode

Example

Without auto binding inputs and outputs

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

With auto binding inputs and outputs

<component-name [bindIO]> </component-name>
Enter fullscreen mode Exit fullscreen mode

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 { }
Enter fullscreen mode Exit fullscreen mode

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);
  }
}
Enter fullscreen mode Exit fullscreen mode

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);
  }
}
Enter fullscreen mode Exit fullscreen mode

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';
}
Enter fullscreen mode Exit fullscreen mode

Debug

For global debug all bindings

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

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

For debug on one place

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

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`
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

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 { }
Enter fullscreen mode Exit fullscreen mode

Links

Demo - Demo application with ngx-bind-io.

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

Top comments (0)