Browse Source

UI: rewrite component search inputs form ngModel to FormControl; Add textSearch pageLink parameters trim value

pull/9021/head
Vladyslav_Prykhodko 3 years ago
parent
commit
12b232132f
  1. 2
      ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html
  2. 57
      ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts
  3. 2
      ui-ngx/src/app/modules/home/components/entity/entities-table.component.html
  4. 105
      ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts
  5. 2
      ui-ngx/src/app/modules/home/components/relation/relation-table.component.html
  6. 56
      ui-ngx/src/app/modules/home/components/relation/relation-table.component.ts
  7. 2
      ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.html
  8. 50
      ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.ts
  9. 2
      ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.html
  10. 49
      ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts
  11. 2
      ui-ngx/src/app/modules/home/components/widget/lib/entities-hierarchy-widget.component.html
  12. 70
      ui-ngx/src/app/modules/home/components/widget/lib/entities-hierarchy-widget.component.ts
  13. 2
      ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html
  14. 55
      ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts
  15. 2
      ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.html
  16. 66
      ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts
  17. 2
      ui-ngx/src/app/modules/home/home.component.html
  18. 49
      ui-ngx/src/app/modules/home/home.component.ts
  19. 8
      ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.html
  20. 39
      ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts
  21. 5
      ui-ngx/src/app/shared/models/page/page-link.ts

2
ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.html

@ -68,7 +68,7 @@
<mat-form-field fxFlex> <mat-form-field fxFlex>
<mat-label>&nbsp;</mat-label> <mat-label>&nbsp;</mat-label>
<input #searchInput matInput <input #searchInput matInput
[(ngModel)]="pageLink.textSearch" [formControl]="textSearch"
placeholder="{{ 'common.enter-search' | translate }}"/> placeholder="{{ 'common.enter-search' | translate }}"/>
</mat-form-field> </mat-form-field>
<button mat-icon-button (click)="exitFilterMode()" <button mat-icon-button (click)="exitFilterMode()"

57
ui-ngx/src/app/modules/home/components/attribute/attribute-table.component.ts

