npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ng-torque

v0.0.18

Published

Angular library for easy use dynamic components

Downloads

269

Readme

NG-TORQUE

NG-TORQUE

  • [x] AOT support
  • [x] IVY-render support
  • [x] Full lifecycle components
  • [x] Dynamic content projection
  • [x] Dynamic FormControl binding

Installation

$ npm install ng-torque --save

Usage

Dynamic Components

Before Usage

  1. Import NgTorqueModule
  2. Declare dynamic entities by factory function provideDynamicEntities
@NgModule({
 declarations: [
   AppComponent,
   AComponent
 ],
 imports: [
   BrowserModule,
   NgTorqueModule,
 ],
 providers: [
   provideDynamicEntities([AComponent])
 ],
 entryComponents: [],
 bootstrap: [AppComponent]
})

Simple

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

@Component({
  selector: 'app-test',
  template: `
      <dynamic-wrapper componentResolver
                       [type]="dynamicEntity"
      ></dynamic-wrapper>
  `,
  styles: []
})
export class TestComponent {
  public dynamicEntity: Type<AComponent> = AComponent;
}

Binding Input/Output

You can also pass input and output to your dynamic component

AComponent

@Component({
  selector: 'a',
  template: `
      <p>
          a works!
          {{title}}: {{counter}}
      </p>
      <button (click)="increase()">Submit Event</button>
  `
})
export class AComponent {
  @Input() public title!: string;
  @Input() public counter!: number;
  @Output() public count: EventEmitter<number> = new EventEmitter<number>();

  public increase() {
    this.count.emit(this.counter += 1);
  }
}

TestComponent

Create variable dynamicEntity: ComponentDynamicEntity with type: AComponent, input with same fields in AComponent, and use callOutput Pipe for binding output handler increseCounter.

Arg $event in pipe use for map EventEmmiter value, i.e this equal:

<a (count)="increaseCounter($event, parentValue)"></a>
@Component({
  selector: 'app-test',
  template: `
      <dynamic-wrapper componentResolver
                       [type]="dynamicEntity.type"
                       [input]="dynamicEntity.input"
                       [output]="{
                            count: increaseCounter | callOutput: ['$event', parentValue]
                       }"
      ></dynamic-wrapper>
  `,
  styles: []
})
export class TestComponent {
  public parentValue = 'HelloWorld';
  public dynamicEntity: ComponentDynamicEntity<AComponent> = new ComponentDynamicEntity<AComponent>(
    {
      type: AComponent,
      input: {
        title: 'Dynamic Title',
        counter: 0
      }
    }
  );

  public increaseCounter(value: number, parentValue: string) {
    console.log(value, parentValue);
  }

}

Dynamic Content

You can also use content projection for your dynamic component.

If you need resolve @ContentChild/ @ContentChildren in your dynamic component - use viewQuery directive.

ViewQueryDirective

If you use the component not only for dynamics, you must use @Optional() for inject ViewQuery.

@Directive({
  selector: '[viewQuery]',
  exportAs: 'viewQuery'
})
export class Query1Directive extends ViewQueryDirective {
  @ContentChild(CComponent, {static: true}) public cComponent!: CComponent;

  constructor(@Optional() @Host() public wrapperComponent: DynamicWrapperComponent) {
    super(wrapperComponent);
  }

}

CComponent

@Component({
  selector: 'app-c',
  template: `
      <p>
          CComponent : {{title}}
      </p>
  `,
  styles: []
})
export class CComponent {
  @Input() public title!: string;
}

AComponent

Use function provideViewQuery for provide yours ViewQuery implementation. If you use the component not only for dynamics, you must use @Optional() for inject ViewQuery.

@Component({
  selector: 'a',
  template: `
      <p>
          a works!
      </p>
      <ng-content></ng-content>
  `,
  providers: [
    provideViewQuery(Query1Directive)
  ]
})
export class AComponent implements AfterContentInit {
  constructor(@Optional() private contentQuery: Query1Directive) {
  }

  public ngAfterContentInit(): void {
    // Set title in CComponent
    this.contentQuery.cComponent.title = 'Hello from AComponent';
  }
}

It's example equal native content-projection:

<a>
    <app-c></app-c>
</a>

Dynamic Control

You can use dynamicControl directive for binding FormControl on your dynamic component which implements ControlValueAccessor.

BComponent


@Component({
  selector: 'b',
  template: `
      <p>B-COMPONENT: {{title}}</p>
      <input type="text" [formControl]="control"/>
  `,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => BComponent),
      multi: true
    },
    {
      provide: NG_VALIDATORS,
      useExisting: forwardRef(() => BComponent),
      multi: true
    },
  ]
})
export class BComponent implements OnInit, ControlValueAccessor, Validator {
  @Input() title!: string;
  public control = new FormControl();

  private validateFn = () => {
  };
  private change = (value: any) => {
  };

  ngOnInit() {
    this.control.valueChanges.subscribe(value => {
      this.change(value);
    });
  }

  registerOnChange(fn: any): void {
    this.change = fn;
  }

  registerOnTouched(fn: any): void {
  }

  setDisabledState(isDisabled: boolean): void {

  }

  writeValue(obj: any): void {
    this.control.setValue(obj, {emitEvent: false});
  }

  registerOnValidatorChange(fn: () => void): void {
    this.validateFn = fn;
  }

  validate(control: AbstractControl): ValidationErrors | null {
    console.log(control.value);
    return Validators.required(control);
  }

}

TestComponent

<dynamic-wrapper
  dynamicControl
  [control]="control"
  componentResolver
  [type]="entityB.type"
></dynamic-wrapper>

Dynamic Templates

Template Containers

Template container represents TemplateRef registry for dynamic resolving in your components.

Before usage need provide containers and map by functions:

@NgModule({
  declarations: [
    AppComponent,
    AComponent
  ],
  imports: [
    BrowserModule,
    NgTorqueModule,
  ],
  providers: [
    provideTemplateContainer({
      key: 'container',
      value: ContainerComponent
    }),
    provideMapTypeTemplate()
  ],
  entryComponents: [],
  bootstrap: [AppComponent]
})

ContainerComponent

@Component({
  selector: 'container',
  template: `
      <ng-template #greeting let-name="name">
          Hello {{name}}
      </ng-template>
  `
})
export class ContainerComponent implements ITemplateContainer {
  @ViewChild('greeting', {static: true}) public greeting!: TemplateRef<any>;

  resolveTemplateRef(): { [p: string]: TemplateRef<any> } {
    return {
      greeting: this.greeting
    };
  }

}

TestComponent

@Component({
  selector: 'app-test',
  template: `
      <ng-container *ngTorDynamicTemplate="'greeting' context {name: 'Zircon63'} from 'container'">
      </ng-container>
  `,
  styles: []
})
export class TestComponent {
}

Utils

mapFactory

Factory function for provide anything MAP-structure values.

For example provide map of dynamic components:

@NgModule({
  declarations: [
    AppComponent,
    AComponent,
    BComponent
  ],
  imports: [
    BrowserModule,
    NgTorqueModule
  ],
  providers: [
    provideMapValue('dynamic-component', {
      value: AComponent,
      key: 'a-component'
    }),
    provideMapValue('dynamic-component', {
      value: BComponent,
      key: 'b-component'
    }),
    provideMap('dynamic-component', 'map-dynamic-component')
  ],
  entryComponents: [],
  bootstrap: [AppComponent]
})
export class AppModule {
  constructor(@Inject('map-dynamic-component') map: Map<string, Type<any>>) {
    console.log(map);
    /*
    * a-component => class AComponent
    * b-component => class BComponent
    * */
  }
}