I am trying to get my head around Angular2 and Typescript respectively. What I want to do is open a sidenav from a button which is in a child component - here app-header
.
I know I could open it with
<button mat-button (click)="sidenav.open()">Open sidenav</button>
but that would only work if I place this within the parent template as it references the template variable reference sidenav
. However, as I said, I want to open it based on a click on a button of a child.
This would be my layout/app template:
<mat-sidenav-container>
<mat-sidenav #sidenav>
<!-- sidenav content -->
Here comes the menu ..
</mat-sidenav>
<div>
<app-header>
<!-- Application header -->
</app-header>
</div>
</mat-sidenav-container>
And this would be the header template:
<div>
header for {{title}} works!
<button mat-button (click)="sidenav.open()">Open sidenav</button>
</div>
Of course this fails because I cannot reference sidenav
- so how can I access sidenav
from within the child correctly?
Or is passing on such references a "no no" in Angular2 and I should actually use an event based trigger or something like that?
You can't access template variable of child components directly. Template variables can only be referenced in the same tamplate you define them. But if you are trying to get the result (confirm/decline) value from the modal child component, you can do it through @Output decorator and EventEmitter s.
To get started using template reference variables, simply create a new Angular component or visit an existing one. To create a template reference variable, locate the HTML element that you want to reference and then tag it like so: #myVarName .
We declare Template reference variables using # followed by the name of the variable ( #variable ). We can also declare them using #variable="customer" when the component/directive defines a customer as the exportAs Property.
If you want to pass data from the parent component to the child component, you need to use two things: @Input and property binding. In this example, we set in the child component a variable named childExample , which is a string. We set Angular's @Input decorator in front of the variable.
this is normal parent / child component communication. The way you COULD do this is to just expose an output event on the child:
parent.component.html excerpt:
<child-component (openNav)="sidenav.open()"></child-component>
and in the child you just bind the button click to emitting that event:
child.component.ts:
@Output() openNav = new EventEmitter();
child.component.html:
<button (click)="openNav.emit()">Open Nav</button>
HOWEVER, since this is a sidenav that may need to be accessed in a lot of places and maybe even in nested places, you might just want to solve the problem for all situations and use a shared service observable:
side-nav.service.ts:
@Injectable()
export class SideNavService {
private openNavSource = new Subject(); // never expose subjects directly
openNav$ = this.openNavSource.asObservable();
openNav = () => this.openNavSource.next();
}
provide it appropriately, and inject it into the parent and child for use
parent.component.ts:
@ViewChild('sidenav') sidenav; // get your template reference in the component
constructor(private sideNavService: SideNavService) { }
private sideNavSub;
ngOnInit() {
this.sideNavSub = this.sideNavService.openNav$.subscribe(() => this.sidenav.open());
}
ngOnDestroy() {
this.sideNavSub.unsubscribe(); //clean your subscriptions
}
child.component.ts:
constructor(private sideNavService: SideNavService) { }
openNav() {
this.sideNavService.openNav(); // looks repetitive but accessing injectd services in templates is bad practice and will cause pains down the line
}
child.component.html:
<button (click)="openNav()">Open Nav</button>
then you can inject the service into any arbitrary component and use it as needed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With