Skip to content

Commit

Permalink
feat(module:breadcrumb): support auto generated breadcrumb
Browse files Browse the repository at this point in the history
  • Loading branch information
Wendell committed Sep 4, 2018
1 parent 69e1f5f commit 5738207
Show file tree
Hide file tree
Showing 10 changed files with 215 additions and 6 deletions.
18 changes: 18 additions & 0 deletions components/breadcrumb/demo/auto.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
order: 4

iframe:
source: https://stackblitz.com/edit/ng-zorro-breadcrumb-auto?embed=1&file=src/app/app.component.html&hideExplorer=1&hideNavigation=1&view=preview
height: 460
title:
zh-CN: 自动生成
en-US: Auto generated breadcrumbs
---

## zh-CN

通过配置 `router.data` 自动生成面包屑。

## en-US

Auto generate breadcrumbs using `router.data`.
11 changes: 11 additions & 0 deletions components/breadcrumb/demo/auto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Component } from '@angular/core';

@Component({
selector: 'nz-demo-breadcrumb-auto',
template: `
<nz-breadcrumb [nzAutoGenerate]="true">
Please refer to StackBlitz demo.
</nz-breadcrumb>
`
})
export class NzDemoBreadcrumbAutoComponent {}
12 changes: 12 additions & 0 deletions components/breadcrumb/doc/index.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,16 @@ A breadcrumb displays the current location within a hierarchy. It allows going b
| Property | Description | Type | Optional | Default |
| -------- | ----------- | ---- | -------- | ------- |
| `[nzSeparator]` | Custom separator | string丨`TemplateRef<void>` | | `/` |
| `[nzAutoGenerate]` | Auto generate breadcrumb | boolean | | `false` |

Using `[nzAutoGenerate]` by configuring `data` like this:

```ts
{
path: '/path',
component: SomeComponent,
data: {
breadcrumb: 'Display Name'
}
}
```
13 changes: 13 additions & 0 deletions components/breadcrumb/doc/index.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,16 @@ title: Breadcrumb
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
| --- | --- | --- | --- | --- |
| `[nzSeparator]` | 分隔符自定义 | string丨`TemplateRef<void>` | | '/' |
| `[nzAutoGenerate]` | 自动生成 Breadcrumb | boolean | | `false` |

使用 `[nzAutoGenerate]` 时,需要在路由类中定义 `data`:

