Commit 075138cc5c6115c0242aebe6cf4dcfd1c99c08db

Authored by Igor Kulikov
Committed by GitHub
2 parents 6a4c2791 dc77426d

Merge pull request #3463 from vvlladd28/improvement/menu/trackBy

Improvement draw menu and breadCrumb
@@ -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;