javascript - Hämta beroende från Injector manuellt inuti en direktiv


Jag försöker skapa en generell direktiv som kommer att ta en klastype för regelvalidering och enligt regeln i klassen kommer direktivet antingen att visa eller dölja ett element.

Det här är mitt försök hittills.

PLUNKER Demo

myIf-Directive.ts

@Directive({
  selector: '[myIf]'
})
export class MyIfDirective {
  constructor(private _viewContainer: ViewContainerRef,
    private _template: TemplateRef<Object>) 
  { }

  @Input() set myIf(rule: string) {
    //rule class type will come as string
    //how can I use string token to get dependency from injector?
    //currently harcoded
    //will the injector create new instance or pass on instance from parent?
    let injector = ReflectiveInjector.resolveAndCreate([AdminOnly]);
    let adminOnly : IRule = injector.get(AdminOnly);
    let show = adminOnly.shouldShowElement();
    show ? this.showItem() : this.hideItem();
  }
  private showItem() {
    this._viewContainer.createEmbeddedView(this._template);
  }

  private hideItem() {
    this._viewContainer.clear();
  }
}

app-komponent.ts

@Component({
  selector: 'my-app',
  template: `
    <div *myIf="'AdminOnly'">
      <h2>Hello {{name}}</h2>
    </div>
  `,
})
export class App {
  name:string;
  constructor() {
    this.name = 'Angular2'
  }
}

Men jag är fast på två ställen:

  1. Jag får hela tiden felet No Provider for AuthService
  2. Jag vet inte hur jag kan få beroendet från Injector med klassnamn som sträng istället för typ

Alla förslag på om detta är rätt sätt att göra det på eller var jag gör fel uppskattas mycket.

Svar #1

Du behöver skicka föräldrainjektorn som

export class MyIfDirective {
  constructor(private injector:Injector, private _viewContainer: ViewContainerRef,
    private _template: TemplateRef<Object>) 
  { }

  @Input() set myIf(rule: string) {
    let resolvedProviders = ReflectiveInjector.resolve([AdminOnly]);
    let childInjector = ReflectiveInjector.fromResolvedProviders(resolvedProviders, this.injector);

    let adminOnly : IRule = childInjector.get(AdminOnly);
    let show = adminOnly.shouldShowElement();
    show ? this.showItem() : this.hideItem();
  }
  private showItem() {
    this._viewContainer.createEmbeddedView(this._template);
  }

  private hideItem() {
    this._viewContainer.clear();
  }
}

Se även Injicera tjänst med ReflectiveInjector utan att specificera alla klasser i beroendeträdet

Svar #2

Uppdatera bara för Angular version 10+

  • Från din tjänst:
  @Injectable({
    providedIn: 'any'
  })
  export class AdminOnly { ... }
  • I ditt direktiv eller en ren funktion, ...:
 import { Injector } from '@angular/core';

 ...
 const injector: Injector = Injector.create({
   providers: [{provide: AdminOnly, deps: []}]
 });
 const adminOnly: AdminOnly = injector.get(AdminOnly);

 let show = adminOnly.shouldShowElement();
 ...

Se mer