Skip to content

Commit d035c86

Browse files
committed
feat(SidebarNavLink): allow routerLink properties as item.linkProps
- feat(SidebarNavLink): allow `routerLink` properties as `item.linkProps` - refactor(SidebarNavLink): `active` class workaround with NavigationEnd Observable - refactor(SidebarNavLink): add pipe SidebarNavLink
1 parent 981f8a7 commit d035c86

File tree

6 files changed

+117
-38
lines changed

6 files changed

+117
-38
lines changed

projects/coreui/angular/src/lib/sidebar/app-sidebar-nav.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import {QueryParamsHandling} from '@angular/router';
2+
13
export interface INavAttributes {
24
[propName: string]: any;
35
}
@@ -10,13 +12,26 @@ export interface INavWrapper {
1012
export interface INavBadge {
1113
text: string;
1214
variant: string;
15+
class?: string;
1316
}
1417

1518
export interface INavLabel {
1619
class?: string;
1720
variant: string;
1821
}
1922

23+
export interface INavLinkProps {
24+
queryParams?: {[k: string]: any};
25+
fragment?: string;
26+
queryParamsHandling?: QueryParamsHandling;
27+
preserveFragment?: boolean;
28+
skipLocationChange?: boolean;
29+
replaceUrl?: boolean;
30+
state?: {[k: string]: any};
31+
routerLinkActiveOptions?: {exact: boolean};
32+
routerLinkActive?: string | string[];
33+
}
34+
2035
export interface INavData {
2136
name?: string;
2237
url?: string | any[];
@@ -30,4 +45,5 @@ export interface INavData {
3045
class?: string;
3146
label?: INavLabel;
3247
wrapper?: INavWrapper;
48+
linkProps?: INavLinkProps;
3349
}

projects/coreui/angular/src/lib/sidebar/app-sidebar-nav/app-sidebar-nav-items.component.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import {Component, Input} from '@angular/core';
1+
import {Component, Inject, Input, Renderer2} from '@angular/core';
22
import {Router} from '@angular/router';
3+
import {DOCUMENT} from '@angular/common';
34

45
import {SidebarNavHelper} from '../app-sidebar-nav.service';
56

@@ -41,16 +42,36 @@ import {SidebarNavHelper} from '../app-sidebar-nav.service';
4142
*ngSwitchDefault
4243
[item]="item"
4344
class="nav-item"
44-
[ngClass]="item | appSidebarNavItemClass">
45+
[ngClass]="item | appSidebarNavItemClass"
46+
(linkClick)="hideMobile()"
47+
>
4548
</app-sidebar-nav-link>
4649
</ng-container>
4750
</ng-container>
4851
`
4952
})
5053
export class AppSidebarNavItemsComponent {
51-
@Input() items: Array<any>;
54+
55+
protected _items: any;
56+
57+
@Input()
58+
set items(items: Array<any>) {
59+
this._items = [...items];
60+
}
61+
get items(): Array<any> {
62+
return this._items;
63+
}
64+
5265
constructor(
66+
@Inject(DOCUMENT) private document: any,
67+
private renderer: Renderer2,
5368
public router: Router,
5469
public helper: SidebarNavHelper
5570
) {}
71+
72+
public hideMobile() {
73+
if (this.document.body.classList.contains('sidebar-show')) {
74+
this.renderer.removeClass(this.document.body, 'sidebar-show');
75+
}
76+
}
5677
}

projects/coreui/angular/src/lib/sidebar/app-sidebar-nav/app-sidebar-nav-link.component.html

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,32 @@
11
<ng-container [ngSwitch]="linkType">
22
<a *ngSwitchCase="'disabled'"
3-
[ngClass]="getLinkClass()"
3+
[ngClass]="item | appSidebarNavLink"
44
[appHtmlAttr]="item.attributes"
55
>
66
<app-sidebar-nav-link-content [item]="item"></app-sidebar-nav-link-content>
77
</a>
88
<a *ngSwitchCase="'external'"
9-
[ngClass]="getLinkClass()"
9+
[ngClass]="item | appSidebarNavLink"
1010
[href]="href"
1111
[appHtmlAttr]="item.attributes"
12+
(click)="linkClicked()"
1213
>
1314
<app-sidebar-nav-link-content [item]="item"></app-sidebar-nav-link-content>
1415
</a>
1516
<a *ngSwitchDefault
16-
[ngClass]="getLinkClass()"
17+
[ngClass]="item | appSidebarNavLink"
1718
[appHtmlAttr]="item.attributes"
18-
[target]="item.attributes && item.attributes.target ? item.attributes.target : '_self'"
19-
[attr.disabled]="isDisabled()"
20-
routerLinkActive="active"
19+
[target]="item.attributes?.target"
20+
[queryParams]="item.linkProps?.queryParams"
21+
[fragment]="item.linkProps?.fragment"
22+
[queryParamsHandling]="item.linkProps?.queryParamsHandling"
23+
[preserveFragment]="item.linkProps?.preserveFragment"
24+
[skipLocationChange]="item.linkProps?.skipLocationChange"
25+
[replaceUrl]="item.linkProps?.replaceUrl"
26+
[state]="item.linkProps?.state"
2127
[routerLink]="item.url"
22-
(click)="hideMobile()"
28+
[class.active]="linkActive"
29+
(click)="linkClicked()"
2330
>
2431
<app-sidebar-nav-link-content [item]="item"></app-sidebar-nav-link-content>
2532
</a>
Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
import {Component, Inject, Input, OnInit, Renderer2} from '@angular/core';
2-
import {DOCUMENT} from '@angular/common';
1+
import {Component, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core';
32
import {SidebarNavHelper} from '../app-sidebar-nav.service';
3+
import {NavigationEnd, Router} from '@angular/router';
4+
import {Observable, Subscription} from 'rxjs';
5+
import {filter} from 'rxjs/operators';
46

57
@Component({
68
selector: 'app-sidebar-nav-link-content',
@@ -15,6 +17,7 @@ import {SidebarNavHelper} from '../app-sidebar-nav.service';
1517
})
1618
export class AppSidebarNavLinkContentComponent {
1719
@Input() item: any;
20+
1821
constructor(
1922
public helper: SidebarNavHelper
2023
) { }
@@ -25,54 +28,66 @@ export class AppSidebarNavLinkContentComponent {
2528
templateUrl: './app-sidebar-nav-link.component.html',
2629
providers: [ SidebarNavHelper ]
2730
})
28-
export class AppSidebarNavLinkComponent implements OnInit {
29-
@Input() item: any;
31+
export class AppSidebarNavLinkComponent implements OnInit, OnDestroy {
32+
33+
protected _item: any;
34+
35+
@Input()
36+
set item(item: any) {
37+
this._item = JSON.parse(JSON.stringify(item));
38+
}
39+
get item(): any {
40+
return this._item;
41+
}
42+
43+
@Output() linkClick = new EventEmitter();
44+
3045
public linkType: string;
3146
public href: string;
47+
public linkActive: boolean;
3248

33-
private classes = { 'nav-link': true };
49+
private navigationEndObservable: Observable<NavigationEnd>;
50+
private navSubscription: Subscription;
3451

35-
constructor(
36-
@Inject(DOCUMENT) private document: any,
37-
private renderer: Renderer2,
38-
public helper: SidebarNavHelper
39-
) { }
52+
constructor (
53+
public router: Router,
54+
) {
55+
this.navigationEndObservable = router.events.pipe(
56+
filter(event => {
57+
return event instanceof NavigationEnd;
58+
})
59+
) as Observable<NavigationEnd>;
60+
}
4061

4162
ngOnInit() {
4263
this.linkType = this.getLinkType();
4364
this.href = this.isDisabled() ? '' : (this.item.href || this.item.url);
65+
this.linkActive = this.router.url.split(/[?#(]/)[0] === this.item.url.split(/[?#(]/)[0];
66+
this.navSubscription = this.navigationEndObservable.subscribe(event => {
67+
const itemUrlArray = this.item.url.split(/[?#(]/)[0].split('/');
68+
const urlArray = event.urlAfterRedirects.split(/[?#(]/)[0].split('/');
69+
this.linkActive = itemUrlArray.every((value, index) => value === urlArray[index]);
70+
});
4471
}
4572

46-
public getLinkClass() {
47-
const disabled = this.isDisabled();
48-
this.classes['disabled'] = disabled;
49-
this.classes['btn-link'] = disabled;
50-
if (this.hasVariant()) {
51-
const variant = `nav-link-${this.item.variant}`;
52-
this.classes[variant] = true;
53-
}
54-
return this.classes;
73+
ngOnDestroy(): void {
74+
this.navSubscription.unsubscribe();
5575
}
5676

5777
public getLinkType() {
5878
return this.isDisabled() ? 'disabled' : this.isExternalLink() ? 'external' : 'link';
5979
}
6080

61-
public hasVariant() {
62-
return !!this.item.variant;
63-
}
64-
6581
public isDisabled() {
6682
return (this.item.attributes && this.item.attributes.disabled) ? true : null;
6783
}
6884

6985
public isExternalLink() {
70-
return !!this.item.href || this.item.url.substring(0, 4) === 'http';
86+
const linkPath = Array.isArray(this.item.url) ? this.item.url[0] : this.item.url;
87+
return !!this.item.href || linkPath.substring(0, 4) === 'http';
7188
}
7289

73-
public hideMobile() {
74-
if (this.document.body.classList.contains('sidebar-show')) {
75-
this.renderer.removeClass(this.document.body, 'sidebar-show');
76-
}
90+
linkClicked() {
91+
this.linkClick.emit();
7792
}
7893
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { Pipe, PipeTransform } from '@angular/core';
2+
3+
@Pipe({
4+
name: 'appSidebarNavLink'
5+
})
6+
export class AppSidebarNavLinkPipe implements PipeTransform {
7+
8+
transform(item: any): any {
9+
10+
const classes = { 'nav-link': true };
11+
12+
const disabled = item.attributes && item.attributes.disabled;
13+
classes['disabled'] = disabled;
14+
classes['btn-link'] = disabled;
15+
classes[`nav-link-${item.variant}`] = !!item.variant;
16+
return classes;
17+
}
18+
}

projects/coreui/angular/src/lib/sidebar/app-sidebar.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { SidebarNavHelper } from './app-sidebar-nav.service';
2121
import { AppSidebarNavLabelComponent } from './app-sidebar-nav/app-sidebar-nav-label.component';
2222
import { AppSidebarNavIconPipe } from './app-sidebar-nav/app-sidebar-nav-icon.pipe';
2323
import { AppSidebarNavBadgePipe } from './app-sidebar-nav/app-sidebar-nav-badge.pipe';
24+
import { AppSidebarNavLinkPipe } from './app-sidebar-nav/app-sidebar-nav-link.pipe';
2425
import { AppSidebarNavItemClassPipe } from './app-sidebar-nav/app-sidebar-nav-item-class.pipe';
2526

2627
@NgModule({
@@ -65,6 +66,7 @@ import { AppSidebarNavItemClassPipe } from './app-sidebar-nav/app-sidebar-nav-it
6566
AppSidebarNavLabelComponent,
6667
AppSidebarNavIconPipe,
6768
AppSidebarNavBadgePipe,
69+
AppSidebarNavLinkPipe,
6870
AppSidebarNavItemClassPipe
6971
],
7072
providers: [

0 commit comments

Comments
 (0)