Data-driven recursive tree. Pass a TreeNode[] via [items] and supply an *bsTreeviewNode template to render each node body with arbitrary Angular content (icons, labels, badges, custom layout). Two-way bindings [(expandedIds)] and [(selectedIds)] let you control state.
It has no shadow root. Anything you render into it — row templates, node templates, cell renderers — stays in the document, so your app's stylesheet, your own component styles and Bootstrap's utility classes reach it normally.
Its own CSS is scoped at build time onto a data-mps attribute, the same device Angular uses for _ngcontent, so it cannot leak out onto your markup.
The trade-off runs the other way: page CSS now reaches this component's internals, exactly as emulated encapsulation behaves everywhere else. ::part() and ::slotted() no longer address it — use ordinary CSS selectors instead.
<bs-treeview [items]="items()" [(expandedIds)]="expandedIds">
<ng-container *bsTreeviewNode="let node = node">
{{ node.label }}
</ng-container>
</bs-treeview>
import { Component, signal } from '@angular/core';
import {
BsTreeviewComponent,
BsTreeviewNodeDirective,
type TreeNode,
} from '@mintplayer/ng-bootstrap/treeview';
@Component({
selector: 'my-treeview-demo',
templateUrl: './my-treeview-demo.component.html',
imports: [BsTreeviewComponent, BsTreeviewNodeDirective],
})
export class MyTreeviewDemoComponent {
protected readonly items = signal<TreeNode[]>([
{
id: 'inbox',
label: 'Inbox',
children: [
{ id: 'customers', label: 'Customers' },
{ id: 'coworkers', label: 'Co-Workers' },
],
},
{ id: 'drafts', label: 'Drafts' },
]);
protected readonly expandedIds = signal<string[]>(['inbox']);
}