diff --git a/apps/responsive-app-e2e/src/integration/app.spec.ts b/apps/responsive-app-e2e/src/integration/app.spec.ts
index 642b3d5..1d8efe7 100644
--- a/apps/responsive-app-e2e/src/integration/app.spec.ts
+++ b/apps/responsive-app-e2e/src/integration/app.spec.ts
@@ -39,7 +39,7 @@ describe('responsive-app', () => {
});
it('should show links and info on desktop', () => {
- getNavigationLinks().should('have.length', 5);
+ getNavigationLinks().should('have.length', 6);
cy.get('h2').contains('Select user profile!');
});
@@ -51,7 +51,7 @@ describe('responsive-app', () => {
it('should show links on desktop profile', () => {
cy.visit('/3');
- getNavigationLinks().should('have.length', 5);
+ getNavigationLinks().should('have.length', 6);
});
it('should not show back button on desktop profile', () => {
diff --git a/apps/responsive-app/src/app/app.component.html b/apps/responsive-app/src/app/app.component.html
index 483439c..5a24143 100644
--- a/apps/responsive-app/src/app/app.component.html
+++ b/apps/responsive-app/src/app/app.component.html
@@ -48,6 +48,14 @@
linkActive="mat-list-single-selected-option"
>Profile 5
+ Profile 6 (Async)
diff --git a/apps/responsive-app/src/app/app.module.ts b/apps/responsive-app/src/app/app.module.ts
index dda93be..16c29c4 100644
--- a/apps/responsive-app/src/app/app.module.ts
+++ b/apps/responsive-app/src/app/app.module.ts
@@ -14,6 +14,7 @@ import { MediaDirective } from './use-media/use-media.directive';
import { SelectUserComponent } from './select-user/select-user.component';
import { UserProfileComponent } from './user-profile/user-profile.component';
import { UserListComponent } from './user-list/user-list.component';
+import { ValueToAsyncValuePipe } from './value-to-async-value.pipe';
@NgModule({
declarations: [
@@ -22,6 +23,7 @@ import { UserListComponent } from './user-list/user-list.component';
SelectUserComponent,
UserProfileComponent,
UserListComponent,
+ ValueToAsyncValuePipe,
],
imports: [
BrowserModule,
diff --git a/apps/responsive-app/src/app/value-to-async-value.pipe.ts b/apps/responsive-app/src/app/value-to-async-value.pipe.ts
new file mode 100644
index 0000000..566f5ba
--- /dev/null
+++ b/apps/responsive-app/src/app/value-to-async-value.pipe.ts
@@ -0,0 +1,12 @@
+import { Pipe, PipeTransform } from '@angular/core';
+import { Observable, of } from 'rxjs';
+import { delay } from 'rxjs/operators';
+
+@Pipe({
+ name: 'valueToAsyncValue',
+})
+export class ValueToAsyncValuePipe implements PipeTransform {
+ transform(value: string): Observable {
+ return of(value).pipe(delay(10));
+ }
+}
diff --git a/libs/router/src/lib/link-active.directive.ts b/libs/router/src/lib/link-active.directive.ts
index c3eb183..7a9c0bb 100644
--- a/libs/router/src/lib/link-active.directive.ts
+++ b/libs/router/src/lib/link-active.directive.ts
@@ -15,6 +15,7 @@ import { LinkTo } from './link-to.directive';
import { Router } from './router.service';
import { combineLatest, of, Subject, Subscription } from 'rxjs';
import { map, mapTo, startWith, takeUntil } from 'rxjs/operators';
+import { filterNullable } from './operators/filter-nullable.operator';
export interface LinkActiveOptions {
exact: boolean;
@@ -39,8 +40,8 @@ export class LinkActive implements AfterContentInit, OnDestroy, OnChanges {
@ContentChildren(LinkTo, { descendants: true }) public links: QueryList<
LinkTo
>;
- @Input('linkActive') activeClass = 'active';
- @Input() activeOptions: LinkActiveOptions;
+ @Input('linkActive') activeClass: string | null = 'active';
+ @Input() activeOptions?: LinkActiveOptions | null;
private _activeOptions: LinkActiveOptions = { exact: true };
private _destroy$ = new Subject();
private _linksSub!: Subscription;
@@ -83,16 +84,20 @@ export class LinkActive implements AfterContentInit, OnDestroy, OnChanges {
.map((link) =>
link.hrefUpdated.pipe(
startWith(link.linkHref),
- mapTo(link.linkHref)
+ mapTo(link.linkHref),
+ filterNullable()
)
)
: [];
+
const link$ = this.link
? this.link.hrefUpdated.pipe(
startWith(this.link.linkHref),
- mapTo(this.link.linkHref)
+ mapTo(this.link.linkHref),
+ filterNullable()
)
: of('');
+
const router$ = this.router.url$.pipe(
map((path) => this.router.getExternalUrl(path || '/'))
);
@@ -109,14 +114,10 @@ export class LinkActive implements AfterContentInit, OnDestroy, OnChanges {
checkActive(linkHrefs: string[], path: string) {
const active = linkHrefs.reduce((isActive, current) => {
const [href] = current.split('?');
-
if (this._activeOptions.exact) {
- isActive = isActive ? isActive : href === path;
- } else {
- isActive = isActive ? isActive : path.startsWith(href);
+ return isActive ? isActive : href === path;
}
-
- return isActive;
+ return isActive ? isActive : path.startsWith(href);
}, false);
this.updateClasses(active);
diff --git a/libs/router/src/lib/link-to.directive.ts b/libs/router/src/lib/link-to.directive.ts
index ec6e512..1e6128d 100644
--- a/libs/router/src/lib/link-to.directive.ts
+++ b/libs/router/src/lib/link-to.directive.ts
@@ -23,19 +23,22 @@ const DEFAULT_TARGET = '_self';
@Directive({ selector: 'a[linkTo]' })
export class LinkTo {
@Input() target = DEFAULT_TARGET;
- @HostBinding('href') linkHref: string;
+ @HostBinding('href') linkHref?: string | null;
- @Input() set linkTo(href: string) {
+ @Input() set linkTo(href: string | null | undefined) {
+ if (href === null || href === undefined) {
+ return;
+ }
this._href = href;
this._updateHref();
}
- @Input() set queryParams(params: Params) {
+ @Input() set queryParams(params: Params | null | undefined) {
this._query = params;
this._updateHref();
}
- @Input() set fragment(hash: string) {
+ @Input() set fragment(hash: string | null | undefined) {
this._hash = hash;
this._updateHref();
}
@@ -54,6 +57,9 @@ export class LinkTo {
*/
@HostListener('click', ['$event'])
onClick(event: any) {
+ if (!this._href) {
+ return;
+ }
if (!this._comboClick(event) && this.target === DEFAULT_TARGET) {
this.router.go(this._href, this._query, this._hash);
diff --git a/libs/router/src/lib/operators/filter-nullable.operator.ts b/libs/router/src/lib/operators/filter-nullable.operator.ts
new file mode 100644
index 0000000..b4417cd
--- /dev/null
+++ b/libs/router/src/lib/operators/filter-nullable.operator.ts
@@ -0,0 +1,9 @@
+import { Observable } from 'rxjs';
+import { filter } from 'rxjs/operators';
+
+export function isNotNullable(it: T): it is NonNullable {
+ return it !== null && it !== undefined;
+}
+
+export const filterNullable = () => (source: Observable) =>
+ source.pipe(filter(isNotNullable));