Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit d5d6ec8

Browse files
author
Alexander Vakrilov
authored
Merge pull request #383 from NativeScript/nsRouterLinkActive
nsRouterLinkDirective
2 parents 48f15e0 + 9f4cd1e commit d5d6ec8

File tree

8 files changed

+154
-27
lines changed

8 files changed

+154
-27
lines changed
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import {AfterContentInit, ContentChildren, Directive, ElementRef, Input, OnChanges, OnDestroy, QueryList, Renderer} from '@angular/core';
2+
import {Subscription} from 'rxjs/Subscription';
3+
4+
import {NavigationEnd, Router} from '@angular/router';
5+
import {UrlTree, containsTree} from '@angular/router/src/url_tree';
6+
7+
import {NSRouterLink} from './ns-router-link';
8+
9+
10+
/**
11+
* The NSRouterLinkActive directive lets you add a CSS class to an element when the link's route
12+
* becomes active.
13+
*
14+
* Consider the following example:
15+
*
16+
* ```
17+
* <a [nsRouterLink]="/user/bob" [nsRouterLinkActive]="active-link">Bob</a>
18+
* ```
19+
*
20+
* When the url is either '/user' or '/user/bob', the active-link class will
21+
* be added to the component. If the url changes, the class will be removed.
22+
*
23+
* You can set more than one class, as follows:
24+
*
25+
* ```
26+
* <a [nsRouterLink]="/user/bob" [nsRouterLinkActive]="class1 class2">Bob</a>
27+
* <a [nsRouterLink]="/user/bob" [nsRouterLinkActive]="['class1', 'class2']">Bob</a>
28+
* ```
29+
*
30+
* You can configure NSRouterLinkActive by passing `exact: true`. This will add the classes
31+
* only when the url matches the link exactly.
32+
*
33+
* ```
34+
* <a [nsRouterLink]="/user/bob" [nsRouterLinkActive]="active-link" [nsRouterLinkActiveOptions]="{exact:
35+
* true}">Bob</a>
36+
* ```
37+
*
38+
* Finally, you can apply the NSRouterLinkActive directive to an ancestor of a RouterLink.
39+
*
40+
* ```
41+
* <div [nsRouterLinkActive]="active-link" [nsRouterLinkActiveOptions]="{exact: true}">
42+
* <a [nsRouterLink]="/user/jim">Jim</a>
43+
* <a [nsRouterLink]="/user/bob">Bob</a>
44+
* </div>
45+
* ```
46+
*
47+
* This will set the active-link class on the div tag if the url is either '/user/jim' or
48+
* '/user/bob'.
49+
*
50+
* @stable
51+
*/
52+
@Directive({ selector: '[nsRouterLinkActive]' })
53+
export class NSRouterLinkActive implements OnChanges, OnDestroy, AfterContentInit {
54+
@ContentChildren(NSRouterLink) links: QueryList<NSRouterLink>;
55+
56+
private classes: string[] = [];
57+
private subscription: Subscription;
58+
59+
@Input() private nsRouterLinkActiveOptions: { exact: boolean } = { exact: false };
60+
61+
constructor(private router: Router, private element: ElementRef, private renderer: Renderer) {
62+
this.subscription = router.events.subscribe(s => {
63+
if (s instanceof NavigationEnd) {
64+
this.update();
65+
}
66+
});
67+
}
68+
69+
ngAfterContentInit(): void {
70+
this.links.changes.subscribe(s => this.update());
71+
this.update();
72+
}
73+
74+
@Input("nsRouterLinkActive")
75+
set nsRouterLinkActive(data: string[] | string) {
76+
if (Array.isArray(data)) {
77+
this.classes = <any>data;
78+
} else {
79+
this.classes = data.split(' ');
80+
}
81+
}
82+
83+
ngOnChanges(changes: {}): any { this.update(); }
84+
ngOnDestroy(): any { this.subscription.unsubscribe(); }
85+
86+
private update(): void {
87+
if (!this.links) return;
88+
89+
const currentUrlTree = this.router.parseUrl(this.router.url);
90+
const isActiveLinks = this.reduceList(currentUrlTree, this.links);
91+
this.classes.forEach(
92+
c => this.renderer.setElementClass(
93+
this.element.nativeElement, c, isActiveLinks));
94+
}
95+
96+
private reduceList(currentUrlTree: UrlTree, q: QueryList<any>): boolean {
97+
return q.reduce(
98+
(res: boolean, link: NSRouterLink) =>
99+
res || containsTree(currentUrlTree, link.urlTree, this.nsRouterLinkActiveOptions.exact),
100+
false);
101+
}
102+
}