```ts
{
path: '/path',
component: SomeComponent,
data: {
breadcrumb: 'Display Name'
}
}
```
5 changes: 4 additions & 1 deletion components/breadcrumb/nz-breadcrumb.component.html
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
<ng-content></ng-content>
<ng-content></ng-content>
<nz-breadcrumb-item *ngFor="let breadcrumb of breadcrumbs">
<a [href]="breadcrumb.url" (click)="navigateTo(breadcrumb.url, $event)">{{ breadcrumb.label }}</a>
</nz-breadcrumb-item>
67 changes: 66 additions & 1 deletion components/breadcrumb/nz-breadcrumb.component.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
import {
Component,
Injector,
Input,
OnDestroy,
OnInit,
TemplateRef
} from '@angular/core';
import { ActivatedRoute, NavigationEnd, Params, PRIMARY_OUTLET, Router } from '@angular/router';
import { Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';

const ROUTE_DATA_BREADCRUMB = 'breadcrumb';

export interface BreadcrumbOption {
label: string;
params: Params;
url: string;
}

@Component({
selector : 'nz-breadcrumb',
Expand All @@ -17,10 +31,13 @@ import {
}
` ]
})
export class NzBreadCrumbComponent {
export class NzBreadCrumbComponent implements OnInit, OnDestroy {
private _separator: string | TemplateRef<void> = '/';
private $destroy = new Subject();
isTemplateRef = false;

@Input() nzAutoGenerate = false;

@Input()
set nzSeparator(value: string | TemplateRef<void>) {
this._separator = value;
Expand All @@ -30,4 +47,52 @@ export class NzBreadCrumbComponent {
get nzSeparator(): string | TemplateRef<void> {
return this._separator;
}

breadcrumbs: BreadcrumbOption[] = [];

getBreadcrumbs(route: ActivatedRoute, url: string = '', breadcrumbs: BreadcrumbOption[] = []): BreadcrumbOption[] {
const children: ActivatedRoute[] = route.children;

if (children.length === 0) { return breadcrumbs; }

for (const child of children) {
if (child.outlet !== PRIMARY_OUTLET) { continue; }
if (!child.snapshot.data.hasOwnProperty(ROUTE_DATA_BREADCRUMB)) { return this.getBreadcrumbs(child, url, breadcrumbs); }
const routeURL: string = child.snapshot.url.map(segment => segment.path).join('/');
const nextUrl = url + `/${routeURL}`;
const breadcrumb: BreadcrumbOption = {
label : child.snapshot.data[ ROUTE_DATA_BREADCRUMB ] || 'Breadcrumb',
params: child.snapshot.params,
url : nextUrl
};
breadcrumbs.push(breadcrumb);
return this.getBreadcrumbs(child, nextUrl, breadcrumbs);
}
}

navigateTo(url: string, e?: MouseEvent): void {
if (e) { e.preventDefault(); }
this._injector.get(Router).navigate(url.split('/'));
}

constructor(private _injector: Injector) {}

ngOnInit(): void {
if (this.nzAutoGenerate) {
const activatedRoute = this._injector.get(ActivatedRoute);
const router = this._injector.get(Router);
if (router && activatedRoute) {
router.events.pipe(filter(e => e instanceof NavigationEnd), takeUntil(this.$destroy)).subscribe(() => {
this.breadcrumbs = this.getBreadcrumbs(activatedRoute.root);
});
} else {
console.error('[ng-zorro] You should import RouterModule if you want to use auto generating!');
}
}
}

ngOnDestroy(): void {
this.$destroy.next();
this.$destroy.complete();
}
}
3 changes: 1 addition & 2 deletions components/breadcrumb/nz-breadcrumb.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,4 @@ import { NzBreadCrumbComponent } from './nz-breadcrumb.component';
declarations: [ NzBreadCrumbComponent, NzBreadCrumbItemComponent ],
exports : [ NzBreadCrumbComponent, NzBreadCrumbItemComponent ]
})
export class NzBreadCrumbModule {
}
export class NzBreadCrumbModule {}
89 changes: 88 additions & 1 deletion components/breadcrumb/nz-breadcrumb.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { async, TestBed } from '@angular/core/testing';
import { Location } from '@angular/common';
import { Component } from '@angular/core';
import { async, fakeAsync, flush, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { ActivatedRoute, Router, Routes } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { dispatchMouseEvent } from '../core/testing';

import { NzDemoBreadcrumbBasicComponent } from './demo/basic';
import { NzDemoBreadcrumbSeparatorComponent } from './demo/separator';
Expand All @@ -10,6 +15,7 @@ import { NzBreadCrumbModule } from './nz-breadcrumb.module';
describe('breadcrumb', () => {
let testComponent;
let fixture;

describe('basic', () => {
let items;
let breadcrumb;
Expand All @@ -35,6 +41,7 @@ describe('breadcrumb', () => {
expect(breadcrumb.nativeElement.classList.contains('ant-breadcrumb')).toBe(true);
});
});

describe('separator', () => {
let items;
let breadcrumbs;
Expand Down Expand Up @@ -62,4 +69,84 @@ describe('breadcrumb', () => {
expect(items[ 3 ].nativeElement.children[ 1 ].firstElementChild.classList.contains('anticon-arrow-right')).toBe(true);
});
});

describe('auto generated', () => {
let breadcrumb;
let items;
let router;
let route;
let location;

beforeEach(async(() => {
TestBed.configureTestingModule({
imports : [ NzBreadCrumbModule, RouterTestingModule.withRoutes(routes) ],
declarations: [ NzBreadcrumbAutoGenerateDemoComponent, NzBreadcrumbNullComponent ],
providers : []
}).compileComponents();

fixture = TestBed.createComponent(NzBreadcrumbAutoGenerateDemoComponent);
testComponent = fixture.debugElement.componentInstance;
router = TestBed.get(Router);
route = TestBed.get(ActivatedRoute);
location = TestBed.get(Location);
breadcrumb = fixture.debugElement.query(By.directive(NzBreadCrumbComponent));
router.initialNavigation();
fixture.detectChanges();
}));

it('should support auto generating', fakeAsync(() => {
router.navigate([ 'one', 'two', 'three' ]);
fixture.detectChanges();
flush();
fixture.detectChanges();
expect(location.path()).toBe('/one/two/three');
router.navigate([ 'one', 'two' ]);
fixture.detectChanges();
flush();
fixture.detectChanges();
expect(location.path()).toBe('/one/two');
router.navigate([ 'one' ]);
fixture.detectChanges();
flush();
fixture.detectChanges();
expect(location.path()).toBe('/one');
}));
});
});

@Component({
selector: 'nz-breadcrumb-auto-generate-demo',
template: `
<nz-breadcrumb [nzAutoGenerate]="true"></nz-breadcrumb>
<router-outlet></router-outlet>
`
})
export class NzBreadcrumbAutoGenerateDemoComponent {}

@Component({
selector: 'nz-breadcrumb-null',
template: ''
})
export class NzBreadcrumbNullComponent {}

const routes: Routes = [
{
path : 'one',
component: NzBreadcrumbAutoGenerateDemoComponent,
data : { breadcrumb: 'Layer 1' },
children : [
{
path : 'two',
component: NzBreadcrumbNullComponent,
data : { breadcrumb: 'Layer 2' },
children : [
{
path : 'three',
component: NzBreadcrumbNullComponent,
data : { breadcrumb: 'Layer 3' }
}
]
}
]
}
];
1 change: 1 addition & 0 deletions components/list/demo/basic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Component } from '@angular/core';
<nz-list [nzDataSource]="data" [nzRenderItem]="item" [nzItemLayout]="'horizontal'">
<ng-template #item let-item>
<nz-list-item>
<nz-list>
<nz-list-item-meta
[nzTitle]="nzTitle"
nzAvatar="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"
Expand Down
2 changes: 1 addition & 1 deletion site_scripts/utils/generate-routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ module.exports = function generateRoutes(showCaseTargetPath, componentsMap, docs
reverseMap[componentsMap[key].type].push(en);
}
const moduleName = capitalizeFirstLetter(camelCase(key));
routes += ` {'path': 'components/${key}', 'loadChildren': './${key}/index.module#NzDemo${moduleName}Module'},\n`;
routes += ` {'path': 'components/${key}', 'loadChildren': './${key}/index.module#NzDemo${moduleName}Module', 'data': { 'breadcrumb': '${key}' } },\n`;
}
for (const key in reverseMap) {
components.push({
Expand Down

0 comments on commit 5738207

Please sign in to comment.