【问题标题】:How to access ViewChild reference defined on parent component - Angular 2如何访问父组件上定义的 ViewChild 引用 - Angular 2
【发布时间】:2017-02-05 08:02:05
【问题描述】:

我在应用模板(根组件的模板)中定义了一个微调器/加载器元素,例如

<!--I have it here so that I don't have to paste it in all my templates-->
<div #spinner></div>

在我的子组件中,我尝试使用@ViewChild 访问它,但这似乎总是返回未定义。我在子组件中访问它的代码是

@ViewChild('spinner', { read: ViewContainerRef }) container: ViewContainerRef;  //this is always undefined

但是,当我将 #spinner 放在子组件的 HTML 中时,它会被正确拾取。

有没有办法将子组件中父组件中定义的元素作为ContainerRef

我需要视图引用以使用ComponentFactoryResolver 在其上动态创建组件。

这似乎是一个similar 问题,但找不到解决方法。

编辑:我现在正在使用可观察的共享服务,但它仍然没有在 .next 上引发事件。

这是我在SpinnerComponent中的代码

@Component({
    selector: 'spinner',
    styleUrls: ['app/styles/spinner.component.css'],
    template:
    `<div [hidden]="state.visible" class="in modal-backdrop spinner-overlay"></div>
     <div class="spinner-message-container" aria-live="assertive" aria-atomic="true">
        <div class="spinner-message" [ngClass]="spinnerMessageClass">{{ state.message }}</div>
    </div>`
})
export class SpinnerComponent {

    constructor(spinnerService: SpinnerService) {
        spinnerService.spinnerStatus.subscribe(event => {
            console.log('Event: ' + event); <= not getting called
            this.state.visible = event;
        });
    }

    public state = {
        message: 'Please wait...',
        visible: false
    };
}

在 SpinnerService 中,我有

@Injectable()
export class SpinnerService {
    public events: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);

    public get spinnerStatus(): Observable<boolean> {
        return this.events.asObservable();
    }

    public showSpinner() {
        this.events.next(true);
    }

    public hideSpinner() {
        this.events.next(false);
    }
}

在调用组件中,我有

@Component({
    selector: 'edit-auction',
    templateUrl: '/auctions/edit.html'
})
export class EditAuctionComponent {

    constructor(public spinnerService: SpinnerService) {  }

    ngAfterViewInit() {
        //start the spinner
        this.spinnerService.showSpinner();
    }
}

在 app.module.ts(根模块)中

@NgModule({
    imports: [BrowserModule, FormsModule, HttpModule, routes],
    declarations: [..],
    providers: [NotificationsService, SpinnerService],
    bootstrap: [AppComponent]
})
export class AppModule { }

【问题讨论】:

  • 顾名思义,@ViewChild 只能用于选择孩子。你想用这个来达到什么目的?如果要显示/隐藏微调器,为什么不使用自定义微调器组件,在其中注入服务发射事件来显示/隐藏微调器。看看这个例子:stackoverflow.com/a/42041945/1153681
  • 是否调用了 ngAfterViewInit?
  • 是的,我在 showSpinner 之前添加了一个警报,它被调用了
  • 是否调用了 SpinnerComponent.constructor?
  • 不,这不会被调用!当我把它放在应用程序组件中时,它似乎可以工作!

标签: angular angular2-template


【解决方案1】:

从其他组件访问数据对我来说听起来不太好。

对于您尝试做的最好的事情可能是定义将共享可观察的服务:

@Injectable()
export class EventService {
    public selectedCategoryName: string = '';
    private events = new BehaviorSubject<Boolean>(false);
    constructor() {

    }

    public showSpinner(){
        this.events.next(true)
    }

    public hideSpinner(){
        this.events.next(false);
    }

    public get spinnerStatus() : Observable<boolean> {
        return this.events.asObservable();
    }
}

然后在你的根组件中订阅

eventServiceInstance.spinnerStatus.subscribe(state=>{
            //thisSpinner.visible = state
        })

现在在所有其他地方你都会打电话

eventServiceInstance.showSpinner()
eventServiceInstance.hideSpinner()

PS。为了使其正常工作,应在 NgModule 中添加 EventService 提供程序,而不是在组件内部

【讨论】:

  • 我同意!我已经在使用通过动态组件加载阻塞 UI 的共享服务!为了阻止完整的视图,我不想在我的模板中编写组件调用,所以我将#spinner 放在我的根组件中
  • 当我从调用组件调用showSpinner() 时,我没有在已订阅它的微调器组件中引发事件,有什么想法吗?
  • @AliBaig 您是否在组件或 NGModule 中添加了 EventService 提供程序?
  • 是的,我的服务名称是SpinnerService,我在两个组件中都将其导入providers
  • @AliBaig 将它们添加到 NGModule 提供者中,那么您将在所有模块中拥有相同的实例,因为现在它为每个模块创建了您自己的服务
【解决方案2】:

尽管为此目的使用Output 参数或公共服务更好,但可以通过以下方式从子组件注入组件:

  1. 将app组件注入到子组件中
  2. 在应用程序组件中,将ViewChild 添加到包装器元素并使其可访问
  3. 通过在包装元素ViewContainerRef 上调用createComponent,从子组件中创建带有ComponentFactoryResolver 的新组件
  4. 将加载的组件添加到模块的entryComponents

代码在plunker中提供

app.ts:

@Component({
selector: 'my-app',
template: `
    <div>
    <h2>App/h2>
    <my-child></my-child>
    <div #spinner></div>
    </div>
`,
})
export class App {
@ViewChild('spinner', { read: ViewContainerRef }) private spinner: any;

public getSpinnerRef() {
    return this.spinner;
}

}

子组件:

@Component({
selector: 'my-child',
template: `
    <div>
    <h3>Child</h3>
    </div>
`,
})
export class ChildCmp implements OnInit {

constructor(private app: App, private componentFactoryResolver: ComponentFactoryResolver) {

}

public ngOnInit() {
    const spinnerCmp = this.componentFactoryResolver.resolveComponentFactory(SpinnerCmp);
    this.app.getSpinnerRef().createComponent(spinnerCmp);
}


}

【讨论】:

    猜你喜欢
    • 2017-11-25
    • 2020-06-17
    • 1970-01-01
    • 2018-01-24
    • 2017-04-12
    • 2017-01-08
    • 2019-07-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多