I had a similar problem, whereby I have a card component, which has a child card-header component as well as a selector for the card body.
The card-header component has a toggle button that dispatches actions for which cards are open / closed.
I then needed the ability to pass extra buttons into the card-header component from the parent component via the card component
I solved it by adding selectors at each level.
First I created a common card-header component, allowing me to have a single piece of code that handled toggling card content by dispatching actions to the NgRx store, which holds an array of cards that are hidden (using the supplied name input property).
The card-header component subscribes to the store and emits an event to the parent component when the toggled
status changes
@Component({
selector: 'po-card-header',
template: `
<div class="card-header">
<span class="text-uppercase">{{ header }}</span>
<div class="header-controls">
<ng-content select=[card-header-option]></ng-content>
<ng-content select=[header-option]></ng-content>
<span class="header-action" (click)="onTogglePanel()">
<i class="fa" [ngClass]="{ 'fa-caret-up': !collapsed, 'fa-caret-down': collapsed}"></i>
</span>
</div>
</div>
`
})
export class CardHeaderComponent implements OnInit, OnDestroy {
...
@Input() name: string;
@Output() togglePanel = new EventEmitter<boolean>();
collapsed$: Observable<boolean>;
collapsedSub: Subscription;
constructor(private store: Store<State>) {
this.collapsed$ = this.store.select(state => getPanelCollapsed(state, this.name);
}
ngOnInit(): void {
this.collapsedSub = this.collapsed$.subscribe(collapsed => {
this.collapsed = collapsed;
this.togglePanel.emit(collapsed);
});
}
.... unsubscribe on destroy.
}
Notice the header has 2 ng-content
sections.
The header-option
selector is for any other icons I want to add when I explicitly use this component e.g.
<div class="card">
<po-card-header>
...
<span header-option class="fa fa-whatever" (click)="doSomething()"></span>
</po-card-header>
...
</div>
My new icon will sit alongside the default toggle icon in the header.
The second card-header-option
selector is for root components, that use the card component, not the card-header component, but still want to pass extra icons into the header.
@Component({
selector: 'po-card',
template: `
<div class="card">
<po-card-header>
...
<ng-content select="[card-header-option] header-option></ng-content>
</po-card-header>
<div class="card-block">
<ng-content select="[card-body]"></ng-content>
</div>
</div>
`
})
...
The [card-header-option]
selector will select any elements with that attribute, then pass them down into the card-header component using the header-option
attribute
The final usage of my card component looks like this.
<div>
Some component that uses the card
<po-card
header="Some text to go in the card header"
name="a-unique-name-for-the-card">
<span card-header-option class='fa fa-blah header-action'></span>
<div card-body>
Some content that gets put inside the [card-body] selector on the card component.
</div>
</po-card>
</div>
The final result is that I can use my custom card component, and get the benefits of the toggle functionality that the card-header component gives, but also supply my own custom actions, which will also get rendered in the header
Hope you find this helpful :-)