From 1439edc5e8780ae0f2e7a5dddef3177ef900c2ae Mon Sep 17 00:00:00 2001 From: Ralf Aron Date: Mon, 10 Jun 2024 21:26:54 +0200 Subject: [PATCH] fix: zoneless aas components --- .../src/lib/aas-tree/aas-tree-search.ts | 35 +++++---- .../src/lib/aas-tree/aas-tree.component.ts | 76 +++++++------------ .../src/lib/aas-tree/aas-tree.store.ts | 2 + .../aas-portal/src/app/aas/aas.component.html | 12 +-- .../aas-portal/src/app/aas/aas.component.ts | 41 +++++----- .../{aas-store.service.ts => aas.store.ts} | 4 +- .../src/app/aas/commands/delete-command.ts | 4 +- .../app/aas/commands/new-element-command.ts | 4 +- .../aas/commands/update-element-command.ts | 4 +- .../aas-portal/src/app/start/start.store.ts | 1 - .../src/test/aas/aas-store.service.spec.ts | 6 +- .../src/test/aas/aas.component.spec.ts | 6 +- .../src/test/aas/delete-command.spec.ts | 6 +- .../src/test/aas/new-element-command.spec.ts | 6 +- .../test/aas/update-element-command.spec.ts | 6 +- 15 files changed, 98 insertions(+), 115 deletions(-) rename projects/aas-portal/src/app/aas/{aas-store.service.ts => aas.store.ts} (95%) diff --git a/projects/aas-lib/src/lib/aas-tree/aas-tree-search.ts b/projects/aas-lib/src/lib/aas-tree/aas-tree-search.ts index b50411ca..87499a9a 100644 --- a/projects/aas-lib/src/lib/aas-tree/aas-tree-search.ts +++ b/projects/aas-lib/src/lib/aas-tree/aas-tree-search.ts @@ -6,7 +6,7 @@ * *****************************************************************************/ -import { Injectable } from '@angular/core'; +import { Injectable, untracked } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import trim from 'lodash-es/trim'; import { @@ -34,7 +34,8 @@ export class AASTreeSearch { ) {} public find(referable: aas.Referable): void { - const index = this.store.rows().findIndex(row => row.element === referable); + const state = untracked(this.store.state); + const index = state.rows.findIndex(row => row.element === referable); if (index >= 0) { this.store.setMatchIndex(index); } @@ -64,6 +65,7 @@ export class AASTreeSearch { if (terms.length > 0) { this.terms = terms; + this.findFirst(); } else { this.store.setMatchIndex(-1); } @@ -71,21 +73,22 @@ export class AASTreeSearch { public findNext(): boolean { let completed = false; - if (this.store.rows().length > 0 && this.terms.length > 0) { + const state = untracked(this.store.state); + if (state.rows.length > 0 && this.terms.length > 0) { let match = false; - let i = this.store.matchIndex() < 0 ? 0 : this.store.matchIndex() + 1; - if (i >= this.store.rows().length) { + let i = state.matchIndex < 0 ? 0 : state.matchIndex + 1; + if (i >= state.rows.length) { i = 0; } const start = i; while (this.loop) { - if (this.match(this.store.rows()[i])) { + if (this.match(state.rows[i])) { match = true; break; } - if (++i >= this.store.rows().length) { + if (++i >= state.rows.length) { i = 0; completed = true; } @@ -103,18 +106,19 @@ export class AASTreeSearch { public findPrevious(): boolean { let completed = false; - if (this.store.rows().length > 0 && this.terms.length > 0) { + const state = untracked(this.store.state); + if (state.rows.length > 0 && this.terms.length > 0) { let match = false; - let i = this.store.matchIndex() <= 0 ? this.store.rows().length - 1 : this.store.matchIndex() - 1; + let i = state.matchIndex <= 0 ? state.rows.length - 1 : state.matchIndex - 1; const start = i; while (this.loop) { - if (this.match(this.store.rows()[i])) { + if (this.match(state.rows[i])) { match = true; break; } if (--i <= 0) { - i = this.store.rows().length - 1; + i = state.rows.length - 1; completed = true; } @@ -134,17 +138,18 @@ export class AASTreeSearch { } private findFirst(): void { - if (this.store.rows().length > 0 && this.terms.length > 0) { + const state = untracked(this.store.state); + if (state.rows.length > 0 && this.terms.length > 0) { let match = false; - let i = this.store.matchIndex() < 0 ? 0 : this.store.matchIndex(); + let i = state.matchIndex < 0 ? 0 : state.matchIndex; const start = i; while (this.loop) { - if (this.match(this.store.rows()[i])) { + if (this.match(state.rows[i])) { match = true; break; } - if (++i >= this.store.rows().length) { + if (++i >= state.rows.length) { i = 0; } diff --git a/projects/aas-lib/src/lib/aas-tree/aas-tree.component.ts b/projects/aas-lib/src/lib/aas-tree/aas-tree.component.ts index aed4b13e..9b1c2257 100644 --- a/projects/aas-lib/src/lib/aas-tree/aas-tree.component.ts +++ b/projects/aas-lib/src/lib/aas-tree/aas-tree.component.ts @@ -12,19 +12,7 @@ import { BehaviorSubject, Subscription } from 'rxjs'; import { WebSocketSubject } from 'rxjs/webSocket'; import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; -import isEqual from 'lodash-es/isEqual'; -import { - ChangeDetectionStrategy, - Component, - EventEmitter, - Input, - OnDestroy, - OnInit, - Output, - computed, - effect, - input, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, OnDestroy, OnInit, computed, effect, input, output } from '@angular/core'; import { aas, @@ -101,16 +89,22 @@ export class AASTreeComponent implements OnInit, OnDestroy { private readonly webSocketFactory: WebSocketFactoryService, private readonly clipboard: ClipboardService, ) { - effect(() => { - const searchText = this.search(); - if (searchText) { - this.searching.start(searchText); - } - }); + effect( + () => { + const searchText = this.searchExpression(); + if (searchText) { + this.searching.start(searchText); + } + }, + { allowSignalWrites: true }, + ); - effect(() => { - this.store.updateRows(this.document()); - }); + effect( + () => { + this.store.updateRows(this.document()); + }, + { allowSignalWrites: true }, + ); effect(() => { if (this.state() === 'online') { @@ -121,7 +115,7 @@ export class AASTreeComponent implements OnInit, OnDestroy { }); effect(() => { - this.selectedChange.emit(this.store.selectedElements()); + this.selected.emit(this.store.selectedElements()); }); effect(() => { @@ -148,21 +142,9 @@ export class AASTreeComponent implements OnInit, OnDestroy { public readonly state = input('offline'); - public readonly search = input(''); - - @Input() - public get selected(): aas.Referable[] { - return this.store.selectedElements(); - } + public readonly searchExpression = input(''); - public set selected(values: aas.Referable[]) { - if (!isEqual(values, this.store.selectedElements())) { - this.store.setSelectedElements(values); - } - } - - @Output() - public selectedChange = new EventEmitter(); + public readonly selected = output(); public readonly onlineReady = computed(() => this.document()?.onlineReady ?? false); @@ -182,20 +164,12 @@ export class AASTreeComponent implements OnInit, OnDestroy { public readonly nodes = this.store.nodes; + public readonly rows = this.store.rows; + public readonly matchIndex = this.store.matchIndex; public readonly matchRow = this.store.matchRow; - public readonly rows = this.store.rows; - - public ngOnInit(): void { - this.subscription.add( - this.translate.onLangChange.subscribe(() => { - this.store.updateRows(this.document()); - }), - ); - } - public get message(): string { const document = this.document(); if (document) { @@ -212,6 +186,14 @@ export class AASTreeComponent implements OnInit, OnDestroy { return this.translate.instant('INFO_NO_SHELL_AVAILABLE'); } + public ngOnInit(): void { + this.subscription.add( + this.translate.onLangChange.subscribe(() => { + this.store.updateRows(this.document()); + }), + ); + } + public ngOnDestroy(): void { this.subscription.unsubscribe(); this.webSocketSubject?.unsubscribe(); diff --git a/projects/aas-lib/src/lib/aas-tree/aas-tree.store.ts b/projects/aas-lib/src/lib/aas-tree/aas-tree.store.ts index 5bb68080..8f922484 100644 --- a/projects/aas-lib/src/lib/aas-tree/aas-tree.store.ts +++ b/projects/aas-lib/src/lib/aas-tree/aas-tree.store.ts @@ -41,6 +41,8 @@ export class AASTreeStore { private readonly translate: TranslateService, ) {} + public readonly state = this._state.asReadonly(); + public readonly rows = computed(() => this._state().rows); public readonly matchIndex = computed(() => this._state().matchIndex); diff --git a/projects/aas-portal/src/app/aas/aas.component.html b/projects/aas-portal/src/app/aas/aas.component.html index f9367daf..1e55dbfd 100644 --- a/projects/aas-portal/src/app/aas/aas.component.html +++ b/projects/aas-portal/src/app/aas/aas.component.html @@ -33,8 +33,8 @@ }
- +
@@ -55,11 +55,11 @@ } - @@ -96,8 +96,8 @@
- + diff --git a/projects/aas-portal/src/app/aas/aas.component.ts b/projects/aas-portal/src/app/aas/aas.component.ts index 6385d435..c9950d32 100644 --- a/projects/aas-portal/src/app/aas/aas.component.ts +++ b/projects/aas-portal/src/app/aas/aas.component.ts @@ -31,7 +31,7 @@ import { NewElementFormComponent } from './new-element-form/new-element-form.com import { DashboardChartType, DashboardPage, DashboardService } from '../dashboard/dashboard.service'; import { DashboardQuery } from '../types/dashboard-query-params'; import { ToolbarService } from '../toolbar.service'; -import { AASStoreService } from './aas-store.service'; +import { AASStore } from './aas.store'; import { AsyncPipe } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; import { FormsModule } from '@angular/forms'; @@ -47,7 +47,7 @@ export class AASComponent implements OnInit, OnDestroy, AfterViewInit { private readonly subscription = new Subscription(); public constructor( - private readonly store: AASStoreService, + private readonly store: AASStore, private readonly router: Router, private readonly route: ActivatedRoute, private readonly modal: NgbModal, @@ -84,7 +84,7 @@ export class AASComponent implements OnInit, OnDestroy, AfterViewInit { public readonly state = this.store.state; - public readonly search = this.store.search; + public readonly searchExpression = this.store.searchExpression; public readonly dashboardPages = this.dashboard.pages; @@ -115,35 +115,30 @@ export class AASComponent implements OnInit, OnDestroy, AfterViewInit { return document != null && !document.readonly && document.modified ? document.modified : false; }); - public get canNewElement(): boolean { - return this.selectedElements.length === 1; - } + public readonly canNewElement = computed(() => this.selectedElements().length === 1); - public get canEditElement(): boolean { - return this.selectedElements.length === 1; - } + public readonly canEditElement = computed(() => this.selectedElements().length === 1); - public get canDeleteElement(): boolean { - return ( + public readonly canDeleteElement = computed( + () => this.selectedElements().length > 0 && - this.selectedElements().every(item => item.modelType !== 'AssetAdministrationShell') - ); - } + this.selectedElements().every(item => item.modelType !== 'AssetAdministrationShell'), + ); - public get canAddToDashboard(): boolean { - const selectedElements = this.selectedElements; + public readonly canAddToDashboard = computed(() => { + const selectedElements = this.selectedElements(); return ( - this.dashboardPage != null && - selectedElements().length > 0 && - selectedElements().every(element => this.isNumberProperty(element) || this.isTimeSeries(element)) + this.dashboardPage() != null && + selectedElements.length > 0 && + selectedElements.every(element => this.isNumberProperty(element) || this.isTimeSeries(element)) ); - } + }); public ngOnInit(): void { this.subscription.add( this.route.queryParams.subscribe(params => { if (params?.search) { - this.store.search.set(params.search); + this.store.searchExpression.set(params.search); } if (params) { @@ -302,8 +297,8 @@ export class AASComponent implements OnInit, OnDestroy, AfterViewInit { ); } - public applySearch(text: string): void { - this.store.search.set(text); + public searchExpressionChange(text: string): void { + this.store.searchExpression.set(text); } private isNumberProperty(element: aas.Referable): boolean { diff --git a/projects/aas-portal/src/app/aas/aas-store.service.ts b/projects/aas-portal/src/app/aas/aas.store.ts similarity index 95% rename from projects/aas-portal/src/app/aas/aas-store.service.ts rename to projects/aas-portal/src/app/aas/aas.store.ts index 058ad62a..bd61247d 100644 --- a/projects/aas-portal/src/app/aas/aas-store.service.ts +++ b/projects/aas-portal/src/app/aas/aas.store.ts @@ -14,7 +14,7 @@ import { AASApiService } from './aas-api.service'; @Injectable({ providedIn: 'root', }) -export class AASStoreService { +export class AASStore { private readonly _document = signal(null); public constructor(private readonly api: AASApiService) {} @@ -23,7 +23,7 @@ export class AASStoreService { public readonly state = signal('offline'); - public readonly search = signal(''); + public readonly searchExpression = signal(''); public getDocumentContent(document: AASDocument): void { this.api.getContent(document.id, document.endpoint).subscribe({ diff --git a/projects/aas-portal/src/app/aas/commands/delete-command.ts b/projects/aas-portal/src/app/aas/commands/delete-command.ts index 18efd0a4..b17ad19c 100644 --- a/projects/aas-portal/src/app/aas/commands/delete-command.ts +++ b/projects/aas-portal/src/app/aas/commands/delete-command.ts @@ -20,7 +20,7 @@ import { import cloneDeep from 'lodash-es/cloneDeep'; import { Command } from '../../types/command'; -import { AASStoreService } from '../aas-store.service'; +import { AASStore } from '../aas.store'; export class DeleteCommand extends Command { private readonly elements: aas.Referable[]; @@ -28,7 +28,7 @@ export class DeleteCommand extends Command { private document: AASDocument; public constructor( - private readonly store: AASStoreService, + private readonly store: AASStore, document: AASDocument, elements: aas.Referable | aas.Referable[], ) { diff --git a/projects/aas-portal/src/app/aas/commands/new-element-command.ts b/projects/aas-portal/src/app/aas/commands/new-element-command.ts index a4beedc0..2fe1e19b 100644 --- a/projects/aas-portal/src/app/aas/commands/new-element-command.ts +++ b/projects/aas-portal/src/app/aas/commands/new-element-command.ts @@ -21,7 +21,7 @@ import { noop, } from 'common'; -import { AASStoreService } from '../aas-store.service'; +import { AASStore } from '../aas.store'; export class NewElementCommand extends Command { private readonly memento: AASDocument; @@ -29,7 +29,7 @@ export class NewElementCommand extends Command { private content: aas.Environment; public constructor( - private readonly store: AASStoreService, + private readonly store: AASStore, document: AASDocument, private readonly parent: aas.Referable, private readonly element: aas.Referable | aas.Environment, diff --git a/projects/aas-portal/src/app/aas/commands/update-element-command.ts b/projects/aas-portal/src/app/aas/commands/update-element-command.ts index 331ab89b..88a11589 100644 --- a/projects/aas-portal/src/app/aas/commands/update-element-command.ts +++ b/projects/aas-portal/src/app/aas/commands/update-element-command.ts @@ -9,7 +9,7 @@ import { aas, AASDocument, selectReferable, noop } from 'common'; import cloneDeep from 'lodash-es/cloneDeep'; import { Command } from '../../types/command'; -import { AASStoreService } from '../aas-store.service'; +import { AASStore } from '../aas.store'; export class UpdateElementCommand extends Command { private readonly origin: aas.SubmodelElement; @@ -18,7 +18,7 @@ export class UpdateElementCommand extends Command { private document: AASDocument; public constructor( - private readonly store: AASStoreService, + private readonly store: AASStore, document: AASDocument, origin: aas.SubmodelElement, element: aas.SubmodelElement, diff --git a/projects/aas-portal/src/app/start/start.store.ts b/projects/aas-portal/src/app/start/start.store.ts index 664e1373..13ea7fa9 100644 --- a/projects/aas-portal/src/app/start/start.store.ts +++ b/projects/aas-portal/src/app/start/start.store.ts @@ -18,7 +18,6 @@ export class StartStore { private _totalCount = 0; private readonly _documents = signal([]); private readonly _activeFavorites = signal(''); - // private readonly _favoritesLists = signal([]); public constructor( private readonly api: StartApiService, diff --git a/projects/aas-portal/src/test/aas/aas-store.service.spec.ts b/projects/aas-portal/src/test/aas/aas-store.service.spec.ts index 53ffb1b6..73b61251 100644 --- a/projects/aas-portal/src/test/aas/aas-store.service.spec.ts +++ b/projects/aas-portal/src/test/aas/aas-store.service.spec.ts @@ -9,11 +9,11 @@ import { TestBed } from '@angular/core/testing'; import { NotifyService } from 'aas-lib'; -import { AASStoreService } from '../../app/aas/aas-store.service'; +import { AASStore } from '../../app/aas/aas.store'; import { AASApiService } from '../../app/aas/aas-api.service'; describe('AASStoreService', () => { - let service: AASStoreService; + let service: AASStore; beforeEach(() => { TestBed.configureTestingModule({ @@ -29,7 +29,7 @@ describe('AASStoreService', () => { ], }); - service = TestBed.inject(AASStoreService); + service = TestBed.inject(AASStore); }); it('should be created', () => { diff --git a/projects/aas-portal/src/test/aas/aas.component.spec.ts b/projects/aas-portal/src/test/aas/aas.component.spec.ts index 0512b2a5..cb78bdb4 100644 --- a/projects/aas-portal/src/test/aas/aas.component.spec.ts +++ b/projects/aas-portal/src/test/aas/aas.component.spec.ts @@ -28,7 +28,7 @@ import { Router, provideRouter } from '@angular/router'; import { Component, EventEmitter, Input, Output, signal } from '@angular/core'; import { AASApiService } from '../../app/aas/aas-api.service'; import { ToolbarService } from '../../app/toolbar.service'; -import { AASStoreService } from '../../app/aas/aas-store.service'; +import { AASStore } from '../../app/aas/aas.store'; @Component({ selector: 'fhg-aas-tree', @@ -81,7 +81,7 @@ describe('AASComponent', () => { let fixture: ComponentFixture; let dashboard: jasmine.SpyObj; let router: Router; - let store: AASStoreService; + let store: AASStore; let api: jasmine.SpyObj; let download: jasmine.SpyObj; let pages: DashboardPage[]; @@ -146,7 +146,7 @@ describe('AASComponent', () => { fixture = TestBed.createComponent(AASComponent); component = fixture.componentInstance; - store = TestBed.inject(AASStoreService); + store = TestBed.inject(AASStore); router = TestBed.inject(Router); store.setDocument(sampleDocument); fixture.detectChanges(); diff --git a/projects/aas-portal/src/test/aas/delete-command.spec.ts b/projects/aas-portal/src/test/aas/delete-command.spec.ts index 4ade8354..ffbdd51d 100644 --- a/projects/aas-portal/src/test/aas/delete-command.spec.ts +++ b/projects/aas-portal/src/test/aas/delete-command.spec.ts @@ -12,12 +12,12 @@ import cloneDeep from 'lodash-es/cloneDeep'; import { NotifyService } from 'aas-lib'; import { DeleteCommand } from '../../app/aas/commands/delete-command'; import { sampleDocument } from '../../test/assets/sample-document'; -import { AASStoreService } from '../../app/aas/aas-store.service'; +import { AASStore } from '../../app/aas/aas.store'; import { AASApiService } from '../../app/aas/aas-api.service'; describe('DeleteCommand', () => { let command: DeleteCommand; - let store: AASStoreService; + let store: AASStore; let document: AASDocument; beforeEach(() => { @@ -34,7 +34,7 @@ describe('DeleteCommand', () => { ], }); - store = TestBed.inject(AASStoreService); + store = TestBed.inject(AASStore); document = cloneDeep(sampleDocument); store.setDocument(document); }); diff --git a/projects/aas-portal/src/test/aas/new-element-command.spec.ts b/projects/aas-portal/src/test/aas/new-element-command.spec.ts index 5695e7f5..0b11dc1c 100644 --- a/projects/aas-portal/src/test/aas/new-element-command.spec.ts +++ b/projects/aas-portal/src/test/aas/new-element-command.spec.ts @@ -12,14 +12,14 @@ import cloneDeep from 'lodash-es/cloneDeep'; import { NotifyService } from 'aas-lib'; import { aasNoTechnicalData, submodelTechnicalData } from '../../test/assets/sample-document'; import { NewElementCommand } from '../../app/aas/commands/new-element-command'; -import { AASStoreService } from '../../app/aas/aas-store.service'; +import { AASStore } from '../../app/aas/aas.store'; import { AASApiService } from '../../app/aas/aas-api.service'; describe('NewElementCommand', function () { let command: NewElementCommand; let document: AASDocument; let submodel: aas.Submodel; - let store: AASStoreService; + let store: AASStore; beforeEach(function () { document = cloneDeep(aasNoTechnicalData); @@ -38,7 +38,7 @@ describe('NewElementCommand', function () { ], }); - store = TestBed.inject(AASStoreService); + store = TestBed.inject(AASStore); store.setDocument(document); }); diff --git a/projects/aas-portal/src/test/aas/update-element-command.spec.ts b/projects/aas-portal/src/test/aas/update-element-command.spec.ts index 1e919cc1..55073faf 100644 --- a/projects/aas-portal/src/test/aas/update-element-command.spec.ts +++ b/projects/aas-portal/src/test/aas/update-element-command.spec.ts @@ -11,13 +11,13 @@ import { aas, AASDocument, selectElement } from 'common'; import cloneDeep from 'lodash-es/cloneDeep'; import { UpdateElementCommand } from '../../app/aas/commands/update-element-command'; import { sampleDocument } from '../../test/assets/sample-document'; -import { AASStoreService } from '../../app/aas/aas-store.service'; +import { AASStore } from '../../app/aas/aas.store'; import { NotifyService } from 'aas-lib'; import { AASApiService } from '../../app/aas/aas-api.service'; describe('SetValueCommand', function () { let command: UpdateElementCommand; - let store: AASStoreService; + let store: AASStore; let document: AASDocument; let property: aas.Property; let element: aas.Property; @@ -41,7 +41,7 @@ describe('SetValueCommand', function () { ], }); - store = TestBed.inject(AASStoreService); + store = TestBed.inject(AASStore); store.setDocument(document); });