I am in the middle of a project building a Header component in Angular which has 2 levels of navigation. The basic component structure is as follows:
<b-header>
<b-header-nav>
<b-header-nav-item>
<button b-header-link>Primary 1<button>
</b-header-nav-item>
<b-header-nav-item>
<button b-header-link>Primary 2<button>
</b-header-nav-item>
<b-header-nav-item>
<button b-header-link>Primary 3<button>
</b-header-nav-item>
</b-header-nav>
</b-header>
When the user clicks on one of the b-header-link
buttons I would like to toggle CSS active state classes on both the button itself and its parent - b-header-nav-item
. I have researched child to parent communication in Angular online but most examples show the use of Output and Event Emitters which I'd ideally not like to use as this work is part of a component library where the inner workings of a component should not really be exposed to the end user. Also as shown the child in this case is added via content projection ng-content
in b-header-nav-item
. I've created a Stackblitz to demo a more featured example of the code (although still simplified compared to the real one).
Any help would be much appreciated,
Thanks
CodePudding user response:
If you don´t like the solution with output and event emitter. I would recommend to use service with behavior subject or viewChild.
Amazing website wich describe each solution Sharing data between angular components
CodePudding user response:
Since you're using Ng-Content, you can handle all the logic in one component. I therefore suggest you try using an array containing all of your buttons data (is active, title, href), then using ngfor to render it.
1 Define NavButton interface :
export interface NavButton {
isActive: boolean;
name: string;
href: string;
subElems: NavButton[];
}
2 Create buttons list and function to toggle the btn's 'active' parameter
navItems: NavButton[] = [{isActive: false, name: 'btn1', href: '', subElems: []}, {isActive: false, name: 'btn2', href: '', subElems: [{isActive: false, name: 'subBtn 2.1', href: '', subElems: []}]}, {isActive: false, name: 'btn3', href: '', subElems: []}, ];
changeState = (navBtn: NavButton) => {
navBtn.isActive = !navBtn.isActive;
}
3 Render nav buttons and subButtons like so :
<b-header>
<b-header-nav>
navItemMap
<b-header-nav-item *ngFor="let currBtn of navItems; let i = index">
<button b-header-link (click)="changeState(currBtn)">
{{currBtn.name}} (number {{ i }})
</button>
<b-header-subnav *ngIf="currBtn.subElems.length > 0">
<b-header-subnav-item *ngFor="let subNavBtn of currBtn.subElems; let i2 = index;">
<a [href]="subNavBtn.href" (click)="changeState(subNavBtn)">{{subNavBtn.name}} (number {{ i2 }})</a>
</b-header-subnav-item>
</b-header-subnav>
</b-header-nav-item>
</b-header-nav>
</b-header>
4 Everything is set up, just add style binding to HTML, for example if you want the "b-header-nav-item" to get a specific CSS class as well as the "b-header-link" once toggled :
<b-header-nav-item *ngFor="....." [class.active]="currBtn.isActive">
<button b-header-link (click)="...." [class.active]="currBtn.isActive"></button>
[...]
</b-header-nav-item>
Note: Here, I've hidden the non-style related logic for visibility
Cheers