Saltar al contenido

Diferencia entre @Self y @Host Angular 2+ Decoradores de inyección de dependencia

Solución:

tl; dr

Parece que cuando @Self se usa, Angular solo buscará un valor que esté vinculado en el inyector de componente para el elemento en el que existe esta Directiva / Componente.

Parece que cuando @Host se utiliza, Angular buscará un valor que esté vinculado en el inyector del componente para el elemento en el que existe esta Directiva / Componente, o en el inyector del componente principal. Angular llama a este componente principal el “host”.

Mas explicacion

Aunque las descripciones principales no son muy útiles, parece que los ejemplos en la documentación de @Self y @Host hacen un trabajo decente al aclarar cómo se usan y cuál es la diferencia (copiado a continuación).

Al tratar de entender esto, puede ser útil recordar que cuando la inyección de dependencia angular intenta resolver un valor particular para un constructor, comienza buscando en el inyector el componente actual, luego itera hacia arriba a través de los inyectores principales. Esto se debe a que Angular usa inyectores jerárquicos y permite la herencia de inyectores ancestros.

Entonces cuando el @Host La documentación dice que “especifica que un inyector debe recuperar una dependencia de cualquier inyector hasta llegar al elemento host del componente actual”, lo que significa que detiene esta iteración ascendente antes de tiempo una vez que alcanza el inyector vinculado al componente principal.

@Self ejemplo (fuente)

class Dependency {}

@Injectable()
class NeedsDependency {
  constructor(@Self() public dependency: Dependency) {}
}

let inj = ReflectiveInjector.resolveAndCreate([Dependency, NeedsDependency]);
const nd = inj.get(NeedsDependency);

expect(nd.dependency instanceof Dependency).toBe(true);

inj = ReflectiveInjector.resolveAndCreate([Dependency]);
const child = inj.resolveAndCreateChild([NeedsDependency]);
expect(() => child.get(NeedsDependency)).toThrowError();

@Host ejemplo (fuente)

class OtherService {}
class HostService {}

@Directive({selector: 'child-directive'})
class ChildDirective {
  logs: string[] = [];

  constructor(@Optional() @Host() os: OtherService, @Optional() @Host() hs: HostService) {
    // os is null: true
    this.logs.push(`os is null: ${os === null}`);
    // hs is an instance of HostService: true
    this.logs.push(`hs is an instance of HostService: ${hs instanceof HostService}`);
  }
}

@Component({
  selector: 'parent-cmp',
  viewProviders: [HostService],
  template: '<child-directive></child-directive>',
})
class ParentCmp {
}

@Component({
  selector: 'app',
  viewProviders: [OtherService],
  template: '<parent-cmp></parent-cmp>',
})
class App {
}

Ejemplo de cuando usar @Self es importante

Digamos que tiene una directiva que se usa para modificar el comportamiento de muchos tipos de componentes; tal vez esta directiva proporcione algún tipo de soporte de configuración.

Esta directiva está vinculada a muchos componentes a lo largo de su aplicación y esta directiva vincula algún servicio en su providers lista. Los componentes que quieran utilizar esta directiva para configurarse dinámicamente inyectarán el servicio que proporciona.

Sin embargo, queremos asegurarnos de que un componente solo use su propia configuración y no inyecte accidentalmente el servicio de configuración que estaba destinado a algún componente principal. Entonces usamos el @Self decorador para decirle a la inyección de dependencia de Angular que solo considere el servicio de configuración proporcionado en el elemento de este componente.

https://netbasal.com/exploring-the-various-decorators-in-angular-b208875b207c

Anfitrión:

@Host: el decorador @Host le dice a DI que busque una dependencia en cualquier inyector hasta que llegue al host

Uno mismo:

@Self: el decorador @Self le dice a DI que busque una dependencia solo de sí mismo, para que no suba por el árbol

Aquí hay un ejemplo:

https://plnkr.co/edit/UmpPTnzcRxgDc9Hn5I9G?p=preview

Como puede ver, la directiva MyDir usa: @Self para acceder a su propio Car Su componente ‘@Host Garage Dependencia @Optional @Host Sun Dependencia que no está definida en el Host sino que está definida en la App. Dado que no está definido en el host, será nulo

La salida será:

 parent component. 
  { "type": "child garage", 
    "car": { "model": "child car" }, 
    "sun": null 
  }

Aquí están los componentes y proveedores:

  class Garage {
    car;
    type;
    sun;

    constructor(type) {
      this.type=type;
    }
    setCar(car) {
      this.car = car;
    }
    setSun(sun) {
      this.sun = sun;
    }
  }

  class Car {
    model;
    constructor(model) {
      this.model=model;
    }
  }

  class Sun { }

  @Directive({
    selector: '[myDir]',
    providers:[
      {provide: Car, useValue: new Car('child car')}
      {provide: Garage, useValue: new Garage('child garage')}
    ]
  })
  export class MyDir {
    constructor(@Self() private car: Car, @Host() private garage: Garage,
      @Optional() @Host() private sun: Sun) {
       this.garage.setCar(this.car);
       this.garage.setSun(this.sun);
    }
  }

  @Component({
    selector: 'parent',
    template: `
       parent component. {{garage|json}}
    `,
    providers:[
      {provide: Car, useValue: new Car('parent car')},
      {provide: Garage, useValue: new Garage('parent garage')}
    ]
  })
  export class Parent {
    childDep;
    constructor(private car: Car, private garage: Garage) {
    }
  }

  @Component({
    selector: 'my-app',
    template: `
  <parent myDir></parent>
    `,
    providers:[
      {provide: Car, useValue: new Car('app car')},
      {provide: Garage, useValue: new Garage('app garage')},
      {provide: Sun, useValue: 'sun'}
    ]
  })
  export class App {
  }

