diff --git a/CHANGELOG.md b/CHANGELOG.md index 82b761170..e7e0c6b06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# [11.2.0](https://github.com/NativeScript/nativescript-angular/compare/11.0.1...11.2.0) (2021-03-24) + + +### Bug Fixes + +* **ivy:** nsRouterLinkActive works on nsRouterLink ([#2322](https://github.com/NativeScript/nativescript-angular/issues/2322)) ([13a3562](https://github.com/NativeScript/nativescript-angular/commit/13a3562146ef8516fea8bb3d988f8e0ddc9617a7)) + + + ## [11.0.1](https://github.com/NativeScript/nativescript-angular/compare/11.0.0...11.0.1) (2021-01-22) diff --git a/nativescript-angular-package/package.json b/nativescript-angular-package/package.json index 0d2a16f8e..317ebb9a1 100644 --- a/nativescript-angular-package/package.json +++ b/nativescript-angular-package/package.json @@ -1,6 +1,6 @@ { "name": "nativescript-angular", - "version": "11.0.0", + "version": "11.2.0", "description": "Compatibility with old style nativescript-angular imports.", "homepage": "https://www.nativescript.org/", "bugs": "https://github.com/NativeScript/nativescript-angular/issues", @@ -30,7 +30,7 @@ "@nativescript/angular": "ns-angular" } }, - "whitelistedNonPeerDependencies": [ + "allowedNonPeerDependencies": [ "." ] }, diff --git a/nativescript-angular/package.json b/nativescript-angular/package.json index 8afa82555..304e20917 100644 --- a/nativescript-angular/package.json +++ b/nativescript-angular/package.json @@ -1,6 +1,6 @@ { "name": "@nativescript/angular", - "version": "11.0.1", + "version": "11.2.0", "description": "An Angular renderer that lets you build mobile apps with NativeScript.", "homepage": "https://www.nativescript.org/", "bugs": "https://github.com/NativeScript/nativescript-angular/issues", @@ -52,7 +52,7 @@ "@nativescript/core": "ns-core" } }, - "whitelistedNonPeerDependencies": [ + "allowedNonPeerDependencies": [ "." ] }, @@ -70,22 +70,22 @@ "@angular/router": "^11.0.0" }, "devDependencies": { - "@angular/animations": "~11.0.0", - "@angular/common": "~11.0.0", - "@angular/compiler": "~11.0.0", - "@angular/compiler-cli": "~11.0.0", - "@angular/core": "~11.0.0", - "@angular/forms": "~11.0.0", - "@angular/platform-browser": "~11.0.0", - "@angular/platform-browser-dynamic": "~11.0.0", - "@angular/router": "~11.0.0", - "@nativescript/core": "~7.1.0", + "@angular/animations": "~11.2.0", + "@angular/common": "~11.2.0", + "@angular/compiler": "~11.2.0", + "@angular/compiler-cli": "~11.2.0", + "@angular/core": "~11.2.0", + "@angular/forms": "~11.2.0", + "@angular/platform-browser": "~11.2.0", + "@angular/platform-browser-dynamic": "~11.2.0", + "@angular/router": "~11.2.0", + "@nativescript/core": "~7.3.0", "codelyzer": "^5.2.0", "conventional-changelog-cli": "^2.0.34", "husky": "^4.2.5", "lint-staged": "^10.2.11", "nativescript-typedoc-theme": "git://github.com/NativeScript/nativescript-typedoc-theme.git#master", - "ng-packagr": "~11.0.0", + "ng-packagr": "~11.2.0", "prettier": "^2.0.5", "rxjs": "~6.6.0", "tslint": "^6.1.2", diff --git a/nativescript-angular/router/ns-router-link-active.ts b/nativescript-angular/router/ns-router-link-active.ts index b00a35679..333cba56d 100644 --- a/nativescript-angular/router/ns-router-link-active.ts +++ b/nativescript-angular/router/ns-router-link-active.ts @@ -1,10 +1,11 @@ -import { AfterContentInit, ContentChildren, Directive, ElementRef, Input, OnChanges, OnDestroy, QueryList, Renderer2 } from '@angular/core'; -import { Subscription } from 'rxjs'; +import { AfterContentInit, ChangeDetectorRef, ContentChildren, Directive, ElementRef, Input, OnChanges, OnDestroy, Optional, QueryList, Renderer2 } from '@angular/core'; +import { from, of, Subscription } from 'rxjs'; import { NavigationEnd, Router, UrlTree } from '@angular/router'; import { containsTree } from './private-imports/router-url-tree'; import { NSRouterLink } from './ns-router-link'; +import { mergeAll } from 'rxjs/operators'; /** * The NSRouterLinkActive directive lets you add a CSS class to an element when the link"s route @@ -54,16 +55,17 @@ import { NSRouterLink } from './ns-router-link'; }) export class NSRouterLinkActive implements OnChanges, OnDestroy, AfterContentInit { // tslint:disable-line:max-line-length directive-class-suffix - @ContentChildren(NSRouterLink) links: QueryList; + @ContentChildren(NSRouterLink, { descendants: true }) links: QueryList; private classes: string[] = []; - private subscription: Subscription; + private routerEventsSubscription: Subscription; + private linkInputChangesSubscription?: Subscription; private active: boolean = false; @Input() nsRouterLinkActiveOptions: { exact: boolean } = { exact: false }; - constructor(private router: Router, private element: ElementRef, private renderer: Renderer2) { - this.subscription = router.events.subscribe((s) => { + constructor(private router: Router, private element: ElementRef, private renderer: Renderer2, private readonly cdr: ChangeDetectorRef, @Optional() private link?: NSRouterLink) { + this.routerEventsSubscription = router.events.subscribe((s) => { if (s instanceof NavigationEnd) { this.update(); } @@ -75,8 +77,25 @@ export class NSRouterLinkActive implements OnChanges, OnDestroy, AfterContentIni } ngAfterContentInit(): void { - this.links.changes.subscribe(() => this.update()); - this.update(); + // `of(null)` is used to force subscribe body to execute once immediately (like `startWith`). + from([this.links.changes, of(null)]) + .pipe(mergeAll()) + .subscribe((_) => { + this.update(); + this.subscribeToEachLinkOnChanges(); + }); + } + + private subscribeToEachLinkOnChanges() { + this.linkInputChangesSubscription?.unsubscribe(); + const allLinkChanges = [...this.links.toArray(), this.link].filter((link): link is NSRouterLink => !!link).map((link) => link.onChanges); + this.linkInputChangesSubscription = from(allLinkChanges) + .pipe(mergeAll()) + .subscribe((link) => { + if (this.isActive !== this.isLinkActive(this.router)(link)) { + this.update(); + } + }); } @Input('nsRouterLinkActive') @@ -92,30 +111,34 @@ export class NSRouterLinkActive implements OnChanges, OnDestroy, AfterContentIni this.update(); } ngOnDestroy(): any { - this.subscription.unsubscribe(); + this.routerEventsSubscription.unsubscribe(); + this.linkInputChangesSubscription?.unsubscribe(); } private update(): void { if (!this.links) { return; } - const hasActiveLinks = this.hasActiveLinks(); - // react only when status has changed to prevent unnecessary dom updates - if (this.active !== hasActiveLinks) { - const currentUrlTree = this.router.parseUrl(this.router.url); - const isActiveLinks = this.reduceList(currentUrlTree, this.links); - this.classes.forEach((c) => { - if (isActiveLinks) { - this.renderer.addClass(this.element.nativeElement, c); - } else { - this.renderer.removeClass(this.element.nativeElement, c); - } - }); - } - Promise.resolve(hasActiveLinks).then((active) => (this.active = active)); + Promise.resolve().then(() => { + const hasActiveLinks = this.hasActiveLinks(); + if (this.active !== hasActiveLinks) { + this.active = hasActiveLinks; + const currentUrlTree = this.router.parseUrl(this.router.url); + const links = this.link ? [...this.links.toArray(), this.link] : this.links; + const isActiveLinks = this.reduceList(currentUrlTree, links); + this.cdr.markForCheck(); + this.classes.forEach((c) => { + if (isActiveLinks) { + this.renderer.addClass(this.element.nativeElement, c); + } else { + this.renderer.removeClass(this.element.nativeElement, c); + } + }); + } + }); } - private reduceList(currentUrlTree: UrlTree, q: QueryList): boolean { + private reduceList(currentUrlTree: UrlTree, q: QueryList | Array): boolean { return q.reduce((res: boolean, link: NSRouterLink) => { return res || containsTree(currentUrlTree, link.urlTree, this.nsRouterLinkActiveOptions.exact); }, false); @@ -126,6 +149,7 @@ export class NSRouterLinkActive implements OnChanges, OnDestroy, AfterContentIni } private hasActiveLinks(): boolean { - return this.links.some(this.isLinkActive(this.router)); + const isActiveCheckFn = this.isLinkActive(this.router); + return (this.link && isActiveCheckFn(this.link)) || this.links.some(isActiveCheckFn); } } diff --git a/nativescript-angular/router/ns-router-link.ts b/nativescript-angular/router/ns-router-link.ts index 02ba34a39..40c0b3a10 100644 --- a/nativescript-angular/router/ns-router-link.ts +++ b/nativescript-angular/router/ns-router-link.ts @@ -1,10 +1,11 @@ -import { Directive, Input, ElementRef, NgZone } from '@angular/core'; +import { Directive, Input, ElementRef, NgZone, OnChanges, SimpleChanges } from '@angular/core'; import { NavigationExtras } from '@angular/router'; import { ActivatedRoute, Router, UrlTree } from '@angular/router'; import { NavigationTransition } from '@nativescript/core'; import { NativeScriptDebug } from '../trace'; import { RouterExtensions } from './router-extensions'; import { NavigationOptions } from './ns-location-utils'; +import { Subject } from 'rxjs'; // Copied from "@angular/router/src/config" export type QueryParamsHandling = 'merge' | 'preserve' | ''; @@ -34,7 +35,7 @@ export type QueryParamsHandling = 'merge' | 'preserve' | ''; * And if the segment begins with `../`, the router will go up one level. */ @Directive({ selector: '[nsRouterLink]' }) -export class NSRouterLink { +export class NSRouterLink implements OnChanges { // tslint:disable-line:directive-class-suffix @Input() target: string; @Input() queryParams: { [k: string]: any }; @@ -50,6 +51,9 @@ export class NSRouterLink { @Input() pageTransition: boolean | string | NavigationTransition = true; @Input() pageTransitionDuration; + /** @internal */ + onChanges = new Subject(); + private commands: any[] = []; constructor(private ngZone: NgZone, private router: Router, private navigator: RouterExtensions, private route: ActivatedRoute, private el: ElementRef) {} @@ -75,6 +79,12 @@ export class NSRouterLink { }); } + ngOnChanges(changes: SimpleChanges) { + // This is subscribed to by `RouterLinkActive` so that it knows to update when there are changes + // to the RouterLinks it's tracking. + this.onChanges.next(this); + } + @Input('nsRouterLink') set params(data: any[] | string) { if (Array.isArray(data)) { diff --git a/nativescript-angular/testing/package.json b/nativescript-angular/testing/package.json index a809f1246..3c4d818df 100644 --- a/nativescript-angular/testing/package.json +++ b/nativescript-angular/testing/package.json @@ -6,12 +6,12 @@ "@nativescript/core": "ns-core" } }, - "whitelistedNonPeerDependencies": [ + "allowedNonPeerDependencies": [ "." ] }, "devDependencies": { - "@nativescript/angular": "rc", - "@nativescript/core": "rc" + "@nativescript/angular": "latest", + "@nativescript/core": "latest" } }