fix(router): routerLinkActive should only set classes after the router has successfully navigated
This commit is contained in:
parent
eb6ff65af7
commit
db54a84d14
|
@ -7,3 +7,4 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export {SpyLocation} from './testing/location_mock';
|
export {SpyLocation} from './testing/location_mock';
|
||||||
|
export {MockLocationStrategy} from './testing/mock_location_strategy';
|
|
@ -16,6 +16,8 @@ import {EventEmitter, ObservableWrapper} from '../src/facade/async';
|
||||||
/**
|
/**
|
||||||
* A mock implementation of {@link LocationStrategy} that allows tests to fire simulated
|
* A mock implementation of {@link LocationStrategy} that allows tests to fire simulated
|
||||||
* location events.
|
* location events.
|
||||||
|
*
|
||||||
|
* @stable
|
||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MockLocationStrategy extends LocationStrategy {
|
export class MockLocationStrategy extends LocationStrategy {
|
||||||
|
|
|
@ -94,7 +94,7 @@ export class RouterLinkActive implements OnChanges, OnDestroy, AfterContentInit
|
||||||
ngOnDestroy(): any { this.subscription.unsubscribe(); }
|
ngOnDestroy(): any { this.subscription.unsubscribe(); }
|
||||||
|
|
||||||
private update(): void {
|
private update(): void {
|
||||||
if (!this.links || !this.linksWithHrefs) return;
|
if (!this.links || !this.linksWithHrefs || !this.router.navigated) return;
|
||||||
|
|
||||||
const currentUrlTree = this.router.parseUrl(this.router.url);
|
const currentUrlTree = this.router.parseUrl(this.router.url);
|
||||||
const isActiveLinks = this.reduceList(currentUrlTree, this.links);
|
const isActiveLinks = this.reduceList(currentUrlTree, this.links);
|
||||||
|
|
|
@ -133,6 +133,13 @@ export class Router {
|
||||||
private config: Routes;
|
private config: Routes;
|
||||||
private configLoader: RouterConfigLoader;
|
private configLoader: RouterConfigLoader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if at least one navigation happened.
|
||||||
|
*
|
||||||
|
* @experimental
|
||||||
|
*/
|
||||||
|
navigated: boolean = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the router service.
|
* Creates the router service.
|
||||||
*/
|
*/
|
||||||
|
@ -385,6 +392,7 @@ export class Router {
|
||||||
})
|
})
|
||||||
.then(
|
.then(
|
||||||
() => {
|
() => {
|
||||||
|
this.navigated = true;
|
||||||
this.routerEvents.next(
|
this.routerEvents.next(
|
||||||
new NavigationEnd(id, this.serializeUrl(url), this.serializeUrl(appliedUrl)));
|
new NavigationEnd(id, this.serializeUrl(url), this.serializeUrl(appliedUrl)));
|
||||||
resolvePromise(navigationIsSuccessful);
|
resolvePromise(navigationIsSuccessful);
|
||||||
|
|
|
@ -1194,6 +1194,29 @@ describe('Integration', () => {
|
||||||
expect(nativeButton.className).toEqual('');
|
expect(nativeButton.className).toEqual('');
|
||||||
})));
|
})));
|
||||||
|
|
||||||
|
it('should not set the class until the first navigation succeeds',
|
||||||
|
fakeAsync(inject(
|
||||||
|
[Router, TestComponentBuilder, Location],
|
||||||
|
(router: Router, tcb: TestComponentBuilder, location: Location) => {
|
||||||
|
@Component({
|
||||||
|
template:
|
||||||
|
'<router-outlet></router-outlet><a routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}" >'
|
||||||
|
})
|
||||||
|
class RootCmpWithLink {
|
||||||
|
}
|
||||||
|
|
||||||
|
const f = tcb.createFakeAsync(RootCmpWithLink);
|
||||||
|
advance(f);
|
||||||
|
|
||||||
|
const link = f.debugElement.nativeElement.querySelector('a');
|
||||||
|
expect(link.className).toEqual('');
|
||||||
|
|
||||||
|
router.initialNavigation();
|
||||||
|
advance(f);
|
||||||
|
|
||||||
|
expect(link.className).toEqual('active');
|
||||||
|
})));
|
||||||
|
|
||||||
|
|
||||||
it('should set the class on a parent element when the link is active',
|
it('should set the class on a parent element when the link is active',
|
||||||
fakeAsync(inject(
|
fakeAsync(inject(
|
||||||
|
|
|
@ -7,8 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Location, LocationStrategy} from '@angular/common';
|
import {Location, LocationStrategy} from '@angular/common';
|
||||||
import {SpyLocation} from '@angular/common/testing';
|
import {MockLocationStrategy, SpyLocation} from '@angular/common/testing';
|
||||||
import {MockLocationStrategy} from '@angular/common/testing/mock_location_strategy';
|
|
||||||
import {AppModule, AppModuleFactory, AppModuleFactoryLoader, Compiler, ComponentResolver, Injectable, Injector} from '@angular/core';
|
import {AppModule, AppModuleFactory, AppModuleFactoryLoader, Compiler, ComponentResolver, Injectable, Injector} from '@angular/core';
|
||||||
|
|
||||||
import {Router, RouterOutletMap, Routes, UrlSerializer} from '../index';
|
import {Router, RouterOutletMap, Routes, UrlSerializer} from '../index';
|
||||||
|
@ -16,6 +15,7 @@ import {ROUTES} from '../src/router_config_loader';
|
||||||
import {ROUTER_DIRECTIVES, ROUTER_PROVIDERS} from '../src/router_module';
|
import {ROUTER_DIRECTIVES, ROUTER_PROVIDERS} from '../src/router_module';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A spy for {@link AppModuleFactoryLoader} that allows tests to simulate the loading of app module
|
* A spy for {@link AppModuleFactoryLoader} that allows tests to simulate the loading of app module
|
||||||
* factories.
|
* factories.
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
"paths": {
|
"paths": {
|
||||||
"@angular/core": ["../../../dist/packages-dist/core"],
|
"@angular/core": ["../../../dist/packages-dist/core"],
|
||||||
"@angular/common": ["../../../dist/packages-dist/common"],
|
"@angular/common": ["../../../dist/packages-dist/common"],
|
||||||
|
"@angular/common/testing": ["../../../dist/packages-dist/common/testing"],
|
||||||
"@angular/compiler": ["../../../dist/packages-dist/compiler"],
|
"@angular/compiler": ["../../../dist/packages-dist/compiler"],
|
||||||
"@angular/platform-browser": ["../../../dist/packages-dist/platform-browser"],
|
"@angular/platform-browser": ["../../../dist/packages-dist/platform-browser"],
|
||||||
"@angular/platform-browser-dynamic": ["../../../dist/packages-dist/platform-browser-dynamic"]
|
"@angular/platform-browser-dynamic": ["../../../dist/packages-dist/platform-browser-dynamic"]
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
"paths": {
|
"paths": {
|
||||||
"@angular/core": ["../../../dist/packages-dist/core"],
|
"@angular/core": ["../../../dist/packages-dist/core"],
|
||||||
"@angular/common": ["../../../dist/packages-dist/common"],
|
"@angular/common": ["../../../dist/packages-dist/common"],
|
||||||
|
"@angular/common/testing": ["../../../dist/packages-dist/common/testing"],
|
||||||
"@angular/compiler": ["../../../dist/packages-dist/compiler"],
|
"@angular/compiler": ["../../../dist/packages-dist/compiler"],
|
||||||
"@angular/platform-browser": ["../../../dist/packages-dist/platform-browser"],
|
"@angular/platform-browser": ["../../../dist/packages-dist/platform-browser"],
|
||||||
"@angular/platform-browser-dynamic": ["../../../dist/packages-dist/platform-browser-dynamic"]
|
"@angular/platform-browser-dynamic": ["../../../dist/packages-dist/platform-browser-dynamic"]
|
||||||
|
|
|
@ -1,3 +1,21 @@
|
||||||
|
/** @stable */
|
||||||
|
export declare class MockLocationStrategy extends LocationStrategy {
|
||||||
|
internalBaseHref: string;
|
||||||
|
internalPath: string;
|
||||||
|
internalTitle: string;
|
||||||
|
urlChanges: string[];
|
||||||
|
constructor();
|
||||||
|
back(): void;
|
||||||
|
forward(): void;
|
||||||
|
getBaseHref(): string;
|
||||||
|
onPopState(fn: (value: any) => void): void;
|
||||||
|
path(includeHash?: boolean): string;
|
||||||
|
prepareExternalUrl(internal: string): string;
|
||||||
|
pushState(ctx: any, title: string, path: string, query: string): void;
|
||||||
|
replaceState(ctx: any, title: string, path: string, query: string): void;
|
||||||
|
simulatePopState(url: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
/** @experimental */
|
/** @experimental */
|
||||||
export declare class SpyLocation implements Location {
|
export declare class SpyLocation implements Location {
|
||||||
urlChanges: string[];
|
urlChanges: string[];
|
||||||
|
|
|
@ -82,6 +82,8 @@ export declare class NavigationError {
|
||||||
/** @experimental */
|
/** @experimental */
|
||||||
export interface NavigationExtras {
|
export interface NavigationExtras {
|
||||||
fragment?: string;
|
fragment?: string;
|
||||||
|
preserveFragment?: boolean;
|
||||||
|
preserveQueryParams?: boolean;
|
||||||
queryParams?: Params;
|
queryParams?: Params;
|
||||||
relativeTo?: ActivatedRoute;
|
relativeTo?: ActivatedRoute;
|
||||||
}
|
}
|
||||||
|
@ -130,10 +132,11 @@ export interface Route {
|
||||||
/** @stable */
|
/** @stable */
|
||||||
export declare class Router {
|
export declare class Router {
|
||||||
events: Observable<Event>;
|
events: Observable<Event>;
|
||||||
|
/** @experimental */ navigated: boolean;
|
||||||
routerState: RouterState;
|
routerState: RouterState;
|
||||||
url: string;
|
url: string;
|
||||||
constructor(rootComponentType: Type, resolver: ComponentResolver, urlSerializer: UrlSerializer, outletMap: RouterOutletMap, location: Location, injector: Injector, loader: AppModuleFactoryLoader, config: Routes);
|
constructor(rootComponentType: Type, resolver: ComponentResolver, urlSerializer: UrlSerializer, outletMap: RouterOutletMap, location: Location, injector: Injector, loader: AppModuleFactoryLoader, config: Routes);
|
||||||
createUrlTree(commands: any[], {relativeTo, queryParams, fragment}?: NavigationExtras): UrlTree;
|
createUrlTree(commands: any[], {relativeTo, queryParams, fragment, preserveQueryParams, preserveFragment}?: NavigationExtras): UrlTree;
|
||||||
dispose(): void;
|
dispose(): void;
|
||||||
initialNavigation(): void;
|
initialNavigation(): void;
|
||||||
navigate(commands: any[], extras?: NavigationExtras): Promise<boolean>;
|
navigate(commands: any[], extras?: NavigationExtras): Promise<boolean>;
|
||||||
|
@ -152,6 +155,8 @@ export declare type RouterConfig = Route[];
|
||||||
/** @stable */
|
/** @stable */
|
||||||
export declare class RouterLink {
|
export declare class RouterLink {
|
||||||
fragment: string;
|
fragment: string;
|
||||||
|
preserveFragment: boolean;
|
||||||
|
preserveQueryParams: boolean;
|
||||||
queryParams: {
|
queryParams: {
|
||||||
[k: string]: any;
|
[k: string]: any;
|
||||||
};
|
};
|
||||||
|
@ -176,10 +181,16 @@ export declare class RouterLinkActive implements OnChanges, OnDestroy, AfterCont
|
||||||
export declare class RouterLinkWithHref implements OnChanges, OnDestroy {
|
export declare class RouterLinkWithHref implements OnChanges, OnDestroy {
|
||||||
fragment: string;
|
fragment: string;
|
||||||
href: string;
|
href: string;
|
||||||
|
preserveFragment: boolean;
|
||||||
|
preserveQueryParams: boolean;
|
||||||
queryParams: {
|
queryParams: {
|
||||||
[k: string]: any;
|
[k: string]: any;
|
||||||
};
|
};
|
||||||
routerLink: any[] | string;
|
routerLink: any[] | string;
|
||||||
|
routerLinkOptions: {
|
||||||
|
preserveQueryParams: boolean;
|
||||||
|
preserveFragment: boolean;
|
||||||
|
};
|
||||||
target: string;
|
target: string;
|
||||||
urlTree: UrlTree;
|
urlTree: UrlTree;
|
||||||
ngOnChanges(changes: {}): any;
|
ngOnChanges(changes: {}): any;
|
||||||
|
|
Loading…
Reference in New Issue