Angular resuelve las dependencias buscándolas dentro de la jerarquía de inyectores de elementos comenzando en el inyector para el elemento actual, luego avanzando hacia el del elemento padre si no se encuentra allí, y así sucesivamente. Si aún no se encuentra la dependencia, pasa a los inyectores del módulo. Si no se encuentra allí, se genera un error. https://angular.io/guide/hierarchical-dependency-injection#host

@Self y @Host son modificadores que le dicen a Angular en qué inyectores debe dejar de buscar dependencias.

@Uno mismo

@Self le dice a Angular que solo debe mirar dentro del inyector en el elemento actual. Un punto importante a tener en cuenta con respecto a esto es que cada elemento tiene un solo inyector que es compartido por todas las directivas que se le atribuyen. Por lo tanto, en este fragmento de plantilla:

<div dir-1 dir-2></div>

Asumiendo que dir-1 corresponde a la Directiva1, y dir-2 corresponde a la Directiva2, si la Directiva1 registra un proveedor, la Directiva2 podrá inyectar ese servicio, y viceversa.

Si una dependencia tiene el modificador @Self, esto significa que Angular solo buscará un proveedor dentro del inyector del elemento actual. A menos que el modificador @Optional también esté presente, se lanzará un error si no puede encontrarlo.

El caso de uso de @Self es si desea que un servicio se inyecte en una directiva o componente, solo si otra directiva en el mismo elemento lo proporciona. (La directiva obviamente puede suministrar el servicio por sí misma, pero eso parece hacer que el uso de @Self sea un poco redundante).

Prueba

https://stackblitz.com/edit/angular-di-test-4-6jxjas?file=src%2Fapp%2Fapp.component.html Considere esta plantilla en app.component.html

<div my-directive-alpha>
    <h1 my-directive-beta my-directive-gamma>Lorem Ipsum..</h1>
</div>

Dejar my-directive-alpha corresponden a MyDirectiveAlpha, my-directive-beta corresponden a MyDirectiveBeta, y my-directive-gamma a MyDirectiveGamma.

Cuando MyDirectiveGamma intenta inyectar MehProvider:

  constructor(@Self() meh: MehProvider) {
    console.log("gamma directive constructor:", meh.name);
  }

Tanto MyDirectiveAlpha como MyDirectiveBeta configuran MehProvider dentro de su matriz de proveedores. Si elimina my-directive-beta de la plantilla, obtendrá un error que indica que Angular no puede encontrar MehProvider. Si luego elimina el decorador @Self de MyDirectiveGamma, Angular encontrará MehProvider desde MyDirectiveAlpha. Por lo tanto, el modificador @Self restringe a Angular a mirar el inyector en el elemento actual.

@Anfitrión

@Host le dice a Angular que debería dejar de buscar proveedores más allá del inyector para la plantilla actual. Para los propósitos de este artículo, llamo a esto el inyector de plantilla, pero la documentación de Angular no usa este término. Este inyector contiene los proveedores del viewProviders matriz del componente. Un componente también puede tener un proveedores array, que configura un inyector que llamaré el inyector de componentes.

Entonces, para este componente:

<my-component></my-component>

Con esta plantilla:

<div>
  <h2>my component</h2>
  <div my-dir-1>
    <div my-dir-2>lorem ipsum...</div>
  </div>
</div>

Asumiendo my-dir-1 corresponde a MyDirective1, y my-dir-2 corresponde a MyDirective2, si MyDirective2 intenta inyectar una dependencia anotada con el modificador @Host:

constructor(@Host() foo: FooProvider) {
...
}

Luego, Angular buscará a través de todos los inyectores de elementos hasta el árbol de elementos, pero no irá más allá del inyector de plantillas de MyComponent. Si no se encuentra el proveedor, asumiendo nuevamente que el modificador @Optional no está presente, se generará un error.

Se seguirá produciendo un error incluso si el proveedor existe dentro del inyector de componentes porque Angular no buscará allí. Por lo tanto, podemos concluir que el inyector de componentes está un nivel por encima del inyector de plantilla.

El caso de uso de @Host es garantizar que el componente contenedor de una directiva tenga el control de cómo se inyecta un servicio en particular.

Prueba

Considere MyComponent:

@Component({
  selector: "my-component",
  providers: [{provide: FooProvider, useValue: {name: 'FooProvider from providers'}}],
  viewProviders: [{provide: FooProvider, useValue: {name: 'FooProvider from view-providers'}}],
  template: `
    <div>
      <h2>This is my component</h2>
      <div>
        <h3 my-directive>Lorem Ipsum...</h3>
      </div>
    </div>
  `,

})
export class MyComponent {}

Dejar my-directive corresponden a MyDirective. Dado que MyDirective intenta inyectar FooProvider y usa el modificador @Host:

  constructor(@Host() foo: FooProvider) {
    console.log("my directive:", foo.name);
  }

La instancia real de FooProvider que se inyecta es la que se encuentra dentro de la matriz viewProviders. Si comentamos esta matriz, obtenemos un error que nos dice que Angular no puede encontrar el proveedor, aunque todavía existe dentro de la matriz de proveedores. Por lo tanto, @Host evita que Angular busque proveedores más allá del inyector de plantilla de un componente.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *