Giter Club home page Giter Club logo

ng-dynamic-component's Introduction

ng-dynamic-component

Dynamic components with full life-cycle support for inputs and outputs

Travis CI Appveyor Coverage Maintainability Npm Npm Downloads Licence semantic-release Greenkeeper badge

Angular ng-dynamic-component NPM package
8.x.x 5.x.x ng-dynamic-component@^5.0.0
7.x.x 4.x.x ng-dynamic-component@^4.0.0
6.x.x 3.x.x ng-dynamic-component@^3.0.0
5.x.x 2.x.x ng-dynamic-component@^2.0.0
4.x.x 1.x.x ng-dynamic-component@^1.0.0
2.x.x 0.x.x ng-dynamic-component@^0.0.0

Installation

$ npm install ng-dynamic-component --save

Usage

Import DynamicModule with dynamic components you want to insert later:

import { DynamicModule } from 'ng-dynamic-component';
import { MyDynamicComponent1, MyDynamicComponent2 } from './my-components';

@NgModule({
  imports: [
    DynamicModule.withComponents([MyDynamicComponent1, MyDynamicComponent2])
  ]
})

DynamicComponent

Then in your component's template include <ndc-dynamic> where you want to render component and bind from your component class type of component to render:

@Component({
  selector: 'my-component',
  template: `<ndc-dynamic [ndcDynamicComponent]="component"></ndc-dynamic>`,
})
class MyComponent {
  component = Math.random() > 0.5 ? MyDynamicComponent1 : MyDynamicComponent2;
}

Inputs and Outputs

You can also pass inputs and outputs to your dynamic components:

@Component({
  selector: 'my-component',
  template: `<ndc-dynamic [ndcDynamicComponent]="component"
                          [ndcDynamicInputs]="inputs"
                          [ndcDynamicOutputs]="outputs"
                          ></ndc-dynamic>`,
})
class MyComponent {
  component = MyDynamicComponent1;
  inputs = {
    hello: 'world',
    something: () => 'can be really complex',
  };
  outputs = {
    onSomething: type => alert(type),
  };
}

@Component({
  selector: 'my-dynamic-component1',
  template: 'Dynamic Component 1',
})
class MyDynamicComponent1 {
  @Input()
  hello: string;
  @Input()
  something: Function;
  @Output()
  onSomething = new EventEmitter<string>();
}

Here you can update your inputs (ex. inputs.hello = 'WORLD') and they will trigger standard Angular's life-cycle hooks (of course you should consider which change detection strategy you are using).

Component Creation Events

You can subscribe to component creation events, being passed a reference to the ComponentRef:

@Component({
  selector: 'my-component',
  template: `<ndc-dynamic [ndcDynamicComponent]="component"
                          (ndcDynamicCreated)="componentCreated($event)"
                          ></ndc-dynamic>`,
})
class MyComponent {
  component = MyDynamicComponent1;
  componentCreated(compRef: ComponentRef<any>) {
    // utilize compRef in some way ...
  }
}

Attributes

Since v2.2.0 you can now declaratively set attributes, as you would inputs, via ndcDynamicAttributes:

import { AttributesMap } from 'ng-dynamic-component';

@Component({
  selector: 'my-component',
  template: `<ndc-dynamic [ndcDynamicComponent]="component"
                          [ndcDynamicAttributes]="attrs"
                          ></ndc-dynamic>`,
})
class MyComponent {
  component = MyDynamicComponent1;
  attrs: AttributesMap = {
    'my-attribute': 'attribute-value',
    class: 'some classes',
  };
}

Remember that attributes values are always strings (while inputs can be any value). So to have better type safety you can use AttributesMap interface for your attributes maps.

Also you can use ngComponentOutlet and ndcDynamicAttributes with * syntax:

import { AttributesMap } from 'ng-dynamic-component';

@Component({
  selector: 'my-component',
  template: `<ng-container *ngComponentOutlet="component;
                            ndcDynamicAttributes: attrs"
                            ></ng-container>`,
})
class MyComponent {
  component = MyDynamicComponent1;
  attrs: AttributesMap = {
    'my-attribute': 'attribute-value',
    class: 'some classes',
  };
}

Directives