‎nativescript-angular/router/ns-router-link.ts‎

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import {Directive, HostListener, Input, Optional} from '@angular/core';
1+
import {Directive, HostListener, Input, Optional,OnChanges} from '@angular/core';
22
import {NavigationExtras} from "@angular/router/src/router";
3-
import {ActivatedRoute} from '@angular/router';
3+
import {ActivatedRoute,Router,UrlTree} from '@angular/router';
44
import {routerLog} from "../trace";
55
import {PageRoute} from "./page-router-outlet";
66
import {RouterExtensions} from "./router-extensions";
@@ -23,7 +23,7 @@ import {isString} from "utils/types";
2323
* <a [nsRouterLink]="['/user']">link to user component</a>
2424
* ```
2525
*
26-
* RouterLink expects the value to be an array of path segments, followed by the params
26+
* NSRouterLink expects the value to be an array of path segments, followed by the params
2727
* for that level of routing. For instance `['/team', {teamId: 1}, 'user', {userId: 2}]`
2828
* means that we want to generate a link to `/team;teamId=1/user;userId=2`.
2929
*
@@ -34,7 +34,7 @@ import {isString} from "utils/types";
3434
* And if the segment begins with `../`, the router will go up one level.
3535
*/
3636
@Directive({ selector: '[nsRouterLink]' })
37-
export class NSRouterLink {
37+
export class NSRouterLink implementsOnChanges{
3838
private commands: any[] = [];
3939
@Input() target: string;
4040
@Input() queryParams: { [k: string]: any };
@@ -43,9 +43,16 @@ export class NSRouterLink {
4343
@Input() clearHistory: boolean;
4444
@Input() pageTransition: boolean | string | NavigationTransition = true;
4545

46+
urlTree: UrlTree;
47+
4648
private usePageRoute: boolean;
4749

50+
private get currentRoute(): ActivatedRoute {
51+
return this.usePageRoute ? this.pageRoute.activatedRoute.getValue() : this.route;
52+
}
53+
4854
constructor(
55+
private router: Router,
4956
private navigator: RouterExtensions,
5057
private route: ActivatedRoute,
5158
@Optional() private pageRoute: PageRoute) {
@@ -62,14 +69,15 @@ export class NSRouterLink {
6269
}
6370
}
6471

72+
6573
@HostListener("tap")
6674
onTap() {
6775
routerLog("nsRouterLink.tapped: " + this.commands + " usePageRoute: " + this.usePageRoute + " clearHistory: " + this.clearHistory + " transition: " + JSON.stringify(this.pageTransition));
6876

69-
const currentRoute = this.usePageRoute ? this.pageRoute.activatedRoute.getValue() : this.route;
7077
const transition = this.getTransition();
78+
7179
let extras: NavigationExtras & NavigationOptions = {
72-
relativeTo: currentRoute,
80+
relativeTo: this.currentRoute,
7381
queryParams: this.queryParams,
7482
fragment: this.fragment,
7583
clearHistory: this.clearHistory,
@@ -93,7 +101,17 @@ export class NSRouterLink {
93101
return {
94102
animated: true,
95103
transition: this.pageTransition
96-
}
104+
};
97105
}
98106
}
107+
108+
ngOnChanges(changes: {}): any {
109+
this.updateUrlTree();
110+
}
111+
112+
private updateUrlTree(): void {
113+
this.urlTree = this.router.createUrlTree(
114+
this.commands,
115+
{ relativeTo: this.currentRoute, queryParams: this.queryParams, fragment: this.fragment });
116+
}
99117
}

‎nativescript-angular/router/ns-router.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { RouterConfig } from '@angular/router';
55
import { provideRouter, ExtraOptions } from '@angular/router/src/common_router_providers';
66

77
import {NSRouterLink} from './ns-router-link';
8+
import {NSRouterLinkActive} from './ns-router-link-active';
89
import {PageRouterOutlet} from './page-router-outlet';
910
import {NSLocationStrategy} from './ns-location-strategy';
1011
import {NativescriptPlatformLocation} from './ns-platform-location';
@@ -25,6 +26,7 @@ export const NS_ROUTER_PROVIDERS: any[] = [
2526

2627
export const NS_ROUTER_DIRECTIVES: Type[] = [
2728
NSRouterLink,
29+
NSRouterLinkActive,
2830
PageRouterOutlet
2931
];
3032

‎ng-sample/app/app.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ import { AnimationStatesTest } from "./examples/animation/animation-states-test"
6565
// nativeScriptBootstrap(RouterOutletAppComponent, [RouterOutletRouterProviders]);
6666
// nativeScriptBootstrap(PageRouterOutletAppComponent, [PageRouterOutletRouterProviders]);
6767
// nativeScriptBootstrap(PageRouterOutletNestedAppComponent, [PageRouterOutletNestedRouterProviders]);
68-
// nativeScriptBootstrap(ClearHistoryAppComponent, [ClearHistoryRouterProviders]);
69-
nativeScriptBootstrap(LoginAppComponent, [LoginExampleProviders]);
68+
nativeScriptBootstrap(ClearHistoryAppComponent, [ClearHistoryRouterProviders]);
69+
// nativeScriptBootstrap(LoginAppComponent, [LoginExampleProviders]);
7070

7171
// router-deprecated
7272
// nativeScriptBootstrap(NavigationTest, [NS_ROUTER_PROVIDERS_DEPRECATED]);

‎ng-sample/app/examples/router/clear-history-test.ts‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ class LocationLogService {
1616
this.routerEvents$.next([...this.routerEvents$.getValue(), e.toString()]);
1717

1818
let states = this.strategy._getSatates()
19-
.map((v, i) => {
20-
return (i + "." + (v.isPageNavigation ? "[PAGE]" : "") + " \"" + v.url + "\"");
19+
.map((v, i) => {
20+
return (i + "." + (v.isPageNavigation ? "[PAGE]" : "") + " \"" + v.url + "\"");
2121
})
2222
.reverse();
2323

@@ -82,7 +82,7 @@ class SecondComponent implements OnInit, OnDestroy {
8282
})
8383
class ThirdComponent implements OnInit, OnDestroy {
8484
name = "Third";
85-
constructor(private nav: RouterExtensions) { }
85+
constructor(private nav: RouterExtensions) { }
8686
ngOnInit() { console.log("ThirdComponent - ngOnInit()"); }
8787
ngOnDestroy() { console.log("ThirdComponent - ngOnDestroy()"); }
8888
}
@@ -93,13 +93,13 @@ class ThirdComponent implements OnInit, OnDestroy {
9393
providers: [LocationLogService],
9494
template: `<page-router-outlet></page-router-outlet>`
9595
})
96-
export class ClearHistoryAppComponent {}
96+
export class ClearHistoryAppComponent {}
9797

9898
const routes: RouterConfig = [
9999
{ path: "", redirectTo: "/first", terminal: true },
100-
{ path: "first", component: FirstComponent},
101-
{ path: "second", component: SecondComponent},
102-
{ path: "third", component: ThirdComponent},
100+
{ path: "first", component: FirstComponent},
101+
{ path: "second", component: SecondComponent},
102+
{ path: "third", component: ThirdComponent},
103103
];
104104

105105
export const ClearHistoryRouterProviders = [

‎ng-sample/app/examples/router/clear-history.component.html‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,19 @@
33

44
<Button row="1" colSpan="3" text="BACK" (tap)="nav.back()" class="stretch"></Button>
55

6-
<Button col="0" row="2" [nsRouterLink]="['/first']"
6+
<Button col="0" row="2" [nsRouterLink]="['/first']"nsRouterLinkActive="active"
77
text="P1" class="stretch"></Button>
8-
<Button col="0" row="3" [nsRouterLink]="['/first']" clearHistory="true" pageTransition="false"
8+
<Button col="0" row="3" [nsRouterLink]="['/first']" nsRouterLinkActive="active" clearHistory="true" pageTransition="false"
99
text="P1[clear]" class="stretch"></Button>
1010

11-
<Button col="1" row="2" [nsRouterLink]="['/second']"
11+
<Button col="1" row="2" [nsRouterLink]="['/second']" nsRouterLinkActive="active"
1212
text="P2" class="stretch"></Button>
13-
<Button col="1" row="3" [nsRouterLink]="['/second']" clearHistory="true" pageTransition="none"
13+
<Button col="1" row="3" [nsRouterLink]="['/second']" nsRouterLinkActive="active" clearHistory="true" pageTransition="none"
1414
text="P2[clear]" class="stretch"></Button>
1515

16-
<Button col="2" row="2" [nsRouterLink]="['/third']"
16+
<Button col="2" row="2" [nsRouterLink]="['/third']" nsRouterLinkActive="active"
1717
text="P3" class="stretch"></Button>
18-
<Button col="2" row="3" [nsRouterLink]="['/third']" clearHistory="true" pageTransition="flipRight"
18+
<Button col="2" row="3" [nsRouterLink]="['/third']" nsRouterLinkActive="active" clearHistory="true" pageTransition="flipRight"
1919
text="P3[clear]" class="stretch"></Button>
2020

2121
<GridLayout colSpan="3" row="5">

‎ng-sample/app/examples/router/router-outlet-test.ts‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class FirstComponent implements OnInit, OnDestroy {
1414
ngOnInit() {
1515
console.log("FirstComponent - ngOnInit()");
1616
}
17-
17+
1818
ngOnDestroy() {
1919
console.log("FirstComponent - ngOnDestroy()");
2020
}
@@ -50,9 +50,9 @@ class SecondComponent implements OnInit, OnDestroy {
5050
template: `
5151
<StackLayout>
5252
<StackLayout class="nav">
53-
<Button text="First" nsRouterLink="/"></Button>
54-
<Button text="Second(1)" nsRouterLink="/second/1"></Button> <!-- Both work -->
55-
<Button text="Second(2)" [nsRouterLink]="['/second', '2' ]"></Button> <!-- Both work -->
53+
<Button text="First" nsRouterLinkActive="active" nsRouterLink="/first"></Button>
54+
<Button text="Second(1)" nsRouterLinkActive="active" nsRouterLink="/second/1"></Button> <!-- Both work -->
55+
<Button text="Second(2)" nsRouterLinkActive="active" [nsRouterLink]="['/second', '2' ]"></Button> <!-- Both work -->
5656
</StackLayout>
5757
5858
<router-outlet></router-outlet>
@@ -64,7 +64,8 @@ export class RouterOutletAppComponent {
6464

6565

6666
const routes: RouterConfig = [
67-
{ path: "", component: FirstComponent},
67+
{ path: "", redirectTo: "/first", terminal: true },
68+
{ path: "first", component: FirstComponent },
6869
{ path: "second/:id", component: SecondComponent },
6970
];
7071

‎ng-sample/app/examples/router/styles.css‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
horizontal-align: center;
2323
}
2424

25+
.active {
26+
color: orangered;
27+
}
28+
2529
.router-link-active {
2630
background-color: lightcoral;
2731
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /