Commit 075138cc5c6115c0242aebe6cf4dcfd1c99c08db
Committed by
GitHub
Merge pull request #3463 from vvlladd28/improvement/menu/trackBy
Improvement draw menu and breadCrumb
Showing
9 changed files
with
47 additions
and
7 deletions
@@ -14,9 +14,11 @@ | @@ -14,9 +14,11 @@ | ||
14 | /// limitations under the License. | 14 | /// limitations under the License. |
15 | /// | 15 | /// |
16 | 16 | ||
17 | +import { HasUUID } from '@shared/models/id/has-uuid'; | ||
18 | + | ||
17 | export declare type MenuSectionType = 'link' | 'toggle'; | 19 | export declare type MenuSectionType = 'link' | 'toggle'; |
18 | 20 | ||
19 | -export class MenuSection { | 21 | +export interface MenuSection extends HasUUID{ |
20 | name: string; | 22 | name: string; |
21 | type: MenuSectionType; | 23 | type: MenuSectionType; |
22 | path: string; | 24 | path: string; |
@@ -26,12 +28,12 @@ export class MenuSection { | @@ -26,12 +28,12 @@ export class MenuSection { | ||
26 | pages?: Array<MenuSection>; | 28 | pages?: Array<MenuSection>; |
27 | } | 29 | } |
28 | 30 | ||
29 | -export class HomeSection { | 31 | +export interface HomeSection { |
30 | name: string; | 32 | name: string; |
31 | places: Array<HomeSectionPlace>; | 33 | places: Array<HomeSectionPlace>; |
32 | } | 34 | } |
33 | 35 | ||
34 | -export class HomeSectionPlace { | 36 | +export interface HomeSectionPlace { |
35 | name: string; | 37 | name: string; |
36 | icon: string; | 38 | icon: string; |
37 | isMdiIcon?: boolean; | 39 | isMdiIcon?: boolean; |
@@ -24,6 +24,7 @@ import { HomeSection, MenuSection } from '@core/services/menu.models'; | @@ -24,6 +24,7 @@ import { HomeSection, MenuSection } from '@core/services/menu.models'; | ||
24 | import { BehaviorSubject, Observable, Subject } from 'rxjs'; | 24 | import { BehaviorSubject, Observable, Subject } from 'rxjs'; |
25 | import { Authority } from '@shared/models/authority.enum'; | 25 | import { Authority } from '@shared/models/authority.enum'; |
26 | import { AuthUser } from '@shared/models/user.model'; | 26 | import { AuthUser } from '@shared/models/user.model'; |
27 | +import { guid } from '@core/utils'; | ||
27 | 28 | ||
28 | @Injectable({ | 29 | @Injectable({ |
29 | providedIn: 'root' | 30 | providedIn: 'root' |
@@ -74,24 +75,28 @@ export class MenuService { | @@ -74,24 +75,28 @@ export class MenuService { | ||
74 | const sections: Array<MenuSection> = []; | 75 | const sections: Array<MenuSection> = []; |
75 | sections.push( | 76 | sections.push( |
76 | { | 77 | { |
78 | + id: guid(), | ||
77 | name: 'home.home', | 79 | name: 'home.home', |
78 | type: 'link', | 80 | type: 'link', |
79 | path: '/home', | 81 | path: '/home', |
80 | icon: 'home' | 82 | icon: 'home' |
81 | }, | 83 | }, |
82 | { | 84 | { |
85 | + id: guid(), | ||
83 | name: 'tenant.tenants', | 86 | name: 'tenant.tenants', |
84 | type: 'link', | 87 | type: 'link', |
85 | path: '/tenants', | 88 | path: '/tenants', |
86 | icon: 'supervisor_account' | 89 | icon: 'supervisor_account' |
87 | }, | 90 | }, |
88 | { | 91 | { |
92 | + id: guid(), | ||
89 | name: 'widget.widget-library', | 93 | name: 'widget.widget-library', |
90 | type: 'link', | 94 | type: 'link', |
91 | path: '/widgets-bundles', | 95 | path: '/widgets-bundles', |
92 | icon: 'now_widgets' | 96 | icon: 'now_widgets' |
93 | }, | 97 | }, |
94 | { | 98 | { |
99 | + id: guid(), | ||
95 | name: 'admin.system-settings', | 100 | name: 'admin.system-settings', |
96 | type: 'toggle', | 101 | type: 'toggle', |
97 | path: '/settings', | 102 | path: '/settings', |
@@ -99,18 +104,21 @@ export class MenuService { | @@ -99,18 +104,21 @@ export class MenuService { | ||
99 | icon: 'settings', | 104 | icon: 'settings', |
100 | pages: [ | 105 | pages: [ |
101 | { | 106 | { |
107 | + id: guid(), | ||
102 | name: 'admin.general', | 108 | name: 'admin.general', |
103 | type: 'link', | 109 | type: 'link', |
104 | path: '/settings/general', | 110 | path: '/settings/general', |
105 | icon: 'settings_applications' | 111 | icon: 'settings_applications' |
106 | }, | 112 | }, |
107 | { | 113 | { |
114 | + id: guid(), | ||
108 | name: 'admin.outgoing-mail', | 115 | name: 'admin.outgoing-mail', |
109 | type: 'link', | 116 | type: 'link', |
110 | path: '/settings/outgoing-mail', | 117 | path: '/settings/outgoing-mail', |
111 | icon: 'mail' | 118 | icon: 'mail' |
112 | }, | 119 | }, |
113 | { | 120 | { |
121 | + id: guid(), | ||
114 | name: 'admin.security-settings', | 122 | name: 'admin.security-settings', |
115 | type: 'link', | 123 | type: 'link', |
116 | path: '/settings/security-settings', | 124 | path: '/settings/security-settings', |
@@ -173,54 +181,63 @@ export class MenuService { | @@ -173,54 +181,63 @@ export class MenuService { | ||
173 | const sections: Array<MenuSection> = []; | 181 | const sections: Array<MenuSection> = []; |
174 | sections.push( | 182 | sections.push( |
175 | { | 183 | { |
184 | + id: guid(), | ||
176 | name: 'home.home', | 185 | name: 'home.home', |
177 | type: 'link', | 186 | type: 'link', |
178 | path: '/home', | 187 | path: '/home', |
179 | icon: 'home' | 188 | icon: 'home' |
180 | }, | 189 | }, |
181 | { | 190 | { |
191 | + id: guid(), | ||
182 | name: 'rulechain.rulechains', | 192 | name: 'rulechain.rulechains', |
183 | type: 'link', | 193 | type: 'link', |
184 | path: '/ruleChains', | 194 | path: '/ruleChains', |
185 | icon: 'settings_ethernet' | 195 | icon: 'settings_ethernet' |
186 | }, | 196 | }, |
187 | { | 197 | { |
198 | + id: guid(), | ||
188 | name: 'customer.customers', | 199 | name: 'customer.customers', |
189 | type: 'link', | 200 | type: 'link', |
190 | path: '/customers', | 201 | path: '/customers', |
191 | icon: 'supervisor_account' | 202 | icon: 'supervisor_account' |
192 | }, | 203 | }, |
193 | { | 204 | { |
205 | + id: guid(), | ||
194 | name: 'asset.assets', | 206 | name: 'asset.assets', |
195 | type: 'link', | 207 | type: 'link', |
196 | path: '/assets', | 208 | path: '/assets', |
197 | icon: 'domain' | 209 | icon: 'domain' |
198 | }, | 210 | }, |
199 | { | 211 | { |
212 | + id: guid(), | ||
200 | name: 'device.devices', | 213 | name: 'device.devices', |
201 | type: 'link', | 214 | type: 'link', |
202 | path: '/devices', | 215 | path: '/devices', |
203 | icon: 'devices_other' | 216 | icon: 'devices_other' |
204 | }, | 217 | }, |
205 | { | 218 | { |
219 | + id: guid(), | ||
206 | name: 'entity-view.entity-views', | 220 | name: 'entity-view.entity-views', |
207 | type: 'link', | 221 | type: 'link', |
208 | path: '/entityViews', | 222 | path: '/entityViews', |
209 | icon: 'view_quilt' | 223 | icon: 'view_quilt' |
210 | }, | 224 | }, |
211 | { | 225 | { |
226 | + id: guid(), | ||
212 | name: 'widget.widget-library', | 227 | name: 'widget.widget-library', |
213 | type: 'link', | 228 | type: 'link', |
214 | path: '/widgets-bundles', | 229 | path: '/widgets-bundles', |
215 | icon: 'now_widgets' | 230 | icon: 'now_widgets' |
216 | }, | 231 | }, |
217 | { | 232 | { |
233 | + id: guid(), | ||
218 | name: 'dashboard.dashboards', | 234 | name: 'dashboard.dashboards', |
219 | type: 'link', | 235 | type: 'link', |
220 | path: '/dashboards', | 236 | path: '/dashboards', |
221 | icon: 'dashboards' | 237 | icon: 'dashboards' |
222 | }, | 238 | }, |
223 | { | 239 | { |
240 | + id: guid(), | ||
224 | name: 'audit-log.audit-logs', | 241 | name: 'audit-log.audit-logs', |
225 | type: 'link', | 242 | type: 'link', |
226 | path: '/auditLogs', | 243 | path: '/auditLogs', |
@@ -316,30 +333,35 @@ export class MenuService { | @@ -316,30 +333,35 @@ export class MenuService { | ||
316 | const sections: Array<MenuSection> = []; | 333 | const sections: Array<MenuSection> = []; |
317 | sections.push( | 334 | sections.push( |
318 | { | 335 | { |
336 | + id: guid(), | ||
319 | name: 'home.home', | 337 | name: 'home.home', |
320 | type: 'link', | 338 | type: 'link', |
321 | path: '/home', | 339 | path: '/home', |
322 | icon: 'home' | 340 | icon: 'home' |
323 | }, | 341 | }, |
324 | { | 342 | { |
343 | + id: guid(), | ||
325 | name: 'asset.assets', | 344 | name: 'asset.assets', |
326 | type: 'link', | 345 | type: 'link', |
327 | path: '/assets', | 346 | path: '/assets', |
328 | icon: 'domain' | 347 | icon: 'domain' |
329 | }, | 348 | }, |
330 | { | 349 | { |
350 | + id: guid(), | ||
331 | name: 'device.devices', | 351 | name: 'device.devices', |
332 | type: 'link', | 352 | type: 'link', |
333 | path: '/devices', | 353 | path: '/devices', |
334 | icon: 'devices_other' | 354 | icon: 'devices_other' |
335 | }, | 355 | }, |
336 | { | 356 | { |
357 | + id: guid(), | ||
337 | name: 'entity-view.entity-views', | 358 | name: 'entity-view.entity-views', |
338 | type: 'link', | 359 | type: 'link', |
339 | path: '/entityViews', | 360 | path: '/entityViews', |
340 | icon: 'view_quilt' | 361 | icon: 'view_quilt' |
341 | }, | 362 | }, |
342 | { | 363 | { |
364 | + id: guid(), | ||
343 | name: 'dashboard.dashboards', | 365 | name: 'dashboard.dashboards', |
344 | type: 'link', | 366 | type: 'link', |
345 | path: '/dashboards', | 367 | path: '/dashboards', |
@@ -24,7 +24,7 @@ | @@ -24,7 +24,7 @@ | ||
24 | [ngClass]="{'tb-toggled' : sectionActive()}"></span> | 24 | [ngClass]="{'tb-toggled' : sectionActive()}"></span> |
25 | </a> | 25 | </a> |
26 | <ul id="docs-menu-{{section.name | nospace}}" class="tb-menu-toggle-list" [ngStyle]="{height: sectionHeight()}"> | 26 | <ul id="docs-menu-{{section.name | nospace}}" class="tb-menu-toggle-list" [ngStyle]="{height: sectionHeight()}"> |
27 | - <li *ngFor="let page of section.pages"> | 27 | + <li *ngFor="let page of section.pages; trackBy: trackBySectionPages"> |
28 | <tb-menu-link [section]="page"></tb-menu-link> | 28 | <tb-menu-link [section]="page"></tb-menu-link> |
29 | </li> | 29 | </li> |
30 | </ul> | 30 | </ul> |
@@ -44,4 +44,8 @@ export class MenuToggleComponent implements OnInit { | @@ -44,4 +44,8 @@ export class MenuToggleComponent implements OnInit { | ||
44 | return '0px'; | 44 | return '0px'; |
45 | } | 45 | } |
46 | } | 46 | } |
47 | + | ||
48 | + trackBySectionPages(index: number, section: MenuSection){ | ||
49 | + return section.id; | ||
50 | + } | ||
47 | } | 51 | } |
@@ -16,7 +16,7 @@ | @@ -16,7 +16,7 @@ | ||
16 | 16 | ||
17 | --> | 17 | --> |
18 | <ul fxFlex fxLayout="column" fxLayoutAlign="start stretch" class="tb-side-menu"> | 18 | <ul fxFlex fxLayout="column" fxLayoutAlign="start stretch" class="tb-side-menu"> |
19 | - <li *ngFor="let section of menuSections$| async" [ngSwitch]="section.type === 'link'"> | 19 | + <li *ngFor="let section of menuSections$ | async; trackBy: trackByMenuSection" [ngSwitch]="section.type === 'link'"> |
20 | <tb-menu-link *ngSwitchCase="true" [section]="section"></tb-menu-link> | 20 | <tb-menu-link *ngSwitchCase="true" [section]="section"></tb-menu-link> |
21 | <tb-menu-toggle *ngSwitchCase="false" [section]="section"></tb-menu-toggle> | 21 | <tb-menu-toggle *ngSwitchCase="false" [section]="section"></tb-menu-toggle> |
22 | </li> | 22 | </li> |
@@ -16,6 +16,7 @@ | @@ -16,6 +16,7 @@ | ||
16 | 16 | ||
17 | import { Component, OnInit } from '@angular/core'; | 17 | import { Component, OnInit } from '@angular/core'; |
18 | import { MenuService } from '@core/services/menu.service'; | 18 | import { MenuService } from '@core/services/menu.service'; |
19 | +import { MenuSection } from '@core/services/menu.models'; | ||
19 | 20 | ||
20 | @Component({ | 21 | @Component({ |
21 | selector: 'tb-side-menu', | 22 | selector: 'tb-side-menu', |
@@ -29,6 +30,10 @@ export class SideMenuComponent implements OnInit { | @@ -29,6 +30,10 @@ export class SideMenuComponent implements OnInit { | ||
29 | constructor(private menuService: MenuService) { | 30 | constructor(private menuService: MenuService) { |
30 | } | 31 | } |
31 | 32 | ||
33 | + trackByMenuSection(index: number, section: MenuSection){ | ||
34 | + return section.id; | ||
35 | + } | ||
36 | + | ||
32 | ngOnInit() { | 37 | ngOnInit() { |
33 | } | 38 | } |
34 | 39 |
@@ -19,7 +19,7 @@ | @@ -19,7 +19,7 @@ | ||
19 | <h1 fxFlex fxHide.gt-sm *ngIf="lastBreadcrumb$ | async; let breadcrumb"> | 19 | <h1 fxFlex fxHide.gt-sm *ngIf="lastBreadcrumb$ | async; let breadcrumb"> |
20 | {{ breadcrumb.ignoreTranslate ? (breadcrumb.labelFunction ? breadcrumb.labelFunction() : breadcrumb.label) : (breadcrumb.label | translate) }} | 20 | {{ breadcrumb.ignoreTranslate ? (breadcrumb.labelFunction ? breadcrumb.labelFunction() : breadcrumb.label) : (breadcrumb.label | translate) }} |
21 | </h1> | 21 | </h1> |
22 | - <span fxHide.lt-md fxLayout="row" *ngFor="let breadcrumb of breadcrumbs$ | async; last as isLast;" [ngSwitch]="isLast"> | 22 | + <span fxHide.lt-md fxLayout="row" *ngFor="let breadcrumb of breadcrumbs$ | async; trackBy: trackByBreadcrumbs; last as isLast;" [ngSwitch]="isLast"> |
23 | <a *ngSwitchCase="false" [routerLink]="breadcrumb.link" [queryParams]="breadcrumb.queryParams"> | 23 | <a *ngSwitchCase="false" [routerLink]="breadcrumb.link" [queryParams]="breadcrumb.queryParams"> |
24 | <mat-icon *ngIf="breadcrumb.isMdiIcon" [svgIcon]="breadcrumb.icon"> | 24 | <mat-icon *ngIf="breadcrumb.isMdiIcon" [svgIcon]="breadcrumb.icon"> |
25 | </mat-icon> | 25 | </mat-icon> |
@@ -20,6 +20,7 @@ import { BreadCrumb, BreadCrumbConfig } from './breadcrumb'; | @@ -20,6 +20,7 @@ import { BreadCrumb, BreadCrumbConfig } from './breadcrumb'; | ||
20 | import { ActivatedRoute, ActivatedRouteSnapshot, NavigationEnd, Router } from '@angular/router'; | 20 | import { ActivatedRoute, ActivatedRouteSnapshot, NavigationEnd, Router } from '@angular/router'; |
21 | import { distinctUntilChanged, filter, map } from 'rxjs/operators'; | 21 | import { distinctUntilChanged, filter, map } from 'rxjs/operators'; |
22 | import { TranslateService } from '@ngx-translate/core'; | 22 | import { TranslateService } from '@ngx-translate/core'; |
23 | +import { guid } from '@core/utils'; | ||
23 | 24 | ||
24 | @Component({ | 25 | @Component({ |
25 | selector: 'tb-breadcrumb', | 26 | selector: 'tb-breadcrumb', |
@@ -94,6 +95,7 @@ export class BreadcrumbComponent implements OnInit, OnDestroy { | @@ -94,6 +95,7 @@ export class BreadcrumbComponent implements OnInit, OnDestroy { | ||
94 | const isMdiIcon = icon.startsWith('mdi:'); | 95 | const isMdiIcon = icon.startsWith('mdi:'); |
95 | const link = [ route.pathFromRoot.map(v => v.url.map(segment => segment.toString()).join('/')).join('/') ]; | 96 | const link = [ route.pathFromRoot.map(v => v.url.map(segment => segment.toString()).join('/')).join('/') ]; |
96 | const breadcrumb = { | 97 | const breadcrumb = { |
98 | + id: guid(), | ||
97 | label, | 99 | label, |
98 | labelFunction, | 100 | labelFunction, |
99 | ignoreTranslate, | 101 | ignoreTranslate, |
@@ -110,4 +112,8 @@ export class BreadcrumbComponent implements OnInit, OnDestroy { | @@ -110,4 +112,8 @@ export class BreadcrumbComponent implements OnInit, OnDestroy { | ||
110 | } | 112 | } |
111 | return newBreadcrumbs; | 113 | return newBreadcrumbs; |
112 | } | 114 | } |
115 | + | ||
116 | + trackByBreadcrumbs(index: number, breadcrumb: BreadCrumb){ | ||
117 | + return breadcrumb.id; | ||
118 | + } | ||
113 | } | 119 | } |
@@ -16,8 +16,9 @@ | @@ -16,8 +16,9 @@ | ||
16 | 16 | ||
17 | import { ActivatedRouteSnapshot, Params } from '@angular/router'; | 17 | import { ActivatedRouteSnapshot, Params } from '@angular/router'; |
18 | import { TranslateService } from '@ngx-translate/core'; | 18 | import { TranslateService } from '@ngx-translate/core'; |
19 | +import { HasUUID } from '@shared/models/id/has-uuid'; | ||
19 | 20 | ||
20 | -export interface BreadCrumb { | 21 | +export interface BreadCrumb extends HasUUID{ |
21 | label: string; | 22 | label: string; |
22 | labelFunction?: () => string; | 23 | labelFunction?: () => string; |
23 | ignoreTranslate: boolean; | 24 | ignoreTranslate: boolean; |