Since v3.1.0 you can now declaratively set directives, via ndcDynamicDirectives:

NOTE: In dynamic directives queries like @ContentChild and host decorators like @HostBinding will not work due to involved complexity required to handle it (but PRs are welcome!).

import { dynamicDirectiveDef } from 'ng-dynamic-component';

@Component({
  selector: 'my-component',
  template: `<ng-container [ngComponentOutlet]="component"
                           [ndcDynamicDirectives]="dirs"
                           ></ng-container>`,
})
class MyComponent {
  component = MyDynamicComponent1;
  dirs = [dynamicDirectiveDef(MyDirective)];
}

It's also possible to bind inputs and outputs to every dynamic directive:

import { dynamicDirectiveDef } from 'ng-dynamic-component';

@Component({
  selector: 'my-component',
  template: `<ng-container [ngComponentOutlet]="component"
                           [ndcDynamicDirectives]="dirs"
                           ></ng-container>`,
})
class MyComponent {
  component = MyDynamicComponent1;
  directiveInputs = { prop1: 'value' };
  directiveOutputs = { output1: evt => this.doSomeStuff(evt) };
  dirs = [
    dynamicDirectiveDef(
      MyDirective,
      this.directiveInputs,
      this.directiveOutputs,
    ),
  ];
}

To change inputs, just update the object:

class MyComponent {
  updateDirectiveInput() {
    this.directiveInputs.prop1 = 'new value';
  }
}

You can have multiple directives applied to same dynamic component (only one directive by same type):

import { dynamicDirectiveDef } from 'ng-dynamic-component';

@Component({
  selector: 'my-component',
  template: `<ng-container [ngComponentOutlet]="component"
                           [ndcDynamicDirectives]="dirs"
                           ></ng-container>`,
})
class MyComponent {
  component = MyDynamicComponent1;
  dirs = [
    dynamicDirectiveDef(MyDirective1),
    dynamicDirectiveDef(MyDirective2),
    dynamicDirectiveDef(MyDirective3),
    dynamicDirectiveDef(MyDirective1), // This will be ignored because MyDirective1 already applied above
  ];
}

NgComponentOutlet support

You can also use NgComponentOutlet directive from @angular/common instead of <ndc-dynamic> and apply inputs and outputs to your dynamic components:

@Component({
  selector: 'my-component',
  template: `<ng-container [ngComponentOutlet]="component"
                           [ndcDynamicInputs]="inputs"
                           [ndcDynamicOutputs]="outputs"
                           ></ng-container>`
})
class MyComponent {
  component = MyDynamicComponent1;
  inputs = {...};
  outputs = {...};
}

Also you can use ngComponentOutlet with * syntax:

@Component({
  selector: 'my-component',
  template: `<ng-container *ngComponentOutlet="component;
                            ndcDynamicInputs: inputs;
                            ndcDynamicOutputs: outputs"
                            ></ng-container>`
})
class MyComponent {
  component = MyDynamicComponent1;
  inputs = {...};
  outputs = {...};
}

Extra

You can have more advanced stuff over your dynamically rendered components like setting custom injector ([ndcDynamicInjector]) or providing additional/overriding providers ([ndcDynamicProviders]) or both simultaneously or projecting nodes ([ndcDynamicContent]).

NOTE: In practice funtionality of this library is splitted in two pieces:

  • one - component (ndc-dynamic) that is responsible for instantianting and rendering of dynamic components;
  • two - directive (ndcDynamic also bound to ndc-dynamic) that is responsible for carrying inputs/outputs to/from dynamic component by the help of so called ComponentInjector (it is ndc-dynamic by default).

Thanks to this separation you are able to connect inputs/outputs and life-cycle hooks to different mechanisms of injecting dynamic components by implementing ComponentInjector and providing it via DynamicModule.withComponents(null, [here]) in second argument.

It was done to be able to reuse NgComponentOutlet added in Angular 4-beta.3.

License

MIT © Alex Malkevich

ng-dynamic-component's People

Contributors

bmarti44 avatar chriswhite199 avatar greenkeeper[bot] avatar gund avatar hpawe01 avatar jerousseau avatar kamilkisiela avatar mcanfield avatar mohamedaymenkarmous avatar pxyup avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.