首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >@Self与@宿主角2+依赖注入解码器的区别

@Self与@宿主角2+依赖注入解码器的区别
EN

Stack Overflow用户
提问于 2017-05-01 23:41:34
回答 5查看 13.7K关注 0票数 24

请解释一下@Self@Host的区别。

角API文档给出了一些想法。但我不太清楚。为Self提供的示例使用ReflectiveInjector来举例说明使用情况。

然而,如果有的话,很少会在实际的应用程序代码中使用ReflectiveInjector (可能更多的是在测试中)。您能给出一个示例,说明在这样的测试场景之外,您将在何处使用@Self 而不是 of @Host吗?

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2017-07-16 17:25:05

tl;dr

看起来,当使用@Self时,角只会为该指令/组件存在的元素寻找一个绑定在组件注入器上的值。

看起来,当使用@Host时,角将查找一个值,该值要么绑定在该指令/组件存在的元素的组件注入器上,要么绑定在父组件的注入器上。角称这个父组件为“主机”。

更多解释

虽然主要的描述不是很有帮助,但是@Self@主机文档中的示例在澄清它们的用法和区别方面做得不错(下面是复制的)。

当试图理解这一点时,它可能有助于记住,当角依赖注入试图解析构造函数的一个特定值时,它从查找当前组件的注入器开始,然后通过父注入器向上迭代。这是因为角使用分层注入器,并允许从祖先注入器继承。

因此,当@Host文档说它“指定注入器应该从任何注入器中检索依赖项,直到到达当前组件的主机元素为止”时,这意味着一旦到达绑定到父组件的注入器,它就会提前停止向上迭代。

@Self示例(来源)

代码语言:javascript
复制
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示例(来源)

代码语言:javascript
复制
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 {
}

使用“Self”的示例非常重要

假设您有一个指令,用于修改多种类型组件的行为;也许这个指令提供了某种配置支持。

该指令绑定到整个应用程序中的许多组件,并且该指令在其providers列表中绑定了一些服务。希望使用此指令动态配置自身的组件将注入它提供的服务。

但是,我们希望确保组件只使用自己的配置,而不会意外地注入用于某些父组件的配置服务。因此,我们使用@Self装饰器来告诉角的依赖注入,以便只考虑该组件元素上提供的配置服务。

票数 26
EN

Stack Overflow用户

发布于 2020-11-15 18:13:37

通过在元素注入器层次结构中搜索依赖项来解决依赖关系,从当前元素的注入器开始,如果没有找到父元素,则移动到父元素的依赖项,依此类推。如果仍然没有找到依赖项,它就会移动到模块注入器上。如果在那里找不到,就会抛出一个错误。https://angular.io/guide/hierarchical-dependency-injection#host

@Self和@Host是一种修饰符,用于判断它应该停止寻找依赖项的注入器的角度。

@Self

@Self告诉角,它应该只在当前元素的注入器内查找。关于这一点,需要注意的一点是,每个元素只有一个注射器,由附加到它的每个指令共享。因此,在这个模板片段中:

代码语言:javascript
复制
<div dir-1 dir-2></div>

假设dir-1对应于Directive1,dir-2对应于Directive2,如果Directive1注册了一个提供者,那么Directive2将能够注入该服务,反之亦然。

如果依赖项具有@Self修饰符,这意味着角将只在当前元素的注入器中查找提供程序。除非@可选修饰符也存在,否则如果找不到它,就会引发错误。

@Self的用例是,只有在同一元素上的另一个指令提供的情况下,才希望将服务注入指令或组件。(指令显然可以提供服务本身,但这似乎使@Self的使用有点多余)。

证明

https://stackblitz.com/edit/angular-di-test-4-6jxjas?file=src%2Fapp%2Fapp.component.htmlapp.component.html中考虑了这个模板

代码语言:javascript
复制
<div my-directive-alpha>
    <h1 my-directive-beta my-directive-gamma>Lorem Ipsum..</h1>
</div>

my-directive-alpha对应于MyDirectiveAlpha,my-directive-beta对应于MyDirectiveBeta,my-directive-gamma对应于MyDirectiveGamma。

当MyDirectiveGamma试图注入MehProvider时:

代码语言:javascript
复制
  constructor(@Self() meh: MehProvider) {
    console.log("gamma directive constructor:", meh.name);
  }

MyDirectiveAlpha和MyDirectiveBeta都在其提供程序数组中配置MehProvider。如果你从模板中删除我的指令-β,你会得到一个错误,说角找不到MehProvider。如果然后从MyDirectiveGamma中删除@Self装饰符,remove将在MyDirectiveAlpha中找到MehProvider。因此,@Self修饰符将角度限制为查看当前元素上的注入器。

@主机

@Host告诉角,它应该停止在注入器之外寻找当前模板的提供者。为了本文的目的,我称它为模板注入器,但是角的文档没有使用这个术语。此注入器包含来自组件的viewProviders数组的提供程序。组件还可能有一个提供者数组,该数组配置我将调用组件注入器的注入器。

因此,对于这个组成部分:

代码语言:javascript
复制
<my-component></my-component>

使用此模板:

代码语言:javascript
复制
<div>
  <h2>my component</h2>
  <div my-dir-1>
    <div my-dir-2>lorem ipsum...</div>
  </div>
</div>

假设my-dir-1对应于MyDirective1,而my-dir-2对应于MyDirective2,则如果MyDirective2试图注入带有@主机修饰符注释的依赖关系:

代码语言:javascript
复制
constructor(@Host() foo: FooProvider) {
...
}

然后角将通过元素树搜索所有元素注入器,但不会超出MyComponent的模板注入器。如果找不到提供程序,再次假设@可选修饰符不存在,则会引发错误。

即使提供程序存在于组件注入器中,仍然会引发错误,因为角不会在那里搜索。因此我们可以得出结论,组件注入器是模板注入器之上的一个级别。

@Host的用例是确保指令的包含组件控制特定服务的注入方式。

证明

https://stackblitz.com/edit/angular-di-host-modifier-proof?file=src%2Fapp%2Fmy-component%2Fmy-component.component.ts

考虑一下MyComponent:

代码语言:javascript
复制
@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 {}

my-directive对应于MyDirective。考虑到MyDirective试图注入FooProvider并使用@Host修饰符:

代码语言:javascript
复制
  constructor(@Host() foo: FooProvider) {
    console.log("my directive:", foo.name);
  }

注入的FooProvider的实际实例是来自viewProviders数组的实例。如果我们注释掉这个数组,我们会得到一个错误,它告诉我们无法找到提供者,即使它仍然存在于提供者数组中。因此,@Host防止了角从组件的模板注入器之外寻找提供者。

票数 10
EN

Stack Overflow用户

发布于 2017-05-01 23:48:21

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

主机:

@主机-@主机装饰符告诉DI在任何注入器中查找依赖项,直到它到达主机

赛尔夫:

@Self - @Self装饰器告诉DI只从自身寻找依赖项,这样它就不会走上树。

以下是一个例子:

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

正如您看到的,MyDir指令使用:@Self访问自己的Car,它的组件“@”可选@Host依赖项,它不是在主机上定义的,而是在应用程序上定义的。因为它不是在主机上定义的-它将是空的

产出将是:

代码语言:javascript
复制
 parent component. 
  { "type": "child garage", 
    "car": { "model": "child car" }, 
    "sun": null 
  }

以下是组件和提供者:

代码语言:javascript
复制
  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 {
  }
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43728007

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档