如何访问在父组件上定义的ViewChild引用 - Angular 2

时间:2017-02-05 08:02:06

标签: angular angular2-template

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

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

在我的子组件中,我试图使用@ViewChild访问它,但似乎总是返回undefined。我在子组件中访问它的代码是

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

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

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

我需要视图引用,使用ComponentFactoryResolver动态创建组件。

这似乎是similar问题,但无法找到克服的方法。

编辑:我现在正在使用带有observable的共享服务,但它仍然没有在.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 { }

2 个答案:

答案 0 :(得分:7)

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

对于您要做的事情,最好的方法是定义将共享可观察的服务:

@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提供程序,而不是在组件

答案 1 :(得分:1)

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

  1. 将app组件注入子组件
  2. 在应用程序组件中,将ViewChild添加到包装器元素并使其可访问
  3. 通过在包装器元素ComponentFactoryResolver上调用createComponent,从子项创建包含ViewContainerRef的新组件
  4. 将加载的组件添加到模块的entryComponents
  5. 代码可在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);
    }
    
    
    }