【问题标题】:Angular 2 - Injecting a component created by a HTML selectorAngular 2 - 注入由 HTML 选择器创建的组件
【发布时间】:2016-08-09 03:40:12
【问题描述】:

我正在尝试访问在某些 HTML 中使用选择器创建的组件。我的印象(似乎是错误的)提供者会寻找现有实例并在注入另一个组件时提供该实例,但显然我误解了分层提供者创建过程。

在下面,我有一个组件,它在其模板 HTML 中使用选择器来创建 TopLevelComponent 的实例。

我正在尝试通过创建提供程序并使用 DI 通过构造函数推送该实例来访问该 TopLevelComponent。

(抱歉,如果这段代码不完美,我只是整理了一个简单的例子。)

@component({
    selector: 'my-app',
    template: '<top-level-component></top-level-component>',

    directives: [TopLevelComponent],
    providers: [TopLevelComponent],
})
export class MyApp {

    constructor( private topLevelComponent: TopLevelComponent) {

    }
}

@component({
    selector: 'top-level-component',
    template: '',
})
export class TopLevelComponent {

    constructor() {
        console.log('CONSTRUCTED A TopLevelComponent...');
    }

}

但是,我没有将一个 TopLevelComponent 实例传递给我的组件,而是得到了两个(调试日志中存在两个“CONSTRUCTED A TopLevelComponent ...”日志证明了这一点)。

如果我从构造函数中删除 'private topLevelComponent: TopLevelComponent',我只会得到该组件的一个实例,但我似乎无法掌握它。

所以我有两个问题

  • 如何使用DI将选择器创建组件的实例传递给其他组件的构造函数

  • 如果我没有在应用程序 HTML 中包含 &lt;top-level-component&gt;&lt;/top-level-component&gt;,我如何将该 HTML 注入应用程序以使其正确呈现?

第三个

  • 如果以上两种方法都可以,推荐哪种方法?

我认为@ViewChild 是正确的方法,因为(!)这个组件只有一个实例,但由于它被注入到许多不同的组件中,我认为这是不可能的。

【问题讨论】:

  • 您能否提供一个 Plunker 来演示您究竟想要完成什么?
  • @GünterZöchbauer 这是 Plunker 显示我正在尝试做的事情 - embed.plnkr.co/J3G1VBDW0p4FGovLBnI7。如您所见,“CONSTRUCTED An InjectingComponent...”被打印了两次。我试图将由“”创建的“InjectingComponent”实例传递给“ReceivingComponent”,但我得到了一个单独的组件。鉴于 InjectingComponent 可以传递给多个其他组件(而不仅仅是本示例中的 ReceivingComponent),我认为 ViewChild 不适合。
  • 我已经提到过给providers添加一个组件是没有用的。您也不能注入兄弟姐妹,只能注入祖先。请参阅我的更新答案以获取可能的解决方案。

标签: javascript html angular typescript


【解决方案1】:

如果您将组件添加到providers: [...],则该组件将被视为普通类。如果 DI 在请求 TopLevelComponent 时找到这样的提供程序,它会创建组件类的实例 @Component(...) 装饰器被忽略。

如果一个组件在directives: [...] 中列出,如果它们因匹配选择器而被实例化,DI 会将它们视为组件和指令。

更新

  <component-to-inject #source></component-to-inject>
  <component-to-receive [injectedComponent]="source"></component-to-receive>

export class ReceivingComponent { 

  @Input() injectedComponent: InjectingComponent;

  ngOnInit() {
    console.log(this.injectedComponent);
  }      
}

【讨论】:

  • 我现在将提出一个 Plunker 示例,但与此同时(因为我不确定我是否理解您的评论),如果我删除 'providers: [TopLevelComponent]' 并留在 'directives : [TopLevelComponent]' 然后我得到以下异常 - 'ORIGINAL EXCEPTION: No provider for TopLevelComponent!'
  • 如果组件是当前组件的子组件,注入将不起作用。然后你必须使用@ViewChild()@ViewChildren()。 DI 只寻找祖先。
  • 太好了,谢谢。将注入的组件作为输入传递给接收组件正是我所需要的。
【解决方案2】:

为了获得对模板中存在的组件的引用,您可以使用 @ViewChild 注释。

@ViewChild(TopLevelComponent)
private myTopLevelComponent:TopLevelComponent

这将为您提供 Angular 在相应模板中找到的第一个 TopLevelComponent 实例。

如果您有多个相同类型的组件,但您想找到某个组件,您可以使用本地模板变量。

@ViewChild('templateVariableName')
private myTopLevelComponent:TopLevelComponent

您的 html 模板需要如下所示:

<top-level-component #templateVariableName></top-level-component>

最后: 如果要查找所有相同类型的组件,可以使用@ViewChildren 注解。

@ViewChildren(TopLevelComponent)
private topLevelComponents:QueryList<TopLevelComponent>

这将为您提供模板中所有 TopLevelComponents 的列表。

为什么在您的示例中创建了两个实例?

使用 providers 数组并要求将该类注入到您的构造函数中,这会告诉 Angular 创建组件类的实例。 当 Angular 解析您的模板时,将创建第二个实例。

请注意,您通过构造函数注入的实例与模板中存在的实例不同。您对注入的实例所做的任何更改都不会反映在屏幕上。

要获得正确的实例,您需要按照我上面提供的步骤操作。

【讨论】:

    【解决方案3】:

    angular2 传递提供者中的服务列表。服务是可重用的代码sn-p。

    可用于任何指令/组件的基本服务示例。

    imports ...
    
        @Injectable()
        export class MyEmpService {
            getAllEmployees() {
                // hit database
                // return employee list
            }
            getEmp(id: number) {
                // hit database
                // return employee data 
            }
    }
    

    Component 是一个带有视图的指令。 Angular2 在指令中传递组件列表。列表中的任何组件都可以在组件中使用。同样,它是可重用的代码,但有视图。 简单的例子: EmployeeComponent.ts

    imports...
    
    @Component({
        selector: 'my-emp',
        template: `
            <button (click)="getEmpList()">Get All Emp</button>
            <button (click)="getEmp(2)">Get Emp</button>
            <div>
                {{result | json}}
            </div>
        `,
        providers: [MyEmpService]
    })
    
    export class EmployeeComponent{
        private result:any = '';
        constructor(private myEmpService: MyEmpService) { }
    
        getEmpList(){
            this.result = myEmpService.getAllEmployees();
        }
        getEmp(id: number){
            this.result = myEmpService.getEmp(id);
        }
    }
    

    AppComponent.ts

    imports...
    
    @Component({
        selector: 'my-app',
        template: `
            <h1>Main Component</h1>
            <my-emp></my-emp>
        `,
        directives: [EmployeeComponent]
    })
    
    export class AppComponent{ }
    

    【讨论】:

    • 在我的情况下,我没有服务,但是我想将之前基于选择器的创建组件传递给另一个组件
    • AppComponent 正在使用 EmployeeComponent
    猜你喜欢
    • 2016-05-19
    • 2016-03-20
    • 1970-01-01
    • 2017-01-24
    • 2016-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多