【问题标题】:Accessing a property in a parent Component访问父组件中的属性
【发布时间】:2016-05-17 23:14:12
【问题描述】:

我在顶级组件中有一个属性,它使用来自 HTTP 源的数据,如下所示(这是在一个名为 app.ts 的文件中):

import {UserData} from './services/user-data/UserData';

Component({
    selector: 'app', // <app></app>
    providers: [...FORM_PROVIDERS],
    directives: [...ROUTER_DIRECTIVES],
    pipes: [],
    template: require('./app.html')
})
@RouteConfig([
    // stuff here
])

export class App {
    // Please note that UserData is an Injectable Service I have written
    userStatus: UserStatus;

    constructor(private userData: UserData) {
        this.userStatus = new UserStatus();
    }

    ngOnInit() {
        this.userData.getUserStatus()
            .subscribe(
            (status) => {
                this.userStatus = status; // I want to access this in my Child Components...
            },
            (err) => {console.log(err);},
            () => {console.log("User status complete");            }
        );
    }
}

现在,我有另一个组件,它是顶级组件的直接子组件,我想在其中访问父组件的属性“userStatus”,这是子组件:

Component({
    selector: 'profile',
    template: require('app/components/profile/profile.html'),
    providers: [],
    directives: [],
    pipes: []
})

export class Profile implements OnInit {
    constructor() {

    }

    ngOnInit() {
        // I want to have access with the parent App Component, 'userStatus' propety here... I only want to read this property
    }
}

现在在 Angular 1.x 中,这很容易,因为我可以在我的子控制器中引用 $parent 或(ANTI PATTERN ALERT!!!)我可能会很愚蠢地将这些数据放入我的$rootScope

在 Angular 2 中访问父级的最佳方式是什么?

