问题
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:
- Inject the app component to the child component
- In the app component add a
ViewChild
to the wrapper element and make it accessible - From the child create the new component with
ComponentFactoryResolver
by callingcreateComponent
on the wrapper elementViewContainerRef
- 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