import { ComponentFactoryResolver, Directive, Input, OnDestroy, TemplateRef, ViewContainerRef } from '@angular/core'; import { Store } from '@ngrx/store'; import { Subscription } from 'rxjs/Subscription'; import 'rxjs/add/operator/map'; import 'rxjs/add/operator/distinctUntilChanged'; import { RxIfConfig, IElseMessageComponent } from './rx-if.interface'; import { RxIfElseMessageComponent } from './rx-if-else-message.component'; @Directive({ selector: '[rxIf]' }) export class RxIfDirective implements OnDestroy { private subscription: Subscription; private config: RxIfConfig; @Input() set rxIf(config: RxIfConfig) { this.config = config; this.ngOnDestroy(); this.subscription = this.store // We cannot use the ".select(...this.config.bindTo.split('.'))" .select(state => this.extractBindingFromState(state)) .map(Boolean) .distinctUntilChanged() .subscribe((result: boolean) => this.updateView(result)); } constructor( private _viewContainer: ViewContainerRef, private templateRef: TemplateRef, private store: Store, private componentFactoryResolver: ComponentFactoryResolver, ) { } ngOnDestroy(): void { if (this.subscription) { this.subscription.unsubscribe(); } } private updateView(result: boolean): void { if (typeof result === 'undefined' || result === null) { return; } this._viewContainer.clear(); if (result) { this._viewContainer.createEmbeddedView(this.templateRef); } else { const component: any = this._viewContainer.createComponent( this.componentFactoryResolver.resolveComponentFactory( this.config.elseMsg ? RxIfElseMessageComponent : this.config.elseMsgCmp ) ); if (this.config.elseMsg) { component.instance.message = this.config.elseMsg; } else { (component.instance as IElseMessageComponent).ctx = this.config.elseMsgCmpCtx; } } } private extractBindingFromState(state: any): any { return this.config.bindTo .split('.') .reduce( (acc, key) => typeof acc === 'string' ? (state && state[acc] ? state[acc][key] : null) : (acc ? acc[key] : null) ); } }