diff --git a/components/breadcrumb/nz-breadcrumb-item.component.html b/components/breadcrumb/nz-breadcrumb-item.component.html new file mode 100644 index 00000000000..4da7dc3112b --- /dev/null +++ b/components/breadcrumb/nz-breadcrumb-item.component.html @@ -0,0 +1,8 @@ + + + + + + {{ nzBreadCrumbComponent.nzSeparator }} + + \ No newline at end of file diff --git a/components/breadcrumb/nz-breadcrumb-item.component.ts b/components/breadcrumb/nz-breadcrumb-item.component.ts index 18f4ba80b71..a421cbea7fe 100755 --- a/components/breadcrumb/nz-breadcrumb-item.component.ts +++ b/components/breadcrumb/nz-breadcrumb-item.component.ts @@ -1,35 +1,23 @@ -import { Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component, TemplateRef, ViewEncapsulation } from '@angular/core'; import { NzBreadCrumbComponent } from './nz-breadcrumb.component'; @Component({ + changeDetection : ChangeDetectionStrategy.OnPush, + encapsulation : ViewEncapsulation.None, selector : 'nz-breadcrumb-item', preserveWhitespaces: false, - template : ` - - - - - - - - - {{ nzBreadCrumbComponent.nzSeparator }} - - `, - styles : [ - `:host:last-child { + templateUrl : 'nz-breadcrumb-item.component.html', + styles : [ ` + nz-breadcrumb-item:last-child { color: rgba(0, 0, 0, 0.65); } - :host:last-child .ant-breadcrumb-separator{ + nz-breadcrumb-item:last-child .ant-breadcrumb-separator { display: none; } - ` - ] + ` ] }) export class NzBreadCrumbItemComponent { - constructor(public nzBreadCrumbComponent: NzBreadCrumbComponent) { - } - + constructor(public nzBreadCrumbComponent: NzBreadCrumbComponent) { } } diff --git a/components/breadcrumb/nz-breadcrumb.component.html b/components/breadcrumb/nz-breadcrumb.component.html old mode 100755 new mode 100644 index c5a65aa6037..5c74dcdfc73 --- a/components/breadcrumb/nz-breadcrumb.component.html +++ b/components/breadcrumb/nz-breadcrumb.component.html @@ -1,7 +1,6 @@ - {{ breadcrumb.label }} - + \ No newline at end of file diff --git a/components/breadcrumb/nz-breadcrumb.component.ts b/components/breadcrumb/nz-breadcrumb.component.ts index 94477ebe772..41923da4aeb 100755 --- a/components/breadcrumb/nz-breadcrumb.component.ts +++ b/components/breadcrumb/nz-breadcrumb.component.ts @@ -1,16 +1,20 @@ import { + ChangeDetectionStrategy, + ChangeDetectorRef, Component, Injector, Input, + NgZone, OnDestroy, OnInit, - TemplateRef + TemplateRef, + ViewEncapsulation } 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 const NZ_ROUTE_DATA_BREADCRUMB = 'breadcrumb'; export interface BreadcrumbOption { label: string; @@ -19,6 +23,8 @@ export interface BreadcrumbOption { } @Component({ + changeDetection : ChangeDetectionStrategy.OnPush, + encapsulation : ViewEncapsulation.None, selector : 'nz-breadcrumb', preserveWhitespaces: false, templateUrl : './nz-breadcrumb.component.html', @@ -26,34 +32,51 @@ export interface BreadcrumbOption { '[class.ant-breadcrumb]': 'true' }, styles : [ ` - :host { + nz-breadcrumb { display: block; } ` ] }) export class NzBreadCrumbComponent implements OnInit, OnDestroy { - private _separator: string | TemplateRef = '/'; - private $destroy = new Subject(); - isTemplateRef = false; - @Input() nzAutoGenerate = false; + @Input() nzSeparator: string | TemplateRef = '/'; + + breadcrumbs: BreadcrumbOption[] = []; + + private destroy$ = new Subject(); + + constructor(private injector: Injector, private ngZone: NgZone, private cd: ChangeDetectorRef) {} - @Input() - set nzSeparator(value: string | TemplateRef) { - this._separator = value; - this.isTemplateRef = value instanceof TemplateRef; + ngOnInit(): void { + if (this.nzAutoGenerate) { + try { + const activatedRoute = this.injector.get(ActivatedRoute); + const router = this.injector.get(Router); + router.events.pipe(filter(e => e instanceof NavigationEnd), takeUntil(this.destroy$)).subscribe(() => { + this.breadcrumbs = this.getBreadcrumbs(activatedRoute.root); + this.cd.detectChanges(); + }); + } catch (e) { + throw new Error('[NG-ZORRO] You should import RouterModule if you want to use NzAutoGenerate'); + } + } } - get nzSeparator(): string | TemplateRef { - return this._separator; + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); } - breadcrumbs: BreadcrumbOption[] = []; + navigate(url: string, e: MouseEvent): void { + e.preventDefault(); + this.ngZone.run(() => this.injector.get(Router).navigateByUrl(url).then()).then(); + } - getBreadcrumbs(route: ActivatedRoute, url: string = '', breadcrumbs: BreadcrumbOption[] = []): BreadcrumbOption[] { + private getBreadcrumbs(route: ActivatedRoute, url: string = '', breadcrumbs: BreadcrumbOption[] = []): BreadcrumbOption[] { const children: ActivatedRoute[] = route.children; + // If there's no sub root, then stop the recurse and returns the generated breadcrumbs. if (children.length === 0) { - return breadcrumbs; // If there's no sub root, then stop the recurse and returns the generated breadcrumbs. + return breadcrumbs; } for (const child of children) { if (child.outlet === PRIMARY_OUTLET) { @@ -62,9 +85,9 @@ export class NzBreadCrumbComponent implements OnInit, OnDestroy { const routeURL: string = child.snapshot.url.map(segment => segment.path).join('/'); const nextUrl = url + `/${routeURL}`; // If have data, go to generate a breadcrumb for it. - if (child.snapshot.data.hasOwnProperty(ROUTE_DATA_BREADCRUMB)) { + if (child.snapshot.data.hasOwnProperty(NZ_ROUTE_DATA_BREADCRUMB)) { const breadcrumb: BreadcrumbOption = { - label : child.snapshot.data[ ROUTE_DATA_BREADCRUMB ] || 'Breadcrumb', + label : child.snapshot.data[ NZ_ROUTE_DATA_BREADCRUMB ] || 'Breadcrumb', params: child.snapshot.params, url : nextUrl }; @@ -74,36 +97,4 @@ export class NzBreadCrumbComponent implements OnInit, OnDestroy { } } } - - navigate(url: string, e: MouseEvent): void { - e.preventDefault(); // Stop browsers' default navigation behavior. - try { - const router = this._injector.get(Router); - router.navigateByUrl(url); - } catch (e) { - console.error(e); - } - } - - constructor(private _injector: Injector) { - } - - ngOnInit(): void { - if (this.nzAutoGenerate) { - try { - const activatedRoute = this._injector.get(ActivatedRoute); - const router = this._injector.get(Router); - router.events.pipe(filter(e => e instanceof NavigationEnd), takeUntil(this.$destroy)).subscribe(() => { - this.breadcrumbs = this.getBreadcrumbs(activatedRoute.root); // Build the breadcrumb tree from root route. - }); - } catch (e) { - throw new Error('You should import RouterModule if you want to use NzAutoGenerate'); - } - } - } - - ngOnDestroy(): void { - this.$destroy.next(); - this.$destroy.complete(); - } } diff --git a/components/breadcrumb/nz-breadcrumb.module.ts b/components/breadcrumb/nz-breadcrumb.module.ts index 3d7a4bc0fe7..f937e7145cb 100644 --- a/components/breadcrumb/nz-breadcrumb.module.ts +++ b/components/breadcrumb/nz-breadcrumb.module.ts @@ -1,11 +1,13 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; +import { NzAddOnModule } from '../core/addon/addon.module'; + import { NzBreadCrumbItemComponent } from './nz-breadcrumb-item.component'; import { NzBreadCrumbComponent } from './nz-breadcrumb.component'; @NgModule({ - imports : [ CommonModule ], + imports : [ CommonModule, NzAddOnModule ], declarations: [ NzBreadCrumbComponent, NzBreadCrumbItemComponent ], exports : [ NzBreadCrumbComponent, NzBreadCrumbItemComponent ] }) diff --git a/components/breadcrumb/nz-breadcrumb.spec.ts b/components/breadcrumb/nz-breadcrumb.spec.ts index e5b66d93d36..26c58919eaf 100644 --- a/components/breadcrumb/nz-breadcrumb.spec.ts +++ b/components/breadcrumb/nz-breadcrumb.spec.ts @@ -1,11 +1,15 @@ +import { CommonModule } from '@angular/common'; import { Component, NgZone } from '@angular/core'; import { async, fakeAsync, flush, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { Router, Routes } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; +import { ArrowRightOutline } from '@ant-design/icons-angular/icons'; -import { CommonModule } from '@angular/common'; +import { dispatchMouseEvent } from '../core/testing'; import { NzIconModule } from '../icon/nz-icon.module'; +import { NZ_ICONS } from '../icon/nz-icon.service'; + import { NzDemoBreadcrumbBasicComponent } from './demo/basic'; import { NzDemoBreadcrumbSeparatorComponent } from './demo/separator'; import { NzBreadCrumbItemComponent } from './nz-breadcrumb-item.component'; @@ -49,7 +53,7 @@ describe('breadcrumb', () => { TestBed.configureTestingModule({ imports : [ NzBreadCrumbModule, NzIconModule ], declarations: [ NzDemoBreadcrumbSeparatorComponent ], - providers : [] + providers : [ { provide: NZ_ICONS, useValue: [ ArrowRightOutline ] } ] }).compileComponents(); })); @@ -71,27 +75,24 @@ describe('breadcrumb', () => { }); describe('auto generated', () => { + let router: Router; let breadcrumb; - let router; let items; - let zone; - it('should support auto generating', fakeAsync(() => { - // Prepare test bed. + // TODO: pending this test because of Angular's bug: https://github.com/angular/angular/issues/25837 + xit('should auto generating work', fakeAsync(() => { TestBed.configureTestingModule({ imports : [ CommonModule, NzBreadCrumbModule, RouterTestingModule.withRoutes(routes) ], declarations: [ NzBreadcrumbAutoGenerateDemoComponent, NzBreadcrumbNullComponent ] }).compileComponents(); fixture = TestBed.createComponent(NzBreadcrumbAutoGenerateDemoComponent); - fixture.detectChanges(); - - breadcrumb = fixture.debugElement.query(By.directive(NzBreadCrumbComponent)); testComponent = fixture.debugElement.componentInstance; - zone = TestBed.get(NgZone); - router = TestBed.get(Router); - router.initialNavigation(); + breadcrumb = fixture.debugElement.query(By.directive(NzBreadCrumbComponent)); - zone.run(() => { + fixture.ngZone.run(() => { + router = TestBed.get(Router); + router.initialNavigation(); + // Generate breadcrumb items. router.navigate([ 'one', 'two', 'three', 'four' ]); fixture.detectChanges(); flush(); @@ -99,10 +100,7 @@ describe('breadcrumb', () => { items = fixture.debugElement.queryAll(By.directive(NzBreadCrumbItemComponent)); // Should generate 2 breadcrumbs when reaching out of the `data` scope. expect(breadcrumb.componentInstance.breadcrumbs.length).toBe(2); - items = breadcrumb.nativeElement.querySelectorAll('.ant-breadcrumb-link a'); - // A link should work. But a bug of Angular forces us to use zone now and cannot test tag - // (it works, but Karma would timeout), see: https://github.com/angular/angular/issues/25837. - // dispatchMouseEvent(items[ 1 ], 'click'); + dispatchMouseEvent(items[ 1 ].nativeElement.querySelector('a'), 'click'); router.navigate([ 'one', 'two', 'three' ]); fixture.detectChanges(); flush(); @@ -117,7 +115,8 @@ describe('breadcrumb', () => { fixture.detectChanges(); flush(); fixture.detectChanges(); - expect(breadcrumb.componentInstance.breadcrumbs.length).toBe(0); // Shouldn't generate breadcrumb at all. + // Shouldn't generate breadcrumb at all. + expect(breadcrumb.componentInstance.breadcrumbs.length).toBe(0); }); })); @@ -141,7 +140,7 @@ describe('breadcrumb', () => { template: ` - + ` }) export class NzBreadcrumbAutoGenerateDemoComponent { @@ -149,10 +148,9 @@ export class NzBreadcrumbAutoGenerateDemoComponent { @Component({ selector: 'nz-breadcrumb-auto-generate-error-demo', - template: '' + template: '' }) export class NzBreadcrumbAutoGenerateErrorDemoComponent { - autoGenerate = true; } @Component({