@ -23,6 +23,7 @@ import {
Injector, Injector,
Input, Input,
NgZone, NgZone,
OnDestroy,
OnInit, OnInit,
StaticProvider, StaticProvider,
ViewChild, ViewChild,
@ -38,8 +39,8 @@ import { TranslateService } from '@ngx-translate/core';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
import { DialogService } from '@core/services/dialog.service'; import { DialogService } from '@core/services/dialog.service';
import { Direction, SortOrder } from '@shared/models/page/sort-order'; import { Direction, SortOrder } from '@shared/models/page/sort-order';
import { fromEvent, merge } from 'rxjs'; import { merge, Subject } from 'rxjs';
import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators'; import { debounceTime, distinctUntilChanged, skip, startWith, takeUntil } from 'rxjs/operators';
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { import {
AttributeData, AttributeData,
@ -92,6 +93,7 @@ import {
DeleteTimeseriesPanelComponent, DeleteTimeseriesPanelComponent,
DeleteTimeseriesPanelData DeleteTimeseriesPanelData
} from '@home/components/attribute/delete-timeseries-panel.component'; } from '@home/components/attribute/delete-timeseries-panel.component';
import { FormBuilder } from '@angular/forms';
@Component({ @Component({
@ -100,7 +102,7 @@ import {
styleUrls: ['./attribute-table.component.scss'], styleUrls: ['./attribute-table.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class AttributeTableComponent extends PageComponent implements AfterViewInit, OnInit { export class AttributeTableComponent extends PageComponent implements AfterViewInit, OnInit, OnDestroy {
telemetryTypeTranslationsMap = telemetryTypeTranslations; telemetryTypeTranslationsMap = telemetryTypeTranslations;
isClientSideTelemetryTypeMap = isClientSideTelemetryType; isClientSideTelemetryTypeMap = isClientSideTelemetryType;
@ -185,6 +187,10 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI
@ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort; @ViewChild(MatSort) sort: MatSort;
textSearch = this.fb.control('', {nonNullable: true});
private destroy$ = new Subject<void>();
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private attributeService: AttributeService, private attributeService: AttributeService,
private telemetryWsService: TelemetryWebsocketService, private telemetryWsService: TelemetryWebsocketService,
@ -199,7 +205,8 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI
private widgetService: WidgetService, private widgetService: WidgetService,
private zone: NgZone, private zone: NgZone,
private cd: ChangeDetectorRef, private cd: ChangeDetectorRef,
private elementRef: ElementRef) { private elementRef: ElementRef,
private fb: FormBuilder) {
super(store); super(store);
this.dirtyValue = !this.activeValue; this.dirtyValue = !this.activeValue;
const sortOrder: SortOrder = { property: 'key', direction: Direction.ASC }; const sortOrder: SortOrder = { property: 'key', direction: Direction.ASC };
@ -222,6 +229,8 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI
if (this.widgetResize$) { if (this.widgetResize$) {
this.widgetResize$.disconnect(); this.widgetResize$.disconnect();
} }
this.destroy$.next();
this.destroy$.complete();
} }
attributeScopeChanged(attributeScope: TelemetryType) { attributeScopeChanged(attributeScope: TelemetryType) {
@ -232,25 +241,23 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI
} }
ngAfterViewInit() { ngAfterViewInit() {
this.textSearch.valueChanges.pipe(
debounceTime(150),
startWith(''),
distinctUntilChanged((a: string, b: string) => a.trim() === b.trim()),
skip(1),
takeUntil(this.destroy$)
).subscribe((value) => {
this.paginator.pageIndex = 0;
this.pageLink.textSearch = value.trim();
this.updateData();
});
fromEvent(this.searchInputField.nativeElement, 'keyup') this.sort.sortChange.pipe(takeUntil(this.destroy$)).subscribe(() => this.paginator.pageIndex = 0);
.pipe(
debounceTime(150), merge(this.sort.sortChange, this.paginator.page).pipe(
distinctUntilChanged(), takeUntil(this.destroy$)
tap(() => { ).subscribe(() => this.updateData());
this.paginator.pageIndex = 0;
this.updateData();
})
)
.subscribe();
this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0);
merge(this.sort.sortChange, this.paginator.page)
.pipe(
tap(() => this.updateData())
)
.subscribe();
this.viewsInited = true; this.viewsInited = true;
if (this.activeValue && this.entityIdValue) { if (this.activeValue && this.entityIdValue) {
@ -268,7 +275,6 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI
enterFilterMode() { enterFilterMode() {
this.textSearchMode = true; this.textSearchMode = true;
this.pageLink.textSearch = '';
setTimeout(() => { setTimeout(() => {
this.searchInputField.nativeElement.focus(); this.searchInputField.nativeElement.focus();
this.searchInputField.nativeElement.setSelectionRange(0, 0); this.searchInputField.nativeElement.setSelectionRange(0, 0);
@ -277,9 +283,7 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI
exitFilterMode() { exitFilterMode() {
this.textSearchMode = false; this.textSearchMode = false;
this.pageLink.textSearch = null; this.textSearch.reset();
this.paginator.pageIndex = 0;
this.updateData();
} }
resetSortAndFilter(update: boolean = true) { resetSortAndFilter(update: boolean = true) {
@ -293,6 +297,7 @@ export class AttributeTableComponent extends PageComponent implements AfterViewI
} }
this.mode = 'default'; this.mode = 'default';
this.textSearchMode = false; this.textSearchMode = false;
this.textSearch.reset('', {emitEvent: false});
this.selectedWidgetsBundleAlias = null; this.selectedWidgetsBundleAlias = null;
this.attributeScope = this.defaultAttributeScope; this.attributeScope = this.defaultAttributeScope;
this.pageLink.textSearch = null; this.pageLink.textSearch = null;

2
ui-ngx/src/app/modules/home/components/entity/entities-table.component.html

@ -111,7 +111,7 @@
<mat-form-field fxFlex> <mat-form-field fxFlex>
<mat-label>&nbsp;</mat-label> <mat-label>&nbsp;</mat-label>
<input #searchInput matInput <input #searchInput matInput
[(ngModel)]="pageLink.textSearch" [formControl]="textSearch"
placeholder="{{ translations.search | translate }}"/> placeholder="{{ translations.search | translate }}"/>
</mat-form-field> </mat-form-field>
<button mat-icon-button (click)="exitFilterMode()" <button mat-icon-button (click)="exitFilterMode()"

105
ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts

@ -24,6 +24,7 @@ import {
EventEmitter, EventEmitter,
Input, Input,
OnChanges, OnChanges,
OnDestroy,
OnInit, OnInit,
SimpleChanges, SimpleChanges,
ViewChild ViewChild
@ -36,9 +37,9 @@ import { MatDialog } from '@angular/material/dialog';
import { MatPaginator } from '@angular/material/paginator'; import { MatPaginator } from '@angular/material/paginator';
import { MatSort, SortDirection } from '@angular/material/sort'; import { MatSort, SortDirection } from '@angular/material/sort';
import { EntitiesDataSource } from '@home/models/datasource/entity-datasource'; import { EntitiesDataSource } from '@home/models/datasource/entity-datasource';
import { catchError, debounceTime, distinctUntilChanged, map, skip, tap } from 'rxjs/operators'; import { catchError, debounceTime, distinctUntilChanged, map, skip, startWith, takeUntil } from 'rxjs/operators';
import { Direction, SortOrder } from '@shared/models/page/sort-order'; import { Direction, SortOrder } from '@shared/models/page/sort-order';
import { forkJoin, fromEvent, merge, Observable, of, Subscription } from 'rxjs'; import { forkJoin, merge, Observable, of, Subject, Subscription } from 'rxjs';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { BaseData, HasId } from '@shared/models/base-data'; import { BaseData, HasId } from '@shared/models/base-data';
import { ActivatedRoute, QueryParamsHandling, Router } from '@angular/router'; import { ActivatedRoute, QueryParamsHandling, Router } from '@angular/router';
@ -59,12 +60,13 @@ import { AddEntityDialogData, EntityAction } from '@home/models/entity/entity-co
import { calculateIntervalStartEndTime, HistoryWindowType, Timewindow } from '@shared/models/time/time.models'; import { calculateIntervalStartEndTime, HistoryWindowType, Timewindow } from '@shared/models/time/time.models';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { TbAnchorComponent } from '@shared/components/tb-anchor.component'; import { TbAnchorComponent } from '@shared/components/tb-anchor.component';
import { isDefined, isEmptyStr, isEqual, isString, isUndefined } from '@core/utils'; import { isDefined, isEmptyStr, isEqual, isNotEmptyStr, isUndefined } from '@core/utils';
import { HasUUID } from '@shared/models/id/has-uuid'; import { HasUUID } from '@shared/models/id/has-uuid';
import { ResizeObserver } from '@juggle/resize-observer'; import { ResizeObserver } from '@juggle/resize-observer';
import { hidePageSizePixelValue } from '@shared/models/constants'; import { hidePageSizePixelValue } from '@shared/models/constants';
import { EntitiesTableAction, IEntitiesTableComponent } from '@home/models/entity/entity-table-component.models'; import { EntitiesTableAction, IEntitiesTableComponent } from '@home/models/entity/entity-table-component.models';
import { EntityDetailsPanelComponent } from '@home/components/entity/entity-details-panel.component'; import { EntityDetailsPanelComponent } from '@home/components/entity/entity-details-panel.component';
import { FormBuilder } from '@angular/forms';
@Component({ @Component({
selector: 'tb-entities-table', selector: 'tb-entities-table',
@ -72,7 +74,7 @@ import { EntityDetailsPanelComponent } from '@home/components/entity/entity-deta
styleUrls: ['./entities-table.component.scss'], styleUrls: ['./entities-table.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class EntitiesTableComponent extends PageComponent implements IEntitiesTableComponent, AfterViewInit, OnInit, OnChanges { export class EntitiesTableComponent extends PageComponent implements IEntitiesTableComponent, AfterViewInit, OnInit, OnChanges, OnDestroy {
@Input() @Input()
entitiesTableConfig: EntityTableConfig<BaseData<HasId>>; entitiesTableConfig: EntityTableConfig<BaseData<HasId>>;
@ -121,12 +123,13 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
@ViewChild('entityDetailsPanel') entityDetailsPanel: EntityDetailsPanelComponent; @ViewChild('entityDetailsPanel') entityDetailsPanel: EntityDetailsPanelComponent;
textSearch = this.fb.control('', {nonNullable: true});
private updateDataSubscription: Subscription; private updateDataSubscription: Subscription;
private viewInited = false; private viewInited = false;
private widgetResize$: ResizeObserver; private widgetResize$: ResizeObserver;
private destroy$ = new Subject<void>();
private rxSubscriptions = new Array<Subscription>();
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
public route: ActivatedRoute, public route: ActivatedRoute,
@ -137,7 +140,8 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
private cd: ChangeDetectorRef, private cd: ChangeDetectorRef,
private router: Router, private router: Router,
private componentFactoryResolver: ComponentFactoryResolver, private componentFactoryResolver: ComponentFactoryResolver,
private elementRef: ElementRef) { private elementRef: ElementRef,
private fb: FormBuilder) {
super(store); super(store);
} }
@ -145,11 +149,11 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
if (this.entitiesTableConfig) { if (this.entitiesTableConfig) {
this.init(this.entitiesTableConfig); this.init(this.entitiesTableConfig);
} else { } else {
this.rxSubscriptions.push(this.route.data.subscribe( this.route.data.pipe(
(data) => { takeUntil(this.destroy$)
).subscribe((data) => {
this.init(data.entitiesTableConfig); this.init(data.entitiesTableConfig);
} });
));
} }
this.widgetResize$ = new ResizeObserver(() => { this.widgetResize$ = new ResizeObserver(() => {
const showHidePageSize = this.elementRef.nativeElement.offsetWidth < hidePageSizePixelValue; const showHidePageSize = this.elementRef.nativeElement.offsetWidth < hidePageSizePixelValue;
@ -165,10 +169,8 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
if (this.widgetResize$) { if (this.widgetResize$) {
this.widgetResize$.disconnect(); this.widgetResize$.disconnect();
} }
this.rxSubscriptions.forEach((subscription) => { this.destroy$.next();
subscription.unsubscribe(); this.destroy$.complete();
});
this.rxSubscriptions.length = 0;
} }
ngOnChanges(changes: SimpleChanges): void { ngOnChanges(changes: SimpleChanges): void {
@ -268,9 +270,12 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
if (routerQueryParams.hasOwnProperty('pageSize')) { if (routerQueryParams.hasOwnProperty('pageSize')) {
this.pageLink.pageSize = Number(routerQueryParams.pageSize); this.pageLink.pageSize = Number(routerQueryParams.pageSize);
} }
if (routerQueryParams.hasOwnProperty('textSearch') && !isEmptyStr(routerQueryParams.textSearch)) { const textSearchParam = routerQueryParams.textSearch;
if (textSearchParam && !isEmptyStr(textSearchParam)) {
const decodedTextSearch = decodeURI(routerQueryParams.textSearch);
this.textSearchMode = true; this.textSearchMode = true;
this.pageLink.textSearch = decodeURI(routerQueryParams.textSearch); this.textSearch.setValue(decodedTextSearch, { emitEvent: false });
this.pageLink.textSearch = decodedTextSearch.trim();
} }
} }
this.dataSource = this.entitiesTableConfig.dataSource(this.dataLoaded.bind(this)); this.dataSource = this.entitiesTableConfig.dataSource(this.dataLoaded.bind(this));
@ -305,37 +310,39 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
ngAfterViewInit() { ngAfterViewInit() {
fromEvent(this.searchInputField.nativeElement, 'keyup') this.textSearch.valueChanges.pipe(
.pipe( debounceTime(150),
debounceTime(150), startWith(''),
distinctUntilChanged(), distinctUntilChanged((a: string, b: string) => a.trim() === b.trim()),
tap(() => { skip(1),
const queryParams: PageQueryParam = { takeUntil(this.destroy$)
textSearch: isString(this.pageLink.textSearch) && this.pageLink.textSearch !== '' ? encodeURI(this.pageLink.textSearch) : null ).subscribe(value => {
}; const queryParams: PageQueryParam = {};
if (this.displayPagination) { if (isNotEmptyStr(value)) {
this.paginator.pageIndex = 0; queryParams.textSearch = encodeURI(value);
queryParams.page = null; this.pageLink.textSearch = value.trim();
} } else {
this.updatedRouterParamsAndData(queryParams); queryParams.textSearch = null;
}) this.pageLink.textSearch = null;
) }
.subscribe(); if (this.displayPagination) {
this.paginator.pageIndex = 0;
queryParams.page = null;
}
this.updatedRouterParamsAndData(queryParams);
});
if (this.pageMode) { if (this.pageMode) {
this.route.queryParams.pipe(skip(1)).subscribe((params: PageQueryParam) => { this.route.queryParams.pipe(
skip(1),
takeUntil(this.destroy$)
).subscribe((params: PageQueryParam) => {
if (this.displayPagination) { if (this.displayPagination) {
this.paginator.pageIndex = Number(params.page) || 0; this.paginator.pageIndex = Number(params.page) || 0;
this.paginator.pageSize = Number(params.pageSize) || this.defaultPageSize; this.paginator.pageSize = Number(params.pageSize) || this.defaultPageSize;
} }
this.sort.active = params.property || this.entitiesTableConfig.defaultSortOrder.property; this.sort.active = params.property || this.entitiesTableConfig.defaultSortOrder.property;
this.sort.direction = (params.direction || this.entitiesTableConfig.defaultSortOrder.direction).toLowerCase() as SortDirection; this.sort.direction = (params.direction || this.entitiesTableConfig.defaultSortOrder.direction).toLowerCase() as SortDirection;
if (params.hasOwnProperty('textSearch') && !isEmptyStr(params.textSearch)) {
this.textSearchMode = true;
this.pageLink.textSearch = decodeURI(params.textSearch);
} else {
this.pageLink.textSearch = null;
}
this.updateData(); this.updateData();
}); });
} }
@ -376,10 +383,8 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
} }
this.updateDataSubscription = ((this.displayPagination ? merge(sortSubscription$, paginatorSubscription$) this.updateDataSubscription = ((this.displayPagination ? merge(sortSubscription$, paginatorSubscription$)
: sortSubscription$) as Observable<PageQueryParam>).pipe( : sortSubscription$) as Observable<PageQueryParam>).pipe(
tap((queryParams) => { takeUntil(this.destroy$)
this.updatedRouterParamsAndData(queryParams); ).subscribe(queryParams => this.updatedRouterParamsAndData(queryParams));
})
).subscribe();
} }
addEnabled() { addEnabled() {
@ -572,7 +577,6 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
enterFilterMode() { enterFilterMode() {
this.textSearchMode = true; this.textSearchMode = true;
this.pageLink.textSearch = '';
setTimeout(() => { setTimeout(() => {
this.searchInputField.nativeElement.focus(); this.searchInputField.nativeElement.focus();
this.searchInputField.nativeElement.setSelectionRange(0, 0); this.searchInputField.nativeElement.setSelectionRange(0, 0);
@ -581,19 +585,12 @@ export class EntitiesTableComponent extends PageComponent implements IEntitiesTa
exitFilterMode() { exitFilterMode() {
this.textSearchMode = false; this.textSearchMode = false;
this.pageLink.textSearch = null; this.textSearch.reset();
const queryParams: PageQueryParam = {
textSearch: null
};
if (this.displayPagination) {
this.paginator.pageIndex = 0;
queryParams.page = null;
}
this.updatedRouterParamsAndData(queryParams);
} }
resetSortAndFilter(update: boolean = true, preserveTimewindow: boolean = false) { resetSortAndFilter(update: boolean = true, preserveTimewindow: boolean = false) {
this.textSearchMode = false; this.textSearchMode = false;
this.textSearch.reset('', {emitEvent: false});
this.pageLink.textSearch = null; this.pageLink.textSearch = null;
if (this.entitiesTableConfig.useTimePageLink && !preserveTimewindow) { if (this.entitiesTableConfig.useTimePageLink && !preserveTimewindow) {
this.timewindow = this.entitiesTableConfig.defaultTimewindowInterval; this.timewindow = this.entitiesTableConfig.defaultTimewindowInterval;

2
ui-ngx/src/app/modules/home/components/relation/relation-table.component.html

@ -62,7 +62,7 @@
<mat-form-field fxFlex> <mat-form-field fxFlex>
<mat-label>&nbsp;</mat-label> <mat-label>&nbsp;</mat-label>
<input #searchInput matInput <input #searchInput matInput
[(ngModel)]="pageLink.textSearch" [formControl]="textSearch"
placeholder="{{ 'common.enter-search' | translate }}"/> placeholder="{{ 'common.enter-search' | translate }}"/>
</mat-form-field> </mat-form-field>
<button mat-icon-button (click)="exitFilterMode()" <button mat-icon-button (click)="exitFilterMode()"

56
ui-ngx/src/app/modules/home/components/relation/relation-table.component.ts

@ -21,6 +21,7 @@ import {
Component, Component,
ElementRef, ElementRef,
Input, Input,
OnDestroy,
OnInit, OnInit,
ViewChild ViewChild
} from '@angular/core'; } from '@angular/core';
@ -35,8 +36,8 @@ import { MatDialog } from '@angular/material/dialog';
import { DialogService } from '@core/services/dialog.service'; import { DialogService } from '@core/services/dialog.service';
import { EntityRelationService } from '@core/http/entity-relation.service'; import { EntityRelationService } from '@core/http/entity-relation.service';
import { Direction, SortOrder } from '@shared/models/page/sort-order'; import { Direction, SortOrder } from '@shared/models/page/sort-order';
import { forkJoin, fromEvent, merge, Observable } from 'rxjs'; import { forkJoin, merge, Observable, Subject } from 'rxjs';
import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators'; import { debounceTime, distinctUntilChanged, skip, startWith, takeUntil } from 'rxjs/operators';
import { import {
EntityRelation, EntityRelation,
EntityRelationInfo, EntityRelationInfo,
@ -49,6 +50,7 @@ import { RelationsDatasource } from '../../models/datasource/relation-datasource
import { RelationDialogComponent, RelationDialogData } from '@home/components/relation/relation-dialog.component'; import { RelationDialogComponent, RelationDialogData } from '@home/components/relation/relation-dialog.component';
import { hidePageSizePixelValue } from '@shared/models/constants'; import { hidePageSizePixelValue } from '@shared/models/constants';
import { ResizeObserver } from '@juggle/resize-observer'; import { ResizeObserver } from '@juggle/resize-observer';
import { FormBuilder } from '@angular/forms';
@Component({ @Component({
selector: 'tb-relation-table', selector: 'tb-relation-table',
@ -56,7 +58,7 @@ import { ResizeObserver } from '@juggle/resize-observer';
styleUrls: ['./relation-table.component.scss'], styleUrls: ['./relation-table.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class RelationTableComponent extends PageComponent implements AfterViewInit, OnInit { export class RelationTableComponent extends PageComponent implements AfterViewInit, OnInit, OnDestroy {
directions = EntitySearchDirection; directions = EntitySearchDirection;
@ -77,8 +79,6 @@ export class RelationTableComponent extends PageComponent implements AfterViewIn
viewsInited = false; viewsInited = false;
private widgetResize$: ResizeObserver;
@Input() @Input()
set active(active: boolean) { set active(active: boolean) {
if (this.activeValue !== active) { if (this.activeValue !== active) {
@ -110,13 +110,19 @@ export class RelationTableComponent extends PageComponent implements AfterViewIn
@ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort; @ViewChild(MatSort) sort: MatSort;
textSearch = this.fb.control('', {nonNullable: true});
private widgetResize$: ResizeObserver;
private destroy$ = new Subject<void>();
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private entityRelationService: EntityRelationService, private entityRelationService: EntityRelationService,
public translate: TranslateService, public translate: TranslateService,
public dialog: MatDialog, public dialog: MatDialog,
private dialogService: DialogService, private dialogService: DialogService,
private cd: ChangeDetectorRef, private cd: ChangeDetectorRef,
private elementRef: ElementRef) { private elementRef: ElementRef,
private fb: FormBuilder) {
super(store); super(store);
this.dirtyValue = !this.activeValue; this.dirtyValue = !this.activeValue;
const sortOrder: SortOrder = { property: 'type', direction: Direction.ASC }; const sortOrder: SortOrder = { property: 'type', direction: Direction.ASC };
@ -141,6 +147,8 @@ export class RelationTableComponent extends PageComponent implements AfterViewIn
if (this.widgetResize$) { if (this.widgetResize$) {
this.widgetResize$.disconnect(); this.widgetResize$.disconnect();
} }
this.destroy$.next();
this.destroy$.complete();
} }
updateColumns() { updateColumns() {
@ -159,25 +167,23 @@ export class RelationTableComponent extends PageComponent implements AfterViewIn
} }
ngAfterViewInit() { ngAfterViewInit() {
this.textSearch.valueChanges.pipe(
fromEvent(this.searchInputField.nativeElement, 'keyup') debounceTime(150),
.pipe( startWith(''),
debounceTime(150), distinctUntilChanged((a: string, b: string) => a.trim() === b.trim()),
distinctUntilChanged(), skip(1),
tap(() => { takeUntil(this.destroy$)
this.paginator.pageIndex = 0; ).subscribe((value) => {
this.updateData(); this.paginator.pageIndex = 0;
}) this.pageLink.textSearch = value.trim();
) this.updateData();
.subscribe(); });
this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0); this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0);
merge(this.sort.sortChange, this.paginator.page) merge(this.sort.sortChange, this.paginator.page).pipe(
.pipe( takeUntil(this.destroy$)
tap(() => this.updateData()) ).subscribe(() => this.updateData());
)
.subscribe();
this.viewsInited = true; this.viewsInited = true;
if (this.activeValue && this.entityIdValue) { if (this.activeValue && this.entityIdValue) {
@ -195,7 +201,6 @@ export class RelationTableComponent extends PageComponent implements AfterViewIn
enterFilterMode() { enterFilterMode() {
this.textSearchMode = true; this.textSearchMode = true;
this.pageLink.textSearch = '';
setTimeout(() => { setTimeout(() => {
this.searchInputField.nativeElement.focus(); this.searchInputField.nativeElement.focus();
this.searchInputField.nativeElement.setSelectionRange(0, 0); this.searchInputField.nativeElement.setSelectionRange(0, 0);
@ -204,14 +209,13 @@ export class RelationTableComponent extends PageComponent implements AfterViewIn
exitFilterMode() { exitFilterMode() {
this.textSearchMode = false; this.textSearchMode = false;
this.pageLink.textSearch = null; this.textSearch.reset();
this.paginator.pageIndex = 0;
this.updateData();
} }
resetSortAndFilter(update: boolean = true) { resetSortAndFilter(update: boolean = true) {
this.direction = EntitySearchDirection.FROM; this.direction = EntitySearchDirection.FROM;
this.updateColumns(); this.updateColumns();
this.textSearch.reset('', {emitEvent: false});
this.pageLink.textSearch = null; this.pageLink.textSearch = null;
this.paginator.pageIndex = 0; this.paginator.pageIndex = 0;
const sortable = this.sort.sortables.get('type'); const sortable = this.sort.sortables.get('type');

2
ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.html

@ -74,7 +74,7 @@
<mat-form-field fxFlex> <mat-form-field fxFlex>
<mat-label>&nbsp;</mat-label> <mat-label>&nbsp;</mat-label>
<input #searchInput matInput <input #searchInput matInput
[(ngModel)]="pageLink.textSearch" [formControl]="textSearch"
placeholder="{{ 'version-control.search' | translate }}"/> placeholder="{{ 'version-control.search' | translate }}"/>
</mat-form-field> </mat-form-field>
<button mat-icon-button (click)="exitFilterMode()" <button mat-icon-button (click)="exitFilterMode()"

50
ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.ts

@ -33,10 +33,10 @@ import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { EntityId, entityIdEquals } from '@shared/models/id/entity-id'; import { EntityId, entityIdEquals } from '@shared/models/id/entity-id';
import { CollectionViewer, DataSource } from '@angular/cdk/collections'; import { CollectionViewer, DataSource } from '@angular/cdk/collections';
import { BehaviorSubject, fromEvent, merge, Observable, of, ReplaySubject } from 'rxjs'; import { BehaviorSubject, merge, Observable, of, ReplaySubject, Subject } from 'rxjs';
import { emptyPageData, PageData } from '@shared/models/page/page-data'; import { emptyPageData, PageData } from '@shared/models/page/page-data';
import { PageLink } from '@shared/models/page/page-link'; import { PageLink } from '@shared/models/page/page-link';
import { catchError, debounceTime, distinctUntilChanged, map, tap } from 'rxjs/operators'; import { catchError, debounceTime, distinctUntilChanged, map, skip, startWith, takeUntil } from 'rxjs/operators';
import { EntityVersion, VersionCreationResult, VersionLoadResult } from '@shared/models/vc.models'; import { EntityVersion, VersionCreationResult, VersionLoadResult } from '@shared/models/vc.models';
import { EntitiesVersionControlService } from '@core/http/entities-version-control.service'; import { EntitiesVersionControlService } from '@core/http/entities-version-control.service';
import { MatPaginator } from '@angular/material/paginator'; import { MatPaginator } from '@angular/material/paginator';
@ -54,7 +54,8 @@ import { EntityVersionDiffComponent } from '@home/components/vc/entity-version-d
import { ComplexVersionCreateComponent } from '@home/components/vc/complex-version-create.component'; import { ComplexVersionCreateComponent } from '@home/components/vc/complex-version-create.component';
import { ComplexVersionLoadComponent } from '@home/components/vc/complex-version-load.component'; import { ComplexVersionLoadComponent } from '@home/components/vc/complex-version-load.component';
import { TbPopoverComponent } from '@shared/components/popover.component'; import { TbPopoverComponent } from '@shared/components/popover.component';
import { AdminService } from "@core/http/admin.service"; import { AdminService } from '@core/http/admin.service';
import { FormBuilder } from '@angular/forms';
@Component({ @Component({
selector: 'tb-entity-versions-table', selector: 'tb-entity-versions-table',
@ -90,7 +91,10 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni
isReadOnly: Observable<boolean>; isReadOnly: Observable<boolean>;
textSearch = this.fb.control('', {nonNullable: true});
private componentResize$: ResizeObserver; private componentResize$: ResizeObserver;
private destroy$ = new Subject<void>();
@Input() @Input()
set active(active: boolean) { set active(active: boolean) {
@ -137,7 +141,8 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni
private renderer: Renderer2, private renderer: Renderer2,
private cd: ChangeDetectorRef, private cd: ChangeDetectorRef,
private viewContainerRef: ViewContainerRef, private viewContainerRef: ViewContainerRef,
private elementRef: ElementRef) { private elementRef: ElementRef,
private fb: FormBuilder) {
super(store); super(store);
this.dirtyValue = !this.activeValue; this.dirtyValue = !this.activeValue;
const sortOrder: SortOrder = { property: 'timestamp', direction: Direction.DESC }; const sortOrder: SortOrder = { property: 'timestamp', direction: Direction.DESC };
@ -161,6 +166,8 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni
if (this.componentResize$) { if (this.componentResize$) {
this.componentResize$.disconnect(); this.componentResize$.disconnect();
} }
this.destroy$.next();
this.destroy$.complete();
} }
branchChanged(newBranch: string) { branchChanged(newBranch: string) {
@ -174,23 +181,22 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni
} }
ngAfterViewInit() { ngAfterViewInit() {
fromEvent(this.searchInputField.nativeElement, 'keyup') this.textSearch.valueChanges.pipe(
.pipe( debounceTime(400),
debounceTime(400), startWith(''),
distinctUntilChanged(), distinctUntilChanged((a: string, b: string) => a.trim() === b.trim()),
tap(() => { skip(1),
this.paginator.pageIndex = 0; takeUntil(this.destroy$)
this.updateData(); ).subscribe((value) => {
}) this.paginator.pageIndex = 0;
) this.pageLink.textSearch = value.trim();
.subscribe(); this.updateData();
});
this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0); this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0);
merge(this.sort.sortChange, this.paginator.page) merge(this.sort.sortChange, this.paginator.page).pipe(
.pipe( takeUntil(this.destroy$)
tap(() => this.updateData()) ).subscribe(() => this.updateData());
)
.subscribe();
this.viewsInited = true; this.viewsInited = true;
if (!this.singleEntityMode || (this.activeValue && this.externalEntityIdValue)) { if (!this.singleEntityMode || (this.activeValue && this.externalEntityIdValue)) {
this.initFromDefaultBranch(); this.initFromDefaultBranch();
@ -341,7 +347,6 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni
enterFilterMode() { enterFilterMode() {
this.textSearchMode = true; this.textSearchMode = true;
this.pageLink.textSearch = '';
setTimeout(() => { setTimeout(() => {
this.searchInputField.nativeElement.focus(); this.searchInputField.nativeElement.focus();
this.searchInputField.nativeElement.setSelectionRange(0, 0); this.searchInputField.nativeElement.setSelectionRange(0, 0);
@ -350,9 +355,7 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni
exitFilterMode() { exitFilterMode() {
this.textSearchMode = false; this.textSearchMode = false;
this.pageLink.textSearch = null; this.textSearch.reset();
this.paginator.pageIndex = 0;
this.updateData();
} }
private initFromDefaultBranch() { private initFromDefaultBranch() {
@ -376,6 +379,7 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni
private resetSortAndFilter(update: boolean) { private resetSortAndFilter(update: boolean) {
this.textSearchMode = false; this.textSearchMode = false;
this.textSearch.reset('', {emitEvent: false});
this.pageLink.textSearch = null; this.pageLink.textSearch = null;
if (this.viewsInited) { if (this.viewsInited) {
this.paginator.pageIndex = 0; this.paginator.pageIndex = 0;

2
ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.html

@ -27,7 +27,7 @@
<mat-form-field fxFlex> <mat-form-field fxFlex>
<mat-label>&nbsp;</mat-label> <mat-label>&nbsp;</mat-label>
<input #searchInput matInput <input #searchInput matInput
[(ngModel)]="pageLink.textSearch" [formControl]="textSearch"
placeholder="{{ 'alarm.search' | translate }}"/> placeholder="{{ 'alarm.search' | translate }}"/>
</mat-form-field> </mat-form-field>
<button mat-icon-button (click)="exitFilterMode()" <button mat-icon-button (click)="exitFilterMode()"

49
ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts

@ -42,9 +42,9 @@ import cssjs from '@core/css/css';
import { sortItems } from '@shared/models/page/page-link'; import { sortItems } from '@shared/models/page/page-link';
import { Direction } from '@shared/models/page/sort-order'; import { Direction } from '@shared/models/page/sort-order';
import { CollectionViewer, DataSource, SelectionModel } from '@angular/cdk/collections'; import { CollectionViewer, DataSource, SelectionModel } from '@angular/cdk/collections';
import { BehaviorSubject, forkJoin, fromEvent, merge, Observable, Subscription } from 'rxjs'; import { BehaviorSubject, forkJoin, merge, Observable, Subject, Subscription } from 'rxjs';
import { emptyPageData, PageData } from '@shared/models/page/page-data'; import { emptyPageData, PageData } from '@shared/models/page/page-data';
import { debounceTime, distinctUntilChanged, map, take, tap } from 'rxjs/operators'; import { debounceTime, distinctUntilChanged, map, skip, startWith, take, takeUntil, tap } from 'rxjs/operators';
import { MatPaginator } from '@angular/material/paginator'; import { MatPaginator } from '@angular/material/paginator';
import { MatSort, SortDirection } from '@angular/material/sort'; import { MatSort, SortDirection } from '@angular/material/sort';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
@ -122,6 +122,7 @@ import {
AlarmFilterConfigData AlarmFilterConfigData
} from '@home/components/alarm/alarm-filter-config.component'; } from '@home/components/alarm/alarm-filter-config.component';
import { getCurrentAuthUser } from '@core/auth/auth.selectors'; import { getCurrentAuthUser } from '@core/auth/auth.selectors';
import { FormBuilder } from '@angular/forms';
interface AlarmsTableWidgetSettings extends TableWidgetSettings { interface AlarmsTableWidgetSettings extends TableWidgetSettings {
alarmsTitle: string; alarmsTitle: string;
@ -159,6 +160,8 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
@ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort; @ViewChild(MatSort) sort: MatSort;
textSearch = this.fb.control('', {nonNullable: true});
public enableSelection = true; public enableSelection = true;
public displayPagination = true; public displayPagination = true;
public enableStickyHeader = true; public enableStickyHeader = true;
@ -184,6 +187,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
private widgetConfig: WidgetConfig; private widgetConfig: WidgetConfig;
private subscription: IWidgetSubscription; private subscription: IWidgetSubscription;
private widgetResize$: ResizeObserver; private widgetResize$: ResizeObserver;
private destroy$ = new Subject<void>();
private displayActivity = false; private displayActivity = false;
private displayDetails = true; private displayDetails = true;
@ -244,7 +248,8 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
private dialogService: DialogService, private dialogService: DialogService,
private entityService: EntityService, private entityService: EntityService,
private alarmService: AlarmService, private alarmService: AlarmService,
private cd: ChangeDetectorRef) { private cd: ChangeDetectorRef,
private fb: FormBuilder) {
super(store); super(store);
this.pageLink = { this.pageLink = {
page: 0, page: 0,
@ -285,28 +290,29 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
if (this.widgetResize$) { if (this.widgetResize$) {
this.widgetResize$.disconnect(); this.widgetResize$.disconnect();
} }
this.destroy$.next();
this.destroy$.complete();
} }
ngAfterViewInit(): void { ngAfterViewInit(): void {
fromEvent(this.searchInputField.nativeElement, 'keyup') this.textSearch.valueChanges.pipe(
.pipe( debounceTime(150),
debounceTime(150), startWith(''),
distinctUntilChanged(), distinctUntilChanged((a: string, b: string) => a.trim() === b.trim()),
tap(() => { skip(1),
this.resetPageIndex(); takeUntil(this.destroy$)
this.updateData(); ).subscribe((value) => {
}) this.resetPageIndex();
) this.pageLink.textSearch = value.trim();
.subscribe(); this.updateData();
});
if (this.displayPagination) { if (this.displayPagination) {
this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0); this.sort.sortChange.pipe(takeUntil(this.destroy$)).subscribe(() => this.paginator.pageIndex = 0);
} }
((this.displayPagination ? merge(this.sort.sortChange, this.paginator.page) : this.sort.sortChange) as Observable<any>) ((this.displayPagination ? merge(this.sort.sortChange, this.paginator.page) : this.sort.sortChange) as Observable<any>).pipe(
.pipe( takeUntil(this.destroy$)
tap(() => this.updateData()) ).subscribe(() => this.updateData());
)
.subscribe();
this.updateData(); this.updateData();
} }
@ -627,7 +633,6 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
private enterFilterMode() { private enterFilterMode() {
this.textSearchMode = true; this.textSearchMode = true;
this.pageLink.textSearch = '';
this.ctx.hideTitlePanel = true; this.ctx.hideTitlePanel = true;
this.ctx.detectChanges(true); this.ctx.detectChanges(true);
setTimeout(() => { setTimeout(() => {
@ -638,9 +643,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
exitFilterMode() { exitFilterMode() {
this.textSearchMode = false; this.textSearchMode = false;
this.pageLink.textSearch = null; this.textSearch.reset();
this.resetPageIndex();
this.updateData();
this.ctx.hideTitlePanel = false; this.ctx.hideTitlePanel = false;
this.ctx.detectChanges(true); this.ctx.detectChanges(true);
} }

2
ui-ngx/src/app/modules/home/components/widget/lib/entities-hierarchy-widget.component.html

@ -27,7 +27,7 @@
<mat-form-field fxFlex> <mat-form-field fxFlex>
<mat-label>&nbsp;</mat-label> <mat-label>&nbsp;</mat-label>
<input #searchInput matInput <input #searchInput matInput
[(ngModel)]="textSearch" [formControl]="textSearch"
placeholder="{{ 'entity.search' | translate }}"/> placeholder="{{ 'entity.search' | translate }}"/>
</mat-form-field> </mat-form-field>
<button mat-icon-button (click)="exitFilterMode()" <button mat-icon-button (click)="exitFilterMode()"

70
ui-ngx/src/app/modules/home/components/widget/lib/entities-hierarchy-widget.component.ts

@ -14,7 +14,16 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild, ViewContainerRef } from '@angular/core'; import {
AfterViewInit,
Component,
ElementRef,
Input,
OnDestroy,
OnInit,
ViewChild,
ViewContainerRef
} from '@angular/core';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
@ -23,8 +32,7 @@ import { DatasourceData, DatasourceType, WidgetConfig, widgetType } from '@share
import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget-api.models'; import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget-api.models';
import { UtilsService } from '@core/services/utils.service'; import { UtilsService } from '@core/services/utils.service';
import cssjs from '@core/css/css'; import cssjs from '@core/css/css';
import { fromEvent } from 'rxjs'; import { debounceTime, distinctUntilChanged, skip, startWith, takeUntil } from 'rxjs/operators';
import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators';
import { constructTableCssString } from '@home/components/widget/lib/table-widget.models'; import { constructTableCssString } from '@home/components/widget/lib/table-widget.models';
import { Overlay } from '@angular/cdk/overlay'; import { Overlay } from '@angular/cdk/overlay';
import { import {
@ -36,7 +44,7 @@ import {
NodesInsertedCallback NodesInsertedCallback
} from '@shared/components/nav-tree.component'; } from '@shared/components/nav-tree.component';
import { EntityType } from '@shared/models/entity-type.models'; import { EntityType } from '@shared/models/entity-type.models';
import { deepClone, hashCode } from '@core/utils'; import { deepClone, hashCode, isDefinedAndNotNull, isEmptyStr } from '@core/utils';
import { import {
defaultNodeIconFunction, defaultNodeIconFunction,
defaultNodeOpenedFunction, defaultNodeOpenedFunction,
@ -60,13 +68,15 @@ import {
import { EntityRelationsQuery } from '@shared/models/relation.models'; import { EntityRelationsQuery } from '@shared/models/relation.models';
import { AliasFilterType, RelationsQueryFilter } from '@shared/models/alias.models'; import { AliasFilterType, RelationsQueryFilter } from '@shared/models/alias.models';
import { EntityFilter } from '@shared/models/query/query.models'; import { EntityFilter } from '@shared/models/query/query.models';
import { FormBuilder } from '@angular/forms';
import { Subject } from 'rxjs';
@Component({ @Component({
selector: 'tb-entities-hierarchy-widget', selector: 'tb-entities-hierarchy-widget',
templateUrl: './entities-hierarchy-widget.component.html', templateUrl: './entities-hierarchy-widget.component.html',
styleUrls: ['./entities-hierarchy-widget.component.scss'] styleUrls: ['./entities-hierarchy-widget.component.scss']
}) })
export class EntitiesHierarchyWidgetComponent extends PageComponent implements OnInit, AfterViewInit { export class EntitiesHierarchyWidgetComponent extends PageComponent implements OnInit, AfterViewInit, OnDestroy {
@Input() @Input()
ctx: WidgetContext; ctx: WidgetContext;
@ -75,8 +85,8 @@ export class EntitiesHierarchyWidgetComponent extends PageComponent implements O
public toastTargetId = 'entities-hierarchy-' + this.utils.guid(); public toastTargetId = 'entities-hierarchy-' + this.utils.guid();
public textSearchMode = false; textSearchMode = false;
public textSearch = null; textSearch = this.fb.control('', {nonNullable: true});
public nodeEditCallbacks: NavTreeEditCallbacks = {}; public nodeEditCallbacks: NavTreeEditCallbacks = {};
@ -106,11 +116,14 @@ export class EntitiesHierarchyWidgetComponent extends PageComponent implements O
} }
}; };
private destroy$ = new Subject<void>();
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private elementRef: ElementRef, private elementRef: ElementRef,
private overlay: Overlay, private overlay: Overlay,
private viewContainerRef: ViewContainerRef, private viewContainerRef: ViewContainerRef,
private utils: UtilsService) { private utils: UtilsService,
private fb: FormBuilder) {
super(store); super(store);
} }
@ -125,15 +138,25 @@ export class EntitiesHierarchyWidgetComponent extends PageComponent implements O
} }
ngAfterViewInit(): void { ngAfterViewInit(): void {
fromEvent(this.searchInputField.nativeElement, 'keyup') this.textSearch.valueChanges.pipe(
.pipe( debounceTime(150),
debounceTime(150), startWith(''),
distinctUntilChanged(), distinctUntilChanged((a: string, b: string) => a.trim() === b.trim()),
tap(() => { skip(1),
this.updateSearchNodes(); takeUntil(this.destroy$)
}) ).subscribe((value) => {
) if (isDefinedAndNotNull(value) && !isEmptyStr(value)) {
.subscribe(); this.nodeEditCallbacks.search(value.trim());
} else {
this.nodeEditCallbacks.clearSearch();
}
});
}
ngOnDestroy() {
super.ngOnDestroy();
this.destroy$.next();
this.destroy$.complete();
} }
public onDataUpdated() { public onDataUpdated() {
@ -190,7 +213,6 @@ export class EntitiesHierarchyWidgetComponent extends PageComponent implements O
private enterFilterMode() { private enterFilterMode() {
this.textSearchMode = true; this.textSearchMode = true;
this.textSearch = '';
this.ctx.hideTitlePanel = true; this.ctx.hideTitlePanel = true;
this.ctx.detectChanges(true); this.ctx.detectChanges(true);
setTimeout(() => { setTimeout(() => {
@ -201,20 +223,12 @@ export class EntitiesHierarchyWidgetComponent extends PageComponent implements O
exitFilterMode() { exitFilterMode() {
this.textSearchMode = false; this.textSearchMode = false;
this.textSearch = null; this.textSearch.reset();
this.updateSearchNodes(); this.nodeEditCallbacks.clearSearch();
this.ctx.hideTitlePanel = false; this.ctx.hideTitlePanel = false;
this.ctx.detectChanges(true); this.ctx.detectChanges(true);
} }
private updateSearchNodes() {
if (this.textSearch != null) {
this.nodeEditCallbacks.search(this.textSearch);
} else {
this.nodeEditCallbacks.clearSearch();
}
}
private updateNodeData(subscriptionData: Array<DatasourceData>) { private updateNodeData(subscriptionData: Array<DatasourceData>) {
const affectedNodes: string[] = []; const affectedNodes: string[] = [];
if (subscriptionData) { if (subscriptionData) {

2
ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html

@ -27,7 +27,7 @@
<mat-form-field fxFlex> <mat-form-field fxFlex>
<mat-label>&nbsp;</mat-label> <mat-label>&nbsp;</mat-label>
<input #searchInput matInput <input #searchInput matInput
[(ngModel)]="pageLink.textSearch" [formControl]="textSearch"
placeholder="{{ 'entity.search' | translate }}"/> placeholder="{{ 'entity.search' | translate }}"/>
</mat-form-field> </mat-form-field>
<button mat-icon-button (click)="exitFilterMode()" <button mat-icon-button (click)="exitFilterMode()"

55
ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts

@ -46,11 +46,11 @@ import { deepClone, hashCode, isDefined, isNumber, isObject, isUndefined } from
import cssjs from '@core/css/css'; import cssjs from '@core/css/css';
import { CollectionViewer, DataSource } from '@angular/cdk/collections'; import { CollectionViewer, DataSource } from '@angular/cdk/collections';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
import { BehaviorSubject, fromEvent, merge, Observable } from 'rxjs'; import { BehaviorSubject, merge, Observable, Subject } from 'rxjs';
import { emptyPageData, PageData } from '@shared/models/page/page-data'; import { emptyPageData, PageData } from '@shared/models/page/page-data';
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { entityTypeTranslations } from '@shared/models/entity-type.models'; import { entityTypeTranslations } from '@shared/models/entity-type.models';
import { debounceTime, distinctUntilChanged, map, tap } from 'rxjs/operators'; import { debounceTime, distinctUntilChanged, map, skip, startWith, takeUntil } from 'rxjs/operators';
import { MatPaginator } from '@angular/material/paginator'; import { MatPaginator } from '@angular/material/paginator';
import { MatSort, SortDirection } from '@angular/material/sort'; import { MatSort, SortDirection } from '@angular/material/sort';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
@ -105,6 +105,7 @@ import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { ResizeObserver } from '@juggle/resize-observer'; import { ResizeObserver } from '@juggle/resize-observer';
import { hidePageSizePixelValue } from '@shared/models/constants'; import { hidePageSizePixelValue } from '@shared/models/constants';
import { AggregationType } from '@shared/models/time/time.models'; import { AggregationType } from '@shared/models/time/time.models';
import { FormBuilder } from '@angular/forms';
interface EntitiesTableWidgetSettings extends TableWidgetSettings { interface EntitiesTableWidgetSettings extends TableWidgetSettings {
entitiesTitle: string; entitiesTitle: string;
@ -131,6 +132,8 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
@ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatPaginator) paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort; @ViewChild(MatSort) sort: MatSort;
textSearch = this.fb.control('', {nonNullable: true});
public displayPagination = true; public displayPagination = true;
public enableStickyHeader = true; public enableStickyHeader = true;
public enableStickyAction = true; public enableStickyAction = true;
@ -155,6 +158,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
private widgetConfig: WidgetConfig; private widgetConfig: WidgetConfig;
private subscription: IWidgetSubscription; private subscription: IWidgetSubscription;
private widgetResize$: ResizeObserver; private widgetResize$: ResizeObserver;
private destroy$ = new Subject<void>();
private defaultPageSize = 10; private defaultPageSize = 10;
private defaultSortOrder = 'entityName'; private defaultSortOrder = 'entityName';
@ -194,7 +198,8 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
private datePipe: DatePipe, private datePipe: DatePipe,
private translate: TranslateService, private translate: TranslateService,
private domSanitizer: DomSanitizer, private domSanitizer: DomSanitizer,
private cd: ChangeDetectorRef) { private cd: ChangeDetectorRef,
private fb: FormBuilder) {
super(store); super(store);
this.pageLink = { this.pageLink = {
page: 0, page: 0,
@ -228,30 +233,31 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
if (this.widgetResize$) { if (this.widgetResize$) {
this.widgetResize$.disconnect(); this.widgetResize$.disconnect();
} }
this.destroy$.next();
this.destroy$.complete();
} }
ngAfterViewInit(): void { ngAfterViewInit(): void {
fromEvent(this.searchInputField.nativeElement, 'keyup') this.textSearch.valueChanges.pipe(
.pipe( debounceTime(150),
debounceTime(150), startWith(''),
distinctUntilChanged(), distinctUntilChanged((a: string, b: string) => a.trim() === b.trim()),
tap(() => { skip(1),
if (this.displayPagination) { takeUntil(this.destroy$)
this.paginator.pageIndex = 0; ).subscribe((value) => {
} if (this.displayPagination) {
this.updateData(); this.paginator.pageIndex = 0;
}) }
) this.pageLink.textSearch = value.trim();
.subscribe(); this.updateData();
});
if (this.displayPagination) { if (this.displayPagination) {
this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0); this.sort.sortChange.pipe(takeUntil(this.destroy$)).subscribe(() => this.paginator.pageIndex = 0);
} }
((this.displayPagination ? merge(this.sort.sortChange, this.paginator.page) : this.sort.sortChange) as Observable<any>) ((this.displayPagination ? merge(this.sort.sortChange, this.paginator.page) : this.sort.sortChange) as Observable<any>).pipe(
.pipe( takeUntil(this.destroy$)
tap(() => this.updateData()) ).subscribe(() => this.updateData());
)
.subscribe();
this.updateData(); this.updateData();
} }
@ -510,7 +516,6 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
private enterFilterMode() { private enterFilterMode() {
this.textSearchMode = true; this.textSearchMode = true;
this.pageLink.textSearch = '';
this.ctx.hideTitlePanel = true; this.ctx.hideTitlePanel = true;
this.ctx.detectChanges(true); this.ctx.detectChanges(true);
setTimeout(() => { setTimeout(() => {
@ -521,11 +526,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
exitFilterMode() { exitFilterMode() {
this.textSearchMode = false; this.textSearchMode = false;
this.pageLink.textSearch = null; this.textSearch.reset();
if (this.displayPagination) {
this.paginator.pageIndex = 0;
}
this.updateData();
this.ctx.hideTitlePanel = false; this.ctx.hideTitlePanel = false;
this.ctx.detectChanges(true); this.ctx.detectChanges(true);
} }

2
ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.html

@ -27,7 +27,7 @@
<mat-form-field fxFlex> <mat-form-field fxFlex>
<mat-label>&nbsp;</mat-label> <mat-label>&nbsp;</mat-label>
<input #searchInput matInput <input #searchInput matInput
[(ngModel)]="textSearch" [formControl]="textSearch"
placeholder="{{ 'widget.search-data' | translate }}"/> placeholder="{{ 'widget.search-data' | translate }}"/>
</mat-form-field> </mat-form-field>
<button mat-icon-button (click)="exitFilterMode()" <button mat-icon-button (click)="exitFilterMode()"

66
ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts

@ -56,9 +56,9 @@ import cssjs from '@core/css/css';
import { PageLink } from '@shared/models/page/page-link'; import { PageLink } from '@shared/models/page/page-link';
import { Direction, SortOrder, sortOrderFromString } from '@shared/models/page/sort-order'; import { Direction, SortOrder, sortOrderFromString } from '@shared/models/page/sort-order';
import { CollectionViewer, DataSource } from '@angular/cdk/collections'; import { CollectionViewer, DataSource } from '@angular/cdk/collections';
import { BehaviorSubject, fromEvent, merge, Observable, of, Subscription } from 'rxjs'; import { BehaviorSubject, merge, Observable, of, Subject, Subscription } from 'rxjs';
import { emptyPageData, PageData } from '@shared/models/page/page-data'; import { emptyPageData, PageData } from '@shared/models/page/page-data';
import { catchError, debounceTime, distinctUntilChanged, map, tap } from 'rxjs/operators'; import { catchError, debounceTime, distinctUntilChanged, map, skip, startWith, takeUntil } from 'rxjs/operators';
import { MatPaginator } from '@angular/material/paginator'; import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort'; import { MatSort } from '@angular/material/sort';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
@ -69,7 +69,9 @@ import {
constructTableCssString, constructTableCssString,
DisplayColumn, DisplayColumn,
getCellContentInfo, getCellContentInfo,
getCellStyleInfo, getColumnDefaultVisibility, getColumnSelectionAvailability, getCellStyleInfo,
getColumnDefaultVisibility,
getColumnSelectionAvailability,
getRowStyleInfo, getRowStyleInfo,
getTableCellButtonActions, getTableCellButtonActions,
noDataMessage, noDataMessage,
@ -90,6 +92,7 @@ import {
DisplayColumnsPanelComponent DisplayColumnsPanelComponent
} from '@home/components/widget/lib/display-columns-panel.component'; } from '@home/components/widget/lib/display-columns-panel.component';
import { ComponentPortal } from '@angular/cdk/portal'; import { ComponentPortal } from '@angular/cdk/portal';
import { FormBuilder } from '@angular/forms';
export interface TimeseriesTableWidgetSettings extends TableWidgetSettings { export interface TimeseriesTableWidgetSettings extends TableWidgetSettings {
showTimestamp: boolean; showTimestamp: boolean;
@ -151,6 +154,8 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
@ViewChildren(MatPaginator) paginators: QueryList<MatPaginator>; @ViewChildren(MatPaginator) paginators: QueryList<MatPaginator>;
@ViewChildren(MatSort) sorts: QueryList<MatSort>; @ViewChildren(MatSort) sorts: QueryList<MatSort>;
textSearch = this.fb.control('', {nonNullable: true});
public displayPagination = true; public displayPagination = true;
public enableStickyHeader = true; public enableStickyHeader = true;
public enableStickyAction = true; public enableStickyAction = true;
@ -158,7 +163,6 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
public pageSizeOptions; public pageSizeOptions;
public textSearchMode = false; public textSearchMode = false;
public hidePageSize = false; public hidePageSize = false;
public textSearch: string = null;
public sources: TimeseriesTableSource[]; public sources: TimeseriesTableSource[];
public sourceIndex: number; public sourceIndex: number;
public noDataDisplayMessageText: string; public noDataDisplayMessageText: string;
@ -189,6 +193,7 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
private subscriptions: Subscription[] = []; private subscriptions: Subscription[] = [];
private widgetTimewindowChanged$: Subscription; private widgetTimewindowChanged$: Subscription;
private widgetResize$: ResizeObserver; private widgetResize$: ResizeObserver;
private destroy$ = new Subject<void>();
private searchAction: WidgetAction = { private searchAction: WidgetAction = {
name: 'action.search', name: 'action.search',
@ -216,7 +221,8 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
private translate: TranslateService, private translate: TranslateService,
private domSanitizer: DomSanitizer, private domSanitizer: DomSanitizer,
private datePipe: DatePipe, private datePipe: DatePipe,
private cd: ChangeDetectorRef) { private cd: ChangeDetectorRef,
private fb: FormBuilder) {
super(store); super(store);
} }
@ -262,24 +268,24 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
} }
ngAfterViewInit(): void { ngAfterViewInit(): void {
fromEvent(this.searchInputField.nativeElement, 'keyup') this.textSearch.valueChanges.pipe(
.pipe( debounceTime(150),
debounceTime(150), startWith(''),
distinctUntilChanged(), distinctUntilChanged((a: string, b: string) => a.trim() === b.trim()),
tap(() => { skip(1),
this.sources.forEach((source) => { takeUntil(this.destroy$)
source.pageLink.textSearch = this.textSearch; ).subscribe((textSearch) => {
if (this.displayPagination) { this.sources.forEach((source) => {
source.pageLink.page = 0; source.pageLink.textSearch = textSearch.trim();
} if (this.displayPagination) {
}); source.pageLink.page = 0;
this.loadCurrentSourceRow(); }
this.ctx.detectChanges(); });
}) this.loadCurrentSourceRow();
) this.ctx.detectChanges();
.subscribe(); });
this.sorts.changes.subscribe(() => { this.sorts.changes.pipe(takeUntil(this.destroy$)).subscribe(() => {
this.initSubscriptionsToSortAndPaginator(); this.initSubscriptionsToSortAndPaginator();
}); });
@ -559,9 +565,7 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
observables.push(paginator.page); observables.push(paginator.page);
} }
this.updateData(sort, paginator); this.updateData(sort, paginator);
this.subscriptions.push(merge(...observables).pipe( this.subscriptions.push(merge(...observables).subscribe(() => this.updateData(sort, paginator)));
tap(() => this.updateData(sort, paginator))
).subscribe());
}); });
} }
@ -573,10 +577,6 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
private enterFilterMode() { private enterFilterMode() {
this.textSearchMode = true; this.textSearchMode = true;
this.textSearch = '';
this.sources.forEach((source) => {
source.pageLink.textSearch = this.textSearch;
});
this.ctx.hideTitlePanel = true; this.ctx.hideTitlePanel = true;
this.ctx.detectChanges(true); this.ctx.detectChanges(true);
setTimeout(() => { setTimeout(() => {
@ -587,13 +587,7 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
exitFilterMode() { exitFilterMode() {
this.textSearchMode = false; this.textSearchMode = false;
this.textSearch = null; this.textSearch.reset();
this.sources.forEach((source) => {
source.pageLink.textSearch = this.textSearch;
if (this.displayPagination) {
source.pageLink.page = 0;
}
});
this.loadCurrentSourceRow(); this.loadCurrentSourceRow();
this.ctx.hideTitlePanel = false; this.ctx.hideTitlePanel = false;
this.ctx.detectChanges(true); this.ctx.detectChanges(true);

2
ui-ngx/src/app/modules/home/home.component.html

@ -57,7 +57,7 @@
<div [fxShow]="displaySearchMode()" fxFlex fxLayout="row" class="tb-dark"> <div [fxShow]="displaySearchMode()" fxFlex fxLayout="row" class="tb-dark">
<mat-form-field fxFlex class="tb-appearance-transparent"> <mat-form-field fxFlex class="tb-appearance-transparent">
<input #searchInput matInput <input #searchInput matInput
[(ngModel)]="searchText" [formControl]="textSearch"
placeholder="{{ 'common.enter-search' | translate }}"/> placeholder="{{ 'common.enter-search' | translate }}"/>
</mat-form-field> </mat-form-field>
</div> </div>

49
ui-ngx/src/app/modules/home/home.component.ts

@ -14,10 +14,10 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { AfterViewInit, Component, ElementRef, Inject, OnInit, ViewChild } from '@angular/core'; import { AfterViewInit, Component, ElementRef, Inject, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { fromEvent } from 'rxjs'; import { startWith, skip, Subject } from 'rxjs';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators'; import { debounceTime, distinctUntilChanged, takeUntil } from 'rxjs/operators';
import { BreakpointObserver, BreakpointState } from '@angular/cdk/layout'; import { BreakpointObserver, BreakpointState } from '@angular/cdk/layout';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
@ -31,13 +31,14 @@ import { WINDOW } from '@core/services/window.service';
import { instanceOfSearchableComponent, ISearchableComponent } from '@home/models/searchable-component.models'; import { instanceOfSearchableComponent, ISearchableComponent } from '@home/models/searchable-component.models';
import { ActiveComponentService } from '@core/services/active-component.service'; import { ActiveComponentService } from '@core/services/active-component.service';
import { RouterTabsComponent } from '@home/components/router-tabs.component'; import { RouterTabsComponent } from '@home/components/router-tabs.component';
import { FormBuilder } from '@angular/forms';
@Component({ @Component({
selector: 'tb-home', selector: 'tb-home',
templateUrl: './home.component.html', templateUrl: './home.component.html',
styleUrls: ['./home.component.scss'] styleUrls: ['./home.component.scss']
}) })
export class HomeComponent extends PageComponent implements AfterViewInit, OnInit { export class HomeComponent extends PageComponent implements AfterViewInit, OnInit, OnDestroy {
authState: AuthState = getCurrentAuthState(this.store); authState: AuthState = getCurrentAuthState(this.store);
@ -60,14 +61,17 @@ export class HomeComponent extends PageComponent implements AfterViewInit, OnIni
searchEnabled = false; searchEnabled = false;
showSearch = false; showSearch = false;
searchText = ''; textSearch = this.fb.control('', {nonNullable: true});
hideLoadingBar = false; hideLoadingBar = false;
private destroy$ = new Subject<void>();
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
@Inject(WINDOW) private window: Window, @Inject(WINDOW) private window: Window,
private activeComponentService: ActiveComponentService, private activeComponentService: ActiveComponentService,
public breakpointObserver: BreakpointObserver) { public breakpointObserver: BreakpointObserver,
private fb: FormBuilder) {
super(store); super(store);
} }
@ -79,6 +83,7 @@ export class HomeComponent extends PageComponent implements AfterViewInit, OnIni
this.breakpointObserver this.breakpointObserver
.observe(MediaBreakpoints['gt-sm']) .observe(MediaBreakpoints['gt-sm'])
.pipe(takeUntil(this.destroy$))
.subscribe((state: BreakpointState) => { .subscribe((state: BreakpointState) => {
if (state.matches) { if (state.matches) {
this.sidenavMode = 'side'; this.sidenavMode = 'side';
@ -91,16 +96,19 @@ export class HomeComponent extends PageComponent implements AfterViewInit, OnIni
); );
} }
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
ngAfterViewInit() { ngAfterViewInit() {
fromEvent(this.searchInputField.nativeElement, 'keyup') this.textSearch.valueChanges.pipe(
.pipe( debounceTime(150),
debounceTime(150), startWith(''),
distinctUntilChanged(), distinctUntilChanged((a: string, b: string) => a.trim() === b.trim()),
tap(() => { skip(1),
this.searchTextUpdated(); takeUntil(this.destroy$)
}) ).subscribe((value) => this.searchTextUpdated(value));
)
.subscribe();
} }
sidenavClicked() { sidenavClicked() {
@ -136,7 +144,7 @@ export class HomeComponent extends PageComponent implements AfterViewInit, OnIni
private updateActiveComponent(activeComponent: any) { private updateActiveComponent(activeComponent: any) {
this.showSearch = false; this.showSearch = false;
this.searchText = ''; this.textSearch.reset('', {emitEvent: false});
this.activeComponent = activeComponent; this.activeComponent = activeComponent;
this.hideLoadingBar = activeComponent && activeComponent instanceof RouterTabsComponent; this.hideLoadingBar = activeComponent && activeComponent instanceof RouterTabsComponent;
if (this.activeComponent && instanceOfSearchableComponent(this.activeComponent)) { if (this.activeComponent && instanceOfSearchableComponent(this.activeComponent)) {
@ -165,16 +173,15 @@ export class HomeComponent extends PageComponent implements AfterViewInit, OnIni
closeSearch() { closeSearch() {
if (this.searchEnabled) { if (this.searchEnabled) {
this.showSearch = false; this.showSearch = false;
if (this.searchText.length) { if (this.textSearch.value.length) {
this.searchText = ''; this.textSearch.reset();
this.searchTextUpdated();
} }
} }
} }
private searchTextUpdated() { private searchTextUpdated(searchText: string) {
if (this.searchableComponent) { if (this.searchableComponent) {
this.searchableComponent.onSearchTextUpdated(this.searchText); this.searchableComponent.onSearchTextUpdated(searchText.trim());
} }
} }
} }

8
ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.html

@ -46,12 +46,12 @@
matTooltipPosition="above"> matTooltipPosition="above">
<mat-icon>search</mat-icon> <mat-icon>search</mat-icon>
</button> </button>
<input #ruleNodeSearchInput matInput <input matInput
[(ngModel)]="ruleNodeTypeSearch" [formControl]="ruleNodeTypeSearch"
placeholder="{{'rulenode.search' | translate}}"/> placeholder="{{'rulenode.search' | translate}}"/>
<button mat-icon-button matSuffix class="tb-small" <button mat-icon-button matSuffix class="tb-small"
[fxShow]="ruleNodeTypeSearch !== ''" [fxShow]="ruleNodeTypeSearch.value !== ''"
(click)="ruleNodeTypeSearch = ''; updateRuleChainLibrary()" (click)="ruleNodeTypeSearch.reset()"
matTooltip="{{'action.clear-search' | translate}}" matTooltip="{{'action.clear-search' | translate}}"
matTooltipPosition="above"> matTooltipPosition="above">
<mat-icon>close</mat-icon> <mat-icon>close</mat-icon>

39
ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts

@ -19,7 +19,6 @@ import {
AfterViewInit, AfterViewInit,
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
ElementRef,
EventEmitter, EventEmitter,
HostBinding, HostBinding,
Inject, Inject,
@ -37,6 +36,7 @@ import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { import {
FormBuilder,
FormGroupDirective, FormGroupDirective,
NgForm, NgForm,
UntypedFormBuilder, UntypedFormBuilder,
@ -77,8 +77,8 @@ import {
} from '@shared/models/rule-node.models'; } from '@shared/models/rule-node.models';
import { FcRuleNodeModel, FcRuleNodeTypeModel, RuleChainMenuContextInfo } from './rulechain-page.models'; import { FcRuleNodeModel, FcRuleNodeTypeModel, RuleChainMenuContextInfo } from './rulechain-page.models';
import { RuleChainService } from '@core/http/rule-chain.service'; import { RuleChainService } from '@core/http/rule-chain.service';
import { fromEvent, NEVER, Observable, of, ReplaySubject, Subscription } from 'rxjs'; import { NEVER, Observable, of, ReplaySubject, startWith, skip, Subject } from 'rxjs';
import { debounceTime, distinctUntilChanged, mergeMap, tap } from 'rxjs/operators'; import { debounceTime, distinctUntilChanged, mergeMap, takeUntil, tap } from 'rxjs/operators';
import { ISearchableComponent } from '../../models/searchable-component.models'; import { ISearchableComponent } from '../../models/searchable-component.models';
import { deepClone, isDefinedAndNotNull } from '@core/utils'; import { deepClone, isDefinedAndNotNull } from '@core/utils';
import { RuleNodeDetailsComponent } from '@home/pages/rulechain/rule-node-details.component'; import { RuleNodeDetailsComponent } from '@home/pages/rulechain/rule-node-details.component';
@ -115,8 +115,6 @@ export class RuleChainPageComponent extends PageComponent
@HostBinding('style.width') width = '100%'; @HostBinding('style.width') width = '100%';
@HostBinding('style.height') height = '100%'; @HostBinding('style.height') height = '100%';
@ViewChild('ruleNodeSearchInput') ruleNodeSearchInputField: ElementRef;
@ViewChild('ruleChainCanvas', {static: true}) ruleChainCanvas: NgxFlowchartComponent; @ViewChild('ruleChainCanvas', {static: true}) ruleChainCanvas: NgxFlowchartComponent;
@ViewChildren('ruleNodeTypeExpansionPanels', @ViewChildren('ruleNodeTypeExpansionPanels',
@ -167,7 +165,7 @@ export class RuleChainPageComponent extends PageComponent
enableHotKeys = true; enableHotKeys = true;
ruleNodeSearch = ''; ruleNodeSearch = '';
ruleNodeTypeSearch = ''; ruleNodeTypeSearch = this.fb.control('', {nonNullable: true});
ruleChain: RuleChain; ruleChain: RuleChain;
ruleChainMetaData: RuleChainMetaData; ruleChainMetaData: RuleChainMetaData;
@ -257,7 +255,7 @@ export class RuleChainPageComponent extends PageComponent
updateBreadcrumbs = new EventEmitter(); updateBreadcrumbs = new EventEmitter();
private rxSubscription: Subscription; private destroy$ = new Subject<void>();
private tooltipTimeout: Timeout; private tooltipTimeout: Timeout;
@ -274,9 +272,11 @@ export class RuleChainPageComponent extends PageComponent
private changeDetector: ChangeDetectorRef, private changeDetector: ChangeDetectorRef,
public dialog: MatDialog, public dialog: MatDialog,
public dialogService: DialogService, public dialogService: DialogService,
public fb: UntypedFormBuilder) { public fb: FormBuilder) {
super(store); super(store);
this.rxSubscription = this.route.data.subscribe( this.route.data.pipe(
takeUntil(this.destroy$)
).subscribe(
() => { () => {
this.reset(); this.reset();
this.init(); this.init();
@ -285,6 +285,13 @@ export class RuleChainPageComponent extends PageComponent
} }
ngOnInit() { ngOnInit() {
this.ruleNodeTypeSearch.valueChanges.pipe(
debounceTime(150),
startWith(''),
distinctUntilChanged((a: string, b: string) => a.trim() === b.trim()),
skip(1),
takeUntil(this.destroy$)
).subscribe(() => this.updateRuleChainLibrary());
} }
ngAfterViewChecked(){ ngAfterViewChecked(){
@ -292,21 +299,13 @@ export class RuleChainPageComponent extends PageComponent
} }
ngAfterViewInit() { ngAfterViewInit() {
fromEvent(this.ruleNodeSearchInputField.nativeElement, 'keyup')
.pipe(
debounceTime(150),
distinctUntilChanged(),
tap(() => {
this.updateRuleChainLibrary();
})
)
.subscribe();
this.ruleChainCanvas.adjustCanvasSize(true); this.ruleChainCanvas.adjustCanvasSize(true);
} }
ngOnDestroy() { ngOnDestroy() {
super.ngOnDestroy(); super.ngOnDestroy();
this.rxSubscription.unsubscribe(); this.destroy$.next();
this.destroy$.complete();
} }
currentRuleChainIdChanged(ruleChainId: string) { currentRuleChainIdChanged(ruleChainId: string) {
@ -461,7 +460,7 @@ export class RuleChainPageComponent extends PageComponent
} }
updateRuleChainLibrary() { updateRuleChainLibrary() {
const search = this.ruleNodeTypeSearch.toUpperCase(); const search = this.ruleNodeTypeSearch.value.trim().toUpperCase();
const res = this.ruleNodeComponents.filter( const res = this.ruleNodeComponents.filter(
(ruleNodeComponent) => ruleNodeComponent.name.toUpperCase().includes(search)); (ruleNodeComponent) => ruleNodeComponent.name.toUpperCase().includes(search));
this.loadRuleChainLibrary(res); this.loadRuleChainLibrary(res);

5
ui-ngx/src/app/shared/models/page/page-link.ts

@ -115,8 +115,9 @@ export class PageLink {
public toQuery(): string { public toQuery(): string {
let query = `?pageSize=${this.pageSize}&page=${this.page}`; let query = `?pageSize=${this.pageSize}&page=${this.page}`;
if (this.textSearch && this.textSearch.length) { const textSearchParams = this.textSearch?.trim();
const textSearch = encodeURIComponent(this.textSearch); if (textSearchParams?.length) {
const textSearch = encodeURIComponent(textSearchParams);
query += `&textSearch=${textSearch}`; query += `&textSearch=${textSearch}`;
} }
if (this.sortOrder) { if (this.sortOrder) {

Loading…
Cancel
Save