【问题讨论】:

    标签: typescript angular


    【解决方案1】:

    您可以通过多种方式找到对父组件的引用。

    @Input() dialogInput: DialogComponent; 
    
    constructor(
     @Host() private dialogHost: DialogComponent,
     private dialogDirect: DialogComponent,
     @Inject(DialogComponent) private dialogInjected: DialogComponent,
     @Inject(forwardRef(() => DialogComponent)) private dialogForward: DialogComponent
     private injector: Injector
    )
          
    ngAfterViewInit(){
     const dialogInjector: DialogComponent = this.injector.get<DialogComponent>(DialogComponent);
    }
    

    如果您希望组件在内容投影中也可用,这是行不通的。我认为您需要为此提供服务,以便您可以使用 DI

    【讨论】:

      【解决方案2】:

      在子组件上声明#contentChild,然后在父组件上声明@ContentChild('contentChild') childContent;

      然后:this.childContent.whatever 让您可以完全访问孩子。

      【讨论】:

        【解决方案3】:

        我制作了一个通用组件,我需要在其中引用使用它的父级。这是我想出的:

        在我的组件中,我创建了一个 @Input :

        @Input()
        parent: any;
        

        然后在父组件中使用这个组件:

        <app-super-component [parent]="this"> </app-super-component>
        

        在超级组件中,我可以使用来自父级的任何公共事物:

        属性:

        parent.anyAttribute
        

        功能:

        parent[myFunction](anyParameter)
        

        当然不能访问私人的东西。

        【讨论】:

        • 这是一个简单直接的解决方案,可以从子组件访问父组件。谢谢。
        【解决方案4】:

        由于父子交互不是一件容易的事。但是通过在子组件中引用父组件,交互起来会容易得多。在我的方法中,首先需要通过调用子组件的函数来传递父组件的引用。这是此方法的说明。

        子组件的代码。

            import { Component, Input,ElementRef,Renderer2 } from '@angular/core';
        
            import { ParentComponent } from './parent.component';
        
            @Component({
              selector: 'qb-child',
              template: `<div class="child"><button (click)="parentClickFun()">Show text Of Parent Element</button><button (click)="childClickFun()">Show text Of Child Element</button><ng-content></ng-content></div>`,
              styleUrls: ['./app.component.css']
            })
            export class ChildComponent  {
              constructor(private el: ElementRef,private rend: Renderer2){
        
              };
        
              qbParent:ParentComponent; 
              getParent(parentEl):void{
                this.qbParent=parentEl;
                console.log(this.el.nativeElement.innerText);
              }
              @Input() name: string;
              childClickFun():void{
                console.log("Properties of Child Component is Accessed");
              }
        
              parentClickFun():void{
                this.qbParent.callFun();
              }
            }
        

        这是父组件的代码

        import { Component, Input , AfterViewInit,ContentChild,ElementRef,Renderer2} from '@angular/core';
        
        import { ChildComponent } from './child.component';
        
        @Component({
          selector: 'qb-parent',
          template: `<div class="parent"><ng-content></ng-content></div>`,
          styleUrls: ['./app.component.css']
        })
        export class ParentComponent implements AfterViewInit {
          constructor(private el: ElementRef,private rend: Renderer2){
        
          };
          @Input() name: string;
          @ContentChild(ChildComponent,{read:ChildComponent,static:true}) qbChild:ChildComponent;
        
          ngAfterViewInit():void{
            this.qbChild.getParent(this);
          }
        
          callFun():void{
            console.log("Properties of Parent Component is Accessed");
          }
        
        }
        

        HTML 代码

        <qb-parent>
          This is Parent
          <qb-child>
            This is Child
          </qb-child>
        </qb-parent>
        

        这里我们通过调用子组件的函数来传递父组件。 下面的链接是这种方法的一个例子。 点击here

        【讨论】:

          【解决方案5】:

          在 Angular 6 上,我通过构造函数注入父级来访问父级属性。不是最好的解决方案,但它有效:

           constructor(@Optional() public parentComponentInjectionObject: ParentComponent){
              // And access like this:
              parentComponentInjectionObject.thePropertyYouWantToAccess;
          }
          

          【讨论】:

          • 但是你必须知道你的父母是谁
          • 这将创建一个循环依赖。
          • 这是 Angular Material 源代码中广泛使用的方法。如果您将子项注入到父项中,它只会创建循环依赖项......不遵循如何创建循环依赖项???
          【解决方案6】:

          我遇到了同样的问题,但我以不同的方式解决了它。我不知道这是否是一种好方法,但它非常适合我的需要。

          我在子组件的构造函数上使用了@Inject,像这样:

          import { Component, OnInit, Inject } from '@angular/core';
          import { ParentComponent } from '../views/parent/parent.component';
          
          export class ChildComponent{
              constructor(@Inject(ParentComponent) private parent: ParentComponent){
          
              }
          
              someMethod(){
                  this.parent.aPublicProperty = 2;
              }
          }
          

          这对我有用,您只需要将要调用的方法或属性声明为公共。

          在我的例子中,AppComponent 处理路由,我在菜单项中使用标记来提醒用户有新的未读消息可用。因此,每次用户阅读消息时,我都希望该计数器刷新,因此我调用 refresh 方法,以便菜单导航处的数字使用新值更新。这可能不是最好的方法,但我喜欢它的简单性。

          【讨论】:

          • 这是一个很好的解决方法,但它不是解决方案,因为在大多数情况下,组件父级会随着您想要重用组件而变化。
          • 请注意,如果您在同一个文件中定义了 ParentComponent 但在子项之后,您需要将其移动到子项上方定义,或者如果存在循环引用,则需要使用 forwardRef .即@Inject(forwardRef(() =&gt; ParentComponent)) parent: ParentComponent
          • @Inject(ParentComponent) 是强制性的吗?由于指定了类型,这似乎没有必要,我错过了什么吗?
          • 如果你在子页面加载你的网站,那么如果它是从异步操作加载的,那么父属性将是未定义的,所以你需要处理这方面。
          【解决方案7】:

          有不同的方式:

          export class Profile implements OnInit {
          constructor(@Host() parent: App) {
            parent.userStatus ...
          }
          
          • 数据绑定
          export class Profile implements OnInit {
            @Input() userStatus:UserStatus;
            ...
          }
          
          <profile [userStatus]="userStatus">
          

          【讨论】:

          • Angular2 非常注重性能。 Angular2 正在(或将会——还有很多工作要做)被优化,因此即使是大型应用程序也能表现良好。还支持 TS 和 Dart(它们比 JS 更适合大型应用程序)带来了一些样板和限制,但这也允许分析代码并进行更智能的优化。无影无光……
          • @GünterZöchbauer,我很高兴你指出了@Inputs 的“缺点:紧密耦合”,但我希望你承认 使用服务的缺点 - 即与 Angular 的依赖注入系统的便利性有关。 读者应该知道,具有高链接度,即扇出/-输入同样危险。使用直接输入链接,您至少要遵守 LoD (得墨忒耳法则),但我会为 every 模块构建一个沙盒或 Director/Facade,这样它们就不会扇出大量服务;因为那是你真的很兴奋的时候。
          • 肯定是@Host() parent: App的紧密耦合问题,而不是使用@Input()的数据绑定技术?
          • 注意:如果你需要@Host()是可选的,你的代码必须是@Optional @Host() parent: App
          【解决方案8】:

          你可以:

          • 为子组件定义一个userStatus参数,并在父组件使用该组件时提供该值:

            @Component({
              (...)
            })
            export class Profile implements OnInit {
              @Input()
              userStatus:UserStatus;
            
              (...)
            }
            

            在父级中:

            <profile [userStatus]="userStatus"></profile>
            
          • 将父组件注入子组件:

            @Component({
              (...)
            })
            export class Profile implements OnInit {
              constructor(app:App) {
                this.userStatus = app.userStatus;
              }
            
              (...)
            }
            

            注意它们之间的循环依赖关系。

          【讨论】:

          • 这对我来说没有多大意义。你的父类 Profile 上有一个属性,然后你说你将它注入到你的子组件 Profile 中?!?!?看起来您正在将名为 userSatus 的绑定绑定到具有配置文件选择器的组件,但您正在将 App 类注入到名称为 Profile 的组件中,没有绑定。根据您的描述,我想您有一个父类(定义 userStatus 的类)和一个子类,该子类在其构造函数中设置本地值,您应该在构造函数中注入父类。是这样吗?
          猜你喜欢
          • 1970-01-01
          • 2014-04-05
          • 2019-06-30
          • 2020-06-17
          • 2020-09-30
          • 1970-01-01
          • 2019-11-23
          • 2016-05-29
          • 2021-01-22
          相关资源
          最近更新 更多