How to access ViewChild reference defined on parent component - Angular 2

眉间皱痕 提交于 2020-07-18 20:26:29

问题


I have a spinner/loader element defined in app template (root component's template) like

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

In my child components, I am trying to access it using @ViewChild but that seems to always return undefined. My code for accessing this in child component is

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

However when I place my #spinner in my child component's HTML, it gets picked up correctly.

Is there a way to get the element defined in parent component in your child component as a ContainerRef?

I need the view reference to create the component on it dynamically using ComponentFactoryResolver.

It seems a similar issue but can't find a way to overcome.

EDIT: I am now using a shared service with the observable, but still it doesn't raise an event on .next.

Here is my code in 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
    };
}

In SpinnerService, I have

@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);
    }
}

And in the calling component, I have

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

    constructor(public spinnerService: SpinnerService) {  }

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

In app.module.ts (root module)

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

回答1:


Accessing data from other components does not sounds good to me.

For what you are trying to do probably best would be to define service which will share observable:

@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();
    }
}

Then in your root component you will subscribe to

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

And now in all other places you will call

eventServiceInstance.showSpinner()
eventServiceInstance.hideSpinner()

PS. To make it works EventService provider should be added in NgModule and not inside of components




回答2:


Although it's better to use either Output parameters or a common service for this purpose its possible to inject a component from a child component by:

  1. Inject the app component to the child component
  2. In the app component add a ViewChild to the wrapper element and make it accessible
  3. From the child create the new component with ComponentFactoryResolver by calling createComponent on the wrapper element ViewContainerRef
  4. add the loaded component to entryComponents of the module

code is available in 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;
}

}

Child Component:

@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);
}


}


来源:https://stackoverflow.com/questions/42049792/how-to-access-viewchild-reference-defined-on-parent-component-angular-2

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!