Commit f9574d7041a453efaa9cc72a836c6231b3880b76
Merge branch 'develop/3.0' of https://github.com/thingsboard/thingsboard into map/3.0
Showing
34 changed files
with
187 additions
and
174 deletions
... | ... | @@ -13,9 +13,9 @@ Before performing initial installation you can configure the type of database to |
13 | 13 | In order to set database type change the value of `DATABASE` variable in `.env` file to one of the following: |
14 | 14 | |
15 | 15 | - `postgres` - use PostgreSQL database; |
16 | -- `cassandra` - use Cassandra database; | |
16 | +- `hybrid` - use PostgreSQL for entities database and Cassandra for timeseries database; | |
17 | 17 | |
18 | -**NOTE**: According to the database type corresponding docker service will be deployed (see `docker-compose.postgres.yml`, `docker-compose.cassandra.yml` for details). | |
18 | +**NOTE**: According to the database type corresponding docker service will be deployed (see `docker-compose.postgres.yml`, `docker-compose.hybrid.yml` for details). | |
19 | 19 | |
20 | 20 | Execute the following command to create log folders for the services and chown of these folders to the docker container users. |
21 | 21 | To be able to change user, **chown** command is used, which requires sudo permissions (script will request password for a sudo access): | ... | ... |
... | ... | @@ -49,7 +49,6 @@ |
49 | 49 | ] |
50 | 50 | }, |
51 | 51 | "scripts": [ |
52 | - "node_modules/javascript-detect-element-resize/detect-element-resize.js", | |
53 | 52 | "node_modules/jquery/dist/jquery.min.js", |
54 | 53 | "node_modules/jquery.terminal/js/jquery.terminal.min.js", |
55 | 54 | "node_modules/flot/lib/jquery.colorhelpers.js", | ... | ... |
... | ... | @@ -1561,6 +1561,11 @@ |
1561 | 1561 | } |
1562 | 1562 | } |
1563 | 1563 | }, |
1564 | + "@juggle/resize-observer": { | |
1565 | + "version": "3.1.3", | |
1566 | + "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.1.3.tgz", | |
1567 | + "integrity": "sha512-y7qc6SzZBlSpx8hEDfV0S9Cx6goROX/vBhS2Ru1Q78Jp1FlCMbxp7UcAN90rLgB3X8DSMBgDFxcmoG/VfdAhFA==" | |
1568 | + }, | |
1564 | 1569 | "@mat-datetimepicker/core": { |
1565 | 1570 | "version": "4.1.0", |
1566 | 1571 | "resolved": "https://registry.npmjs.org/@mat-datetimepicker/core/-/core-4.1.0.tgz", |
... | ... | @@ -7083,11 +7088,6 @@ |
7083 | 7088 | "integrity": "sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4=", |
7084 | 7089 | "dev": true |
7085 | 7090 | }, |
7086 | - "javascript-detect-element-resize": { | |
7087 | - "version": "0.5.3", | |
7088 | - "resolved": "https://registry.npmjs.org/javascript-detect-element-resize/-/javascript-detect-element-resize-0.5.3.tgz", | |
7089 | - "integrity": "sha1-GnHNUd/lZZB/KZAS/nOilBBAJd4=" | |
7090 | - }, | |
7091 | 7091 | "jest-worker": { |
7092 | 7092 | "version": "25.1.0", |
7093 | 7093 | "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.1.0.tgz", | ... | ... |
... | ... | @@ -28,6 +28,7 @@ |
28 | 28 | "@date-io/date-fns": "^2.6.1", |
29 | 29 | "@flowjs/flow.js": "^2.14.0", |
30 | 30 | "@flowjs/ngx-flow": "^0.4.3", |
31 | + "@juggle/resize-observer": "^3.1.3", | |
31 | 32 | "@mat-datetimepicker/core": "^4.1.0", |
32 | 33 | "@material-ui/core": "^4.9.11", |
33 | 34 | "@material-ui/icons": "^4.9.1", |
... | ... | @@ -49,7 +50,6 @@ |
49 | 50 | "flot": "git://github.com/thingsboard/flot.git#0.9-work", |
50 | 51 | "flot.curvedlines": "git://github.com/MichaelZinsmaier/CurvedLines.git#master", |
51 | 52 | "font-awesome": "^4.7.0", |
52 | - "javascript-detect-element-resize": "^0.5.3", | |
53 | 53 | "jquery": "^3.5.0", |
54 | 54 | "jquery.terminal": "^2.15.4", |
55 | 55 | "js-beautify": "^1.11.0", | ... | ... |
... | ... | @@ -123,7 +123,7 @@ export class AliasController implements IAliasController { |
123 | 123 | } |
124 | 124 | |
125 | 125 | getAliasInfo(aliasId: string): Observable<AliasInfo> { |
126 | - const aliasInfo = this.resolvedAliases[aliasId]; | |
126 | + let aliasInfo = this.resolvedAliases[aliasId]; | |
127 | 127 | if (aliasInfo) { |
128 | 128 | return of(aliasInfo); |
129 | 129 | } else if (this.resolvedAliasesObservable[aliasId]) { |
... | ... | @@ -159,7 +159,12 @@ export class AliasController implements IAliasController { |
159 | 159 | delete this.resolvedAliasesObservable[aliasId]; |
160 | 160 | return res; |
161 | 161 | } |
162 | - return this.resolvedAliasesObservable[aliasId]; | |
162 | + aliasInfo = this.resolvedAliases[aliasId]; | |
163 | + if (aliasInfo) { | |
164 | + return of(aliasInfo); | |
165 | + } else { | |
166 | + return this.resolvedAliasesObservable[aliasId]; | |
167 | + } | |
163 | 168 | } |
164 | 169 | } |
165 | 170 | ... | ... |
... | ... | @@ -19,7 +19,7 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; |
19 | 19 | import { Store } from '@ngrx/store'; |
20 | 20 | import { AppState } from '@core/core.state'; |
21 | 21 | import { FormBuilder, FormGroup } from '@angular/forms'; |
22 | -import { Observable, Subject } from 'rxjs'; | |
22 | +import { Observable, ReplaySubject } from 'rxjs'; | |
23 | 23 | import { Router } from '@angular/router'; |
24 | 24 | import { DialogComponent } from '@app/shared/components/dialog.component'; |
25 | 25 | import { |
... | ... | @@ -30,7 +30,7 @@ import { |
30 | 30 | alarmStatusTranslations |
31 | 31 | } from '@app/shared/models/alarm.models'; |
32 | 32 | import { AlarmService } from '@core/http/alarm.service'; |
33 | -import { share, tap } from 'rxjs/operators'; | |
33 | +import { tap } from 'rxjs/operators'; | |
34 | 34 | import { DatePipe } from '@angular/common'; |
35 | 35 | import { TranslateService } from '@ngx-translate/core'; |
36 | 36 | |
... | ... | @@ -54,10 +54,9 @@ export class AlarmDetailsDialogComponent extends DialogComponent<AlarmDetailsDia |
54 | 54 | allowClear: boolean; |
55 | 55 | displayDetails: boolean; |
56 | 56 | |
57 | - loadAlarmSubject = new Subject<AlarmInfo>(); | |
57 | + loadAlarmSubject = new ReplaySubject<AlarmInfo>(); | |
58 | 58 | alarm$: Observable<AlarmInfo> = this.loadAlarmSubject.asObservable().pipe( |
59 | - tap(alarm => this.loadAlarmFields(alarm)), | |
60 | - share() | |
59 | + tap(alarm => this.loadAlarmFields(alarm)) | |
61 | 60 | ); |
62 | 61 | |
63 | 62 | alarmSeverityColorsMap = alarmSeverityColors; | ... | ... |
... | ... | @@ -43,6 +43,6 @@ export class AlarmTableHeaderComponent extends EntityTableHeaderComponent<AlarmI |
43 | 43 | |
44 | 44 | searchStatusChanged(searchStatus: AlarmSearchStatus) { |
45 | 45 | this.alarmTableConfig.searchStatus = searchStatus; |
46 | - this.alarmTableConfig.table.updateData(); | |
46 | + this.alarmTableConfig.table.resetSortAndFilter(true, true); | |
47 | 47 | } |
48 | 48 | } | ... | ... |
... | ... | @@ -63,12 +63,16 @@ export class AliasesEntitySelectComponent implements OnInit, OnDestroy { |
63 | 63 | ngOnInit(): void { |
64 | 64 | this.rxSubscriptions.push(this.aliasController.entityAliasesChanged.subscribe( |
65 | 65 | () => { |
66 | - this.updateDisplayValue(); | |
66 | + setTimeout(() => { | |
67 | + this.updateDisplayValue(); | |
68 | + }, 0); | |
67 | 69 | } |
68 | 70 | )); |
69 | 71 | this.rxSubscriptions.push(this.aliasController.entityAliasResolved.subscribe( |
70 | 72 | () => { |
71 | - this.updateDisplayValue(); | |
73 | + setTimeout(() => { | |
74 | + this.updateDisplayValue(); | |
75 | + }, 0); | |
72 | 76 | } |
73 | 77 | )); |
74 | 78 | } | ... | ... |
... | ... | @@ -53,6 +53,7 @@ import { Widget, WidgetPosition } from '@app/shared/models/widget.models'; |
53 | 53 | import { MatMenuTrigger } from '@angular/material/menu'; |
54 | 54 | import { SafeStyle } from '@angular/platform-browser'; |
55 | 55 | import { distinct } from 'rxjs/operators'; |
56 | +import { ResizeObserver } from '@juggle/resize-observer'; | |
56 | 57 | |
57 | 58 | @Component({ |
58 | 59 | selector: 'tb-dashboard', |
... | ... | @@ -166,7 +167,7 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo |
166 | 167 | |
167 | 168 | private optionsChangeNotificationsPaused = false; |
168 | 169 | |
169 | - private gridsterResizeListener = null; | |
170 | + private gridsterResize$: ResizeObserver; | |
170 | 171 | |
171 | 172 | constructor(protected store: Store<AppState>, |
172 | 173 | private timeService: TimeService, |
... | ... | @@ -225,9 +226,8 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo |
225 | 226 | |
226 | 227 | ngOnDestroy(): void { |
227 | 228 | super.ngOnDestroy(); |
228 | - if (this.gridsterResizeListener) { | |
229 | - // @ts-ignore | |
230 | - removeResizeListener(this.gridster.el, this.gridsterResizeListener); | |
229 | + if (this.gridsterResize$) { | |
230 | + this.gridsterResize$.disconnect(); | |
231 | 231 | } |
232 | 232 | if (this.breakpointObserverSubscription) { |
233 | 233 | this.breakpointObserverSubscription.unsubscribe(); |
... | ... | @@ -290,9 +290,10 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo |
290 | 290 | } |
291 | 291 | |
292 | 292 | ngAfterViewInit(): void { |
293 | - this.gridsterResizeListener = this.onGridsterParentResize.bind(this); | |
294 | - // @ts-ignore | |
295 | - addResizeListener(this.gridster.el, this.gridsterResizeListener); | |
293 | + this.gridsterResize$ = new ResizeObserver(() => { | |
294 | + this.onGridsterParentResize() | |
295 | + }); | |
296 | + this.gridsterResize$.observe(this.gridster.el); | |
296 | 297 | } |
297 | 298 | |
298 | 299 | onUpdateTimewindow(startTimeMs: number, endTimeMs: number, interval?: number, persist?: boolean): void { |
... | ... | @@ -491,8 +492,8 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo |
491 | 492 | const parentHeight = this.gridster.el.offsetHeight; |
492 | 493 | if (this.isMobileSize && this.mobileAutofillHeight && parentHeight) { |
493 | 494 | this.updateMobileOpts(parentHeight); |
494 | - this.notifyGridsterOptionsChanged(); | |
495 | 495 | } |
496 | + this.notifyGridsterOptionsChanged(); | |
496 | 497 | } |
497 | 498 | |
498 | 499 | private updateLayoutOpts() { | ... | ... |
... | ... | @@ -434,9 +434,9 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn |
434 | 434 | this.updateData(); |
435 | 435 | } |
436 | 436 | |
437 | - resetSortAndFilter(update: boolean = true) { | |
437 | + resetSortAndFilter(update: boolean = true, preserveTimewindow: boolean = false) { | |
438 | 438 | this.pageLink.textSearch = null; |
439 | - if (this.entitiesTableConfig.useTimePageLink) { | |
439 | + if (this.entitiesTableConfig.useTimePageLink && !preserveTimewindow) { | |
440 | 440 | this.timewindow = historyInterval(DAY); |
441 | 441 | } |
442 | 442 | if (this.displayPagination) { | ... | ... |
... | ... | @@ -40,6 +40,6 @@ export class EventTableHeaderComponent extends EntityTableHeaderComponent<Event> |
40 | 40 | |
41 | 41 | eventTypeChanged(eventType: EventType | DebugEventType) { |
42 | 42 | this.eventTableConfig.eventType = eventType; |
43 | - this.eventTableConfig.table.updateData(); | |
43 | + this.eventTableConfig.table.resetSortAndFilter(true, true); | |
44 | 44 | } |
45 | 45 | } | ... | ... |
... | ... | @@ -35,6 +35,7 @@ import { CustomActionDescriptor } from '@shared/models/widget.models'; |
35 | 35 | import * as ace from 'ace-builds'; |
36 | 36 | import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; |
37 | 37 | import { css_beautify, html_beautify } from 'js-beautify'; |
38 | +import { ResizeObserver } from '@juggle/resize-observer'; | |
38 | 39 | |
39 | 40 | @Component({ |
40 | 41 | selector: 'tb-custom-action-pretty-resources-tabs', |
... | ... | @@ -64,7 +65,7 @@ export class CustomActionPrettyResourcesTabsComponent extends PageComponent impl |
64 | 65 | |
65 | 66 | aceEditors: ace.Ace.Editor[] = []; |
66 | 67 | editorsResizeCafs: {[editorId: string]: CancelAnimationFrame} = {}; |
67 | - aceResizeListeners: { element: any, resizeListener: any }[] = []; | |
68 | + aceResize$: ResizeObserver; | |
68 | 69 | htmlEditor: ace.Ace.Editor; |
69 | 70 | cssEditor: ace.Ace.Editor; |
70 | 71 | setValuesPending = false; |
... | ... | @@ -84,10 +85,7 @@ export class CustomActionPrettyResourcesTabsComponent extends PageComponent impl |
84 | 85 | } |
85 | 86 | |
86 | 87 | ngOnDestroy(): void { |
87 | - this.aceResizeListeners.forEach((resizeListener) => { | |
88 | - // @ts-ignore | |
89 | - removeResizeListener(resizeListener.element, resizeListener.resizeListener); | |
90 | - }); | |
88 | + this.aceResize$.disconnect(); | |
91 | 89 | } |
92 | 90 | |
93 | 91 | ngOnChanges(changes: SimpleChanges): void { |
... | ... | @@ -153,6 +151,12 @@ export class CustomActionPrettyResourcesTabsComponent extends PageComponent impl |
153 | 151 | } |
154 | 152 | |
155 | 153 | private initAceEditors() { |
154 | + this.aceResize$ = new ResizeObserver((entries) => { | |
155 | + entries.forEach((entry) => { | |
156 | + const editor = this.aceEditors.find(aceEditor => aceEditor.container === entry.target); | |
157 | + this.onAceEditorResize(editor); | |
158 | + }) | |
159 | + }); | |
156 | 160 | this.htmlEditor = this.createAceEditor(this.htmlInputElmRef, 'html'); |
157 | 161 | this.htmlEditor.on('input', () => { |
158 | 162 | const editorValue = this.htmlEditor.getValue(); |
... | ... | @@ -187,12 +191,7 @@ export class CustomActionPrettyResourcesTabsComponent extends PageComponent impl |
187 | 191 | const aceEditor = ace.edit(editorElement, editorOptions); |
188 | 192 | aceEditor.session.setUseWrapMode(true); |
189 | 193 | this.aceEditors.push(aceEditor); |
190 | - | |
191 | - const resizeListener = this.onAceEditorResize.bind(this, aceEditor); | |
192 | - | |
193 | - // @ts-ignore | |
194 | - addResizeListener(editorElement, resizeListener); | |
195 | - this.aceResizeListeners.push({element: editorElement, resizeListener}); | |
194 | + this.aceResize$.observe(editorElement); | |
196 | 195 | return aceEditor; |
197 | 196 | } |
198 | 197 | ... | ... |
... | ... | @@ -84,9 +84,6 @@ export class DateRangeNavigatorWidgetComponent extends PageComponent implements |
84 | 84 | } |
85 | 85 | |
86 | 86 | ngOnInit(): void { |
87 | - this.dashboardTimewindowChangedSubscription = this.ctx.dashboard.dashboardTimewindowChanged.subscribe(() => { | |
88 | - this.widgetContextTimewindowSync(); | |
89 | - }); | |
90 | 87 | this.settings = this.ctx.settings; |
91 | 88 | this.settings.useSessionStorage = isDefined(this.settings.useSessionStorage) ? this.settings.useSessionStorage : true; |
92 | 89 | let selection; |
... | ... | @@ -110,6 +107,9 @@ export class DateRangeNavigatorWidgetComponent extends PageComponent implements |
110 | 107 | } |
111 | 108 | this.selectedStepSize = this.datesMap[this.settings.stepSize || 'day'].ts; |
112 | 109 | this.widgetContextTimewindowSync(); |
110 | + this.dashboardTimewindowChangedSubscription = this.ctx.dashboard.dashboardTimewindowChanged.subscribe(() => { | |
111 | + this.widgetContextTimewindowSync(); | |
112 | + }); | |
113 | 113 | } |
114 | 114 | |
115 | 115 | ngOnDestroy(): void { | ... | ... |
... | ... | @@ -58,6 +58,7 @@ import { AttributeData, AttributeScope } from '@shared/models/telemetry/telemetr |
58 | 58 | import { forkJoin, Observable } from 'rxjs'; |
59 | 59 | import { tap } from 'rxjs/operators'; |
60 | 60 | import { ImportExportService } from '@home/components/import-export/import-export.service'; |
61 | +import { ResizeObserver } from '@juggle/resize-observer'; | |
61 | 62 | |
62 | 63 | // @dynamic |
63 | 64 | @Component({ |
... | ... | @@ -93,7 +94,7 @@ export class GatewayFormComponent extends PageComponent implements OnInit, OnDes |
93 | 94 | private successfulSaved: string; |
94 | 95 | private gatewayNameExists: string; |
95 | 96 | private archiveFileName: string; |
96 | - private formResizeListener: any; | |
97 | + private formResize$: ResizeObserver; | |
97 | 98 | private subscribeStorageType$: any; |
98 | 99 | private subscribeGateway$: any; |
99 | 100 | |
... | ... | @@ -116,15 +117,15 @@ export class GatewayFormComponent extends PageComponent implements OnInit, OnDes |
116 | 117 | |
117 | 118 | this.buildForm(); |
118 | 119 | this.ctx.updateWidgetParams(); |
119 | - this.formResizeListener = this.resize.bind(this); | |
120 | - // @ts-ignore | |
121 | - addResizeListener(this.formContainerRef.nativeElement, this.formResizeListener); | |
120 | + this.formResize$ = new ResizeObserver(() => { | |
121 | + this.resize(); | |
122 | + }); | |
123 | + this.formResize$.observe(this.formContainerRef.nativeElement); | |
122 | 124 | } |
123 | 125 | |
124 | 126 | ngOnDestroy(): void { |
125 | - if (this.formResizeListener) { | |
126 | - // @ts-ignore | |
127 | - removeResizeListener(this.formContainerRef.nativeElement, this.formResizeListener); | |
127 | + if (this.formResize$) { | |
128 | + this.formResize$.disconnect(); | |
128 | 129 | } |
129 | 130 | this.subscribeGateway$.unsubscribe(); |
130 | 131 | this.subscribeStorageType$.unsubscribe(); | ... | ... |
... | ... | @@ -33,6 +33,7 @@ import { AttributeService } from '@core/http/attribute.service'; |
33 | 33 | import { AttributeData, AttributeScope, LatestTelemetry } from '@shared/models/telemetry/telemetry.models'; |
34 | 34 | import { forkJoin, Observable } from 'rxjs'; |
35 | 35 | import { EntityId } from '@shared/models/id/entity-id'; |
36 | +import { ResizeObserver } from '@juggle/resize-observer'; | |
36 | 37 | |
37 | 38 | type FieldAlignment = 'row' | 'column'; |
38 | 39 | |
... | ... | @@ -94,7 +95,7 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni |
94 | 95 | @Input() |
95 | 96 | ctx: WidgetContext; |
96 | 97 | |
97 | - private formResizeListener: any; | |
98 | + private formResize$: ResizeObserver; | |
98 | 99 | private settings: MultipleInputWidgetSettings; |
99 | 100 | private widgetConfig: WidgetConfig; |
100 | 101 | private subscription: IWidgetSubscription; |
... | ... | @@ -135,15 +136,15 @@ export class MultipleInputWidgetComponent extends PageComponent implements OnIni |
135 | 136 | this.updateDatasources(); |
136 | 137 | this.buildForm(); |
137 | 138 | this.ctx.updateWidgetParams(); |
138 | - this.formResizeListener = this.resize.bind(this); | |
139 | - // @ts-ignore | |
140 | - addResizeListener(this.formContainerRef.nativeElement, this.formResizeListener); | |
139 | + this.formResize$ = new ResizeObserver(() => { | |
140 | + this.resize(); | |
141 | + }); | |
142 | + this.formResize$.observe(this.formContainerRef.nativeElement); | |
141 | 143 | } |
142 | 144 | |
143 | 145 | ngOnDestroy(): void { |
144 | - if (this.formResizeListener) { | |
145 | - // @ts-ignore | |
146 | - removeResizeListener(this.formContainerRef.nativeElement, this.formResizeListener); | |
146 | + if (this.formResize$) { | |
147 | + this.formResize$.disconnect(); | |
147 | 148 | } |
148 | 149 | } |
149 | 150 | ... | ... |
... | ... | @@ -22,9 +22,9 @@ import { Store } from '@ngrx/store'; |
22 | 22 | import { AppState } from '@core/core.state'; |
23 | 23 | import { isDefined, isNumber } from '@core/utils'; |
24 | 24 | import { CanvasDigitalGauge, CanvasDigitalGaugeOptions } from '@home/components/widget/lib/canvas-digital-gauge'; |
25 | -import GenericOptions = CanvasGauges.GenericOptions; | |
26 | 25 | import * as tinycolor_ from 'tinycolor2'; |
27 | -import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; | |
26 | +import GenericOptions = CanvasGauges.GenericOptions; | |
27 | +import { ResizeObserver } from '@juggle/resize-observer'; | |
28 | 28 | |
29 | 29 | const tinycolor = tinycolor_; |
30 | 30 | |
... | ... | @@ -103,7 +103,7 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { |
103 | 103 | |
104 | 104 | private canvasBar: CanvasDigitalGauge; |
105 | 105 | |
106 | - private knobResizeListener: any; | |
106 | + private knobResize$: ResizeObserver; | |
107 | 107 | |
108 | 108 | constructor(private utils: UtilsService, |
109 | 109 | protected store: Store<AppState>) { |
... | ... | @@ -126,16 +126,16 @@ export class KnobComponent extends PageComponent implements OnInit, OnDestroy { |
126 | 126 | this.textMeasure = $(this.textMeasureRef.nativeElement); |
127 | 127 | this.canvasBarElement = this.canvasBarElementRef.nativeElement; |
128 | 128 | |
129 | - this.knobResizeListener = this.resize.bind(this); | |
130 | - // @ts-ignore | |
131 | - addResizeListener(this.knobContainerRef.nativeElement, this.knobResizeListener); | |
129 | + this.knobResize$ = new ResizeObserver(() => { | |
130 | + this.resize(); | |
131 | + }); | |
132 | + this.knobResize$.observe(this.knobContainerRef.nativeElement); | |
132 | 133 | this.init(); |
133 | 134 | } |
134 | 135 | |
135 | 136 | ngOnDestroy(): void { |
136 | - if (this.knobResizeListener) { | |
137 | - // @ts-ignore | |
138 | - removeResizeListener(this.knobContainerRef.nativeElement, this.knobResizeListener); | |
137 | + if (this.knobResize$) { | |
138 | + this.knobResize$.disconnect(); | |
139 | 139 | } |
140 | 140 | } |
141 | 141 | ... | ... |
... | ... | @@ -25,6 +25,7 @@ import { UtilsService } from '@core/services/utils.service'; |
25 | 25 | import { IWidgetSubscription, SubscriptionInfo, WidgetSubscriptionOptions } from '@core/api/widget-api.models'; |
26 | 26 | import { DatasourceType, widgetType } from '@shared/models/widget.models'; |
27 | 27 | import { EntityType } from '@shared/models/entity-type.models'; |
28 | +import { ResizeObserver } from '@juggle/resize-observer'; | |
28 | 29 | import Timeout = NodeJS.Timeout; |
29 | 30 | |
30 | 31 | const tinycolor = tinycolor_; |
... | ... | @@ -93,7 +94,7 @@ export class LedIndicatorComponent extends PageComponent implements OnInit, OnDe |
93 | 94 | private ledErrorContainer: JQuery<HTMLElement>; |
94 | 95 | private ledError: JQuery<HTMLElement>; |
95 | 96 | |
96 | - private ledResizeListener: any; | |
97 | + private ledResize$: ResizeObserver; | |
97 | 98 | |
98 | 99 | private subscriptionOptions: WidgetSubscriptionOptions = { |
99 | 100 | callbacks: { |
... | ... | @@ -122,9 +123,10 @@ export class LedIndicatorComponent extends PageComponent implements OnInit, OnDe |
122 | 123 | this.ledErrorContainer = $(this.ledErrorContainerRef.nativeElement); |
123 | 124 | this.ledError = $(this.ledErrorRef.nativeElement); |
124 | 125 | |
125 | - this.ledResizeListener = this.resize.bind(this); | |
126 | - // @ts-ignore | |
127 | - addResizeListener(this.ledContainerRef.nativeElement, this.ledResizeListener); | |
126 | + this.ledResize$ = new ResizeObserver(() => { | |
127 | + this.resize(); | |
128 | + }); | |
129 | + this.ledResize$.observe(this.ledContainerRef.nativeElement); | |
128 | 130 | this.init(); |
129 | 131 | } |
130 | 132 | |
... | ... | @@ -136,9 +138,8 @@ export class LedIndicatorComponent extends PageComponent implements OnInit, OnDe |
136 | 138 | if (this.subscription) { |
137 | 139 | this.ctx.subscriptionApi.removeSubscription(this.subscription.id); |
138 | 140 | } |
139 | - if (this.ledResizeListener) { | |
140 | - // @ts-ignore | |
141 | - removeResizeListener(this.ledContainerRef.nativeElement, this.ledResizeListener); | |
141 | + if (this.ledResize$) { | |
142 | + this.ledResize$.disconnect(); | |
142 | 143 | } |
143 | 144 | } |
144 | 145 | ... | ... |
... | ... | @@ -14,7 +14,7 @@ |
14 | 14 | /// limitations under the License. |
15 | 15 | /// |
16 | 16 | |
17 | -import { AfterViewInit, Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; | |
17 | +import { Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; | |
18 | 18 | import { PageComponent } from '@shared/components/page.component'; |
19 | 19 | import { WidgetContext } from '@home/models/widget-component.models'; |
20 | 20 | import { UtilsService } from '@core/services/utils.service'; |
... | ... | @@ -24,6 +24,7 @@ import { isDefined } from '@core/utils'; |
24 | 24 | import { IWidgetSubscription, SubscriptionInfo, WidgetSubscriptionOptions } from '@core/api/widget-api.models'; |
25 | 25 | import { DatasourceType, widgetType } from '@shared/models/widget.models'; |
26 | 26 | import { EntityType } from '@shared/models/entity-type.models'; |
27 | +import { ResizeObserver } from '@juggle/resize-observer'; | |
27 | 28 | |
28 | 29 | type RetrieveValueMethod = 'rpc' | 'attribute' | 'timeseries'; |
29 | 30 | |
... | ... | @@ -88,7 +89,7 @@ export class RoundSwitchComponent extends PageComponent implements OnInit, OnDes |
88 | 89 | private switchErrorContainer: JQuery<HTMLElement>; |
89 | 90 | private switchError: JQuery<HTMLElement>; |
90 | 91 | |
91 | - private switchResizeListener: any; | |
92 | + private switchResize$: ResizeObserver; | |
92 | 93 | |
93 | 94 | constructor(private utils: UtilsService, |
94 | 95 | protected store: Store<AppState>) { |
... | ... | @@ -110,20 +111,19 @@ export class RoundSwitchComponent extends PageComponent implements OnInit, OnDes |
110 | 111 | this.onValue(); |
111 | 112 | }); |
112 | 113 | |
113 | - this.switchResizeListener = this.resize.bind(this); | |
114 | - // @ts-ignore | |
115 | - addResizeListener(this.switchContainerRef.nativeElement, this.switchResizeListener); | |
114 | + this.switchResize$ = new ResizeObserver(() => { | |
115 | + this.resize(); | |
116 | + }); | |
117 | + this.switchResize$.observe(this.switchContainerRef.nativeElement); | |
116 | 118 | this.init(); |
117 | - // this.ctx.resize = this.resize.bind(this); | |
118 | 119 | } |
119 | 120 | |
120 | 121 | ngOnDestroy(): void { |
121 | 122 | if (this.valueSubscription) { |
122 | 123 | this.ctx.subscriptionApi.removeSubscription(this.valueSubscription.id); |
123 | 124 | } |
124 | - if (this.switchResizeListener) { | |
125 | - // @ts-ignore | |
126 | - removeResizeListener(this.switchContainerRef.nativeElement, this.switchResizeListener); | |
125 | + if (this.switchResize$) { | |
126 | + this.switchResize$.disconnect(); | |
127 | 127 | } |
128 | 128 | } |
129 | 129 | ... | ... |
... | ... | @@ -25,6 +25,7 @@ import { IWidgetSubscription, SubscriptionInfo, WidgetSubscriptionOptions } from |
25 | 25 | import { DatasourceType, widgetType } from '@shared/models/widget.models'; |
26 | 26 | import { EntityType } from '@shared/models/entity-type.models'; |
27 | 27 | import { MatSlideToggle } from '@angular/material/slide-toggle'; |
28 | +import { ResizeObserver } from '@juggle/resize-observer'; | |
28 | 29 | |
29 | 30 | const switchAspectRation = 2.7893; |
30 | 31 | |
... | ... | @@ -98,7 +99,7 @@ export class SwitchComponent extends PageComponent implements OnInit, OnDestroy |
98 | 99 | private switchErrorContainer: JQuery<HTMLElement>; |
99 | 100 | private switchError: JQuery<HTMLElement>; |
100 | 101 | |
101 | - private switchResizeListener: any; | |
102 | + private switchResize$: ResizeObserver; | |
102 | 103 | |
103 | 104 | constructor(private utils: UtilsService, |
104 | 105 | protected store: Store<AppState>) { |
... | ... | @@ -118,9 +119,10 @@ export class SwitchComponent extends PageComponent implements OnInit, OnDestroy |
118 | 119 | this.switchErrorContainer = $(this.switchErrorContainerRef.nativeElement); |
119 | 120 | this.switchError = $(this.switchErrorRef.nativeElement); |
120 | 121 | |
121 | - this.switchResizeListener = this.resize.bind(this); | |
122 | - // @ts-ignore | |
123 | - addResizeListener(this.switchContainerRef.nativeElement, this.switchResizeListener); | |
122 | + this.switchResize$ = new ResizeObserver(() => { | |
123 | + this.resize(); | |
124 | + }) | |
125 | + this.switchResize$.observe(this.switchContainerRef.nativeElement); | |
124 | 126 | this.init(); |
125 | 127 | } |
126 | 128 | |
... | ... | @@ -128,9 +130,8 @@ export class SwitchComponent extends PageComponent implements OnInit, OnDestroy |
128 | 130 | if (this.valueSubscription) { |
129 | 131 | this.ctx.subscriptionApi.removeSubscription(this.valueSubscription.id); |
130 | 132 | } |
131 | - if (this.switchResizeListener) { | |
132 | - // @ts-ignore | |
133 | - removeResizeListener(this.switchContainerRef.nativeElement, this.switchResizeListener); | |
133 | + if (this.switchResize$) { | |
134 | + this.switchResize$.disconnect(); | |
134 | 135 | } |
135 | 136 | } |
136 | 137 | ... | ... |
... | ... | @@ -91,6 +91,7 @@ import { DatasourceService } from '@core/api/datasource.service'; |
91 | 91 | import { WidgetSubscription } from '@core/api/widget-subscription'; |
92 | 92 | import { EntityService } from '@core/http/entity.service'; |
93 | 93 | import { ServicesMap } from '@home/models/services.map'; |
94 | +import { ResizeObserver } from '@juggle/resize-observer'; | |
94 | 95 | |
95 | 96 | @Component({ |
96 | 97 | selector: 'tb-widget', |
... | ... | @@ -140,7 +141,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI |
140 | 141 | |
141 | 142 | cafs: {[cafId: string]: CancelAnimationFrame} = {}; |
142 | 143 | |
143 | - onResizeListener = null; | |
144 | + private widgetResize$: ResizeObserver; | |
144 | 145 | |
145 | 146 | private cssParser = new cssjs(); |
146 | 147 | |
... | ... | @@ -647,10 +648,8 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI |
647 | 648 | } |
648 | 649 | |
649 | 650 | private destroyDynamicWidgetComponent() { |
650 | - if (this.widgetContext.$containerParent && this.onResizeListener) { | |
651 | - // @ts-ignore | |
652 | - removeResizeListener(this.widgetContext.$containerParent[0], this.onResizeListener); | |
653 | - this.onResizeListener = null; | |
651 | + if (this.widgetContext.$containerParent && this.widgetResize$) { | |
652 | + this.widgetResize$.disconnect() | |
654 | 653 | } |
655 | 654 | if (this.dynamicWidgetComponentRef) { |
656 | 655 | this.dynamicWidgetComponentRef.destroy(); |
... | ... | @@ -709,9 +708,10 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI |
709 | 708 | } |
710 | 709 | } |
711 | 710 | |
712 | - this.onResizeListener = this.onResize.bind(this); | |
713 | - // @ts-ignore | |
714 | - addResizeListener(this.widgetContext.$containerParent[0], this.onResizeListener); | |
711 | + this.widgetResize$ = new ResizeObserver(() => { | |
712 | + this.onResize(); | |
713 | + }); | |
714 | + this.widgetResize$.observe(this.widgetContext.$containerParent[0]); | |
715 | 715 | } |
716 | 716 | |
717 | 717 | private createSubscription(options: WidgetSubscriptionOptions, subscribe?: boolean): Observable<IWidgetSubscription> { | ... | ... |
... | ... | @@ -389,7 +389,7 @@ export class DashboardWidget implements GridsterItem, IDashboardWidget { |
389 | 389 | padding: this.padding, |
390 | 390 | margin: this.margin}; |
391 | 391 | if (this.widget.config.widgetStyle) { |
392 | - this.style = {...this.widget.config.widgetStyle, ...this.style}; | |
392 | + this.style = {...this.style, ...this.widget.config.widgetStyle}; | |
393 | 393 | } |
394 | 394 | |
395 | 395 | this.showWidgetTitlePanel = this.widgetContext.hideTitlePanel ? false : |
... | ... | @@ -474,7 +474,8 @@ export class DashboardWidget implements GridsterItem, IDashboardWidget { |
474 | 474 | if (mobileHeight) { |
475 | 475 | res = mobileHeight; |
476 | 476 | } else { |
477 | - res = this.widget.sizeY * 24 / this.dashboard.gridsterOpts.minCols; | |
477 | + const sizeY = this.widgetLayout ? this.widgetLayout.sizeY : this.widget.sizeY; | |
478 | + res = sizeY * 24 / this.dashboard.gridsterOpts.minCols; | |
478 | 479 | } |
479 | 480 | } else { |
480 | 481 | if (this.widgetLayout) { | ... | ... |
... | ... | @@ -36,7 +36,7 @@ export class AssetTableHeaderComponent extends EntityTableHeaderComponent<AssetI |
36 | 36 | |
37 | 37 | assetTypeChanged(assetType: string) { |
38 | 38 | this.entitiesTableConfig.componentsData.assetType = assetType; |
39 | - this.entitiesTableConfig.table.updateData(); | |
39 | + this.entitiesTableConfig.table.resetSortAndFilter(true); | |
40 | 40 | } |
41 | 41 | |
42 | 42 | } | ... | ... |
... | ... | @@ -14,8 +14,7 @@ |
14 | 14 | /// limitations under the License. |
15 | 15 | /// |
16 | 16 | |
17 | -import { Component, OnDestroy, OnInit, ViewEncapsulation, Input, Output, EventEmitter } from '@angular/core'; | |
18 | -import { PageComponent } from '@shared/components/page.component'; | |
17 | +import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; | |
19 | 18 | |
20 | 19 | @Component({ |
21 | 20 | selector: 'tb-dashboard-toolbar', | ... | ... |
... | ... | @@ -36,7 +36,7 @@ export class DeviceTableHeaderComponent extends EntityTableHeaderComponent<Devic |
36 | 36 | |
37 | 37 | deviceTypeChanged(deviceType: string) { |
38 | 38 | this.entitiesTableConfig.componentsData.deviceType = deviceType; |
39 | - this.entitiesTableConfig.table.updateData(); | |
39 | + this.entitiesTableConfig.table.resetSortAndFilter(true); | |
40 | 40 | } |
41 | 41 | |
42 | 42 | } | ... | ... |
... | ... | @@ -36,7 +36,7 @@ export class EntityViewTableHeaderComponent extends EntityTableHeaderComponent<E |
36 | 36 | |
37 | 37 | entityViewTypeChanged(entityViewType: string) { |
38 | 38 | this.entitiesTableConfig.componentsData.entityViewType = entityViewType; |
39 | - this.entitiesTableConfig.table.updateData(); | |
39 | + this.entitiesTableConfig.table.resetSortAndFilter(true); | |
40 | 40 | } |
41 | 41 | |
42 | 42 | } | ... | ... |
... | ... | @@ -46,6 +46,7 @@ import { |
46 | 46 | } from '@home/pages/widget/save-widget-type-as-dialog.component'; |
47 | 47 | import { Subscription } from 'rxjs'; |
48 | 48 | import Timeout = NodeJS.Timeout; |
49 | +import { ResizeObserver } from '@juggle/resize-observer'; | |
49 | 50 | |
50 | 51 | // @dynamic |
51 | 52 | @Component({ |
... | ... | @@ -124,7 +125,7 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe |
124 | 125 | jsonSettingsEditor: ace.Ace.Editor; |
125 | 126 | dataKeyJsonSettingsEditor: ace.Ace.Editor; |
126 | 127 | jsEditor: ace.Ace.Editor; |
127 | - aceResizeListeners: { element: any, resizeListener: any }[] = []; | |
128 | + aceResize$: ResizeObserver; | |
128 | 129 | |
129 | 130 | onWindowMessageListener = this.onWindowMessage.bind(this); |
130 | 131 | |
... | ... | @@ -193,10 +194,7 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe |
193 | 194 | |
194 | 195 | ngOnDestroy(): void { |
195 | 196 | this.window.removeEventListener('message', this.onWindowMessageListener); |
196 | - this.aceResizeListeners.forEach((resizeListener) => { | |
197 | - // @ts-ignore | |
198 | - removeResizeListener(resizeListener.element, resizeListener.resizeListener); | |
199 | - }); | |
197 | + this.aceResize$.disconnect(); | |
200 | 198 | this.rxSubscriptions.forEach((subscription) => { |
201 | 199 | subscription.unsubscribe(); |
202 | 200 | }); |
... | ... | @@ -272,6 +270,12 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe |
272 | 270 | } |
273 | 271 | |
274 | 272 | private initAceEditors() { |
273 | + this.aceResize$ = new ResizeObserver((entries) => { | |
274 | + entries.forEach((entry) => { | |
275 | + const editor = this.aceEditors.find(aceEditor => aceEditor.container === entry.target); | |
276 | + this.onAceEditorResize(editor); | |
277 | + }) | |
278 | + }); | |
275 | 279 | this.htmlEditor = this.createAceEditor(this.htmlInputElmRef, 'html'); |
276 | 280 | this.htmlEditor.on('input', () => { |
277 | 281 | const editorValue = this.htmlEditor.getValue(); |
... | ... | @@ -342,12 +346,7 @@ export class WidgetEditorComponent extends PageComponent implements OnInit, OnDe |
342 | 346 | const aceEditor = ace.edit(editorElement, editorOptions); |
343 | 347 | aceEditor.session.setUseWrapMode(true); |
344 | 348 | this.aceEditors.push(aceEditor); |
345 | - | |
346 | - const resizeListener = this.onAceEditorResize.bind(this, aceEditor); | |
347 | - | |
348 | - // @ts-ignore | |
349 | - addResizeListener(editorElement, resizeListener); | |
350 | - this.aceResizeListeners.push({element: editorElement, resizeListener}); | |
349 | + this.aceResize$.observe(editorElement); | |
351 | 350 | return aceEditor; |
352 | 351 | } |
353 | 352 | ... | ... |
... | ... | @@ -93,7 +93,6 @@ export class BreadcrumbComponent implements OnInit, OnDestroy { |
93 | 93 | const icon = breadcrumbConfig.icon || 'home'; |
94 | 94 | const isMdiIcon = icon.startsWith('mdi:'); |
95 | 95 | const link = [ route.pathFromRoot.map(v => v.url.map(segment => segment.toString()).join('/')).join('/') ]; |
96 | - const queryParams = route.queryParams; | |
97 | 96 | const breadcrumb = { |
98 | 97 | label, |
99 | 98 | labelFunction, |
... | ... | @@ -101,7 +100,7 @@ export class BreadcrumbComponent implements OnInit, OnDestroy { |
101 | 100 | icon, |
102 | 101 | isMdiIcon, |
103 | 102 | link, |
104 | - queryParams | |
103 | + queryParams: null | |
105 | 104 | }; |
106 | 105 | newBreadcrumbs = [...breadcrumbs, breadcrumb]; |
107 | 106 | } | ... | ... |
... | ... | @@ -29,6 +29,7 @@ import { |
29 | 29 | } from '@angular/core'; |
30 | 30 | import { WINDOW } from '@core/services/window.service'; |
31 | 31 | import { CanColorCtor, mixinColor } from '@angular/material/core'; |
32 | +import { ResizeObserver } from '@juggle/resize-observer'; | |
32 | 33 | |
33 | 34 | export declare type FabToolbarDirection = 'left' | 'right'; |
34 | 35 | |
... | ... | @@ -77,14 +78,14 @@ export class FabActionsDirective implements OnInit { |
77 | 78 | }) |
78 | 79 | export class FabToolbarComponent extends MatFabToolbarMixinBase implements OnInit, OnDestroy, AfterViewInit, OnChanges { |
79 | 80 | |
81 | + private fabToolbarResize$: ResizeObserver; | |
82 | + | |
80 | 83 | @Input() |
81 | 84 | isOpen: boolean; |
82 | 85 | |
83 | 86 | @Input() |
84 | 87 | direction: FabToolbarDirection; |
85 | 88 | |
86 | - fabToolbarResizeListener = this.onFabToolbarResize.bind(this); | |
87 | - | |
88 | 89 | constructor(private el: ElementRef<HTMLElement>, |
89 | 90 | @Inject(WINDOW) private window: Window) { |
90 | 91 | super(el); |
... | ... | @@ -96,13 +97,14 @@ export class FabToolbarComponent extends MatFabToolbarMixinBase implements OnIni |
96 | 97 | element.find('mat-fab-trigger').find('button') |
97 | 98 | .prepend('<div class="mat-fab-toolbar-background"></div>'); |
98 | 99 | element.addClass(`mat-${this.direction}`); |
99 | - // @ts-ignore | |
100 | - addResizeListener(this.el.nativeElement, this.fabToolbarResizeListener); | |
100 | + this.fabToolbarResize$ = new ResizeObserver(() => { | |
101 | + this.onFabToolbarResize(); | |
102 | + }); | |
103 | + this.fabToolbarResize$.observe(this.el.nativeElement); | |
101 | 104 | } |
102 | 105 | |
103 | 106 | ngOnDestroy(): void { |
104 | - // @ts-ignore | |
105 | - removeResizeListener(this.el.nativeElement, this.fabToolbarResizeListener); | |
107 | + this.fabToolbarResize$.disconnect(); | |
106 | 108 | } |
107 | 109 | |
108 | 110 | ngAfterViewInit(): void { | ... | ... |
... | ... | @@ -15,11 +15,11 @@ |
15 | 15 | /// |
16 | 16 | |
17 | 17 | import { |
18 | - ChangeDetectionStrategy, | |
19 | 18 | Component, |
20 | 19 | ElementRef, |
21 | 20 | forwardRef, |
22 | - Input, OnDestroy, | |
21 | + Input, | |
22 | + OnDestroy, | |
23 | 23 | OnInit, |
24 | 24 | ViewChild, |
25 | 25 | ViewEncapsulation |
... | ... | @@ -34,6 +34,7 @@ import { UtilsService } from '@core/services/utils.service'; |
34 | 34 | import { guid, isUndefined } from '@app/core/utils'; |
35 | 35 | import { TranslateService } from '@ngx-translate/core'; |
36 | 36 | import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; |
37 | +import { ResizeObserver } from '@juggle/resize-observer'; | |
37 | 38 | |
38 | 39 | @Component({ |
39 | 40 | selector: 'tb-js-func', |
... | ... | @@ -60,7 +61,7 @@ export class JsFuncComponent implements OnInit, OnDestroy, ControlValueAccessor, |
60 | 61 | |
61 | 62 | private jsEditor: ace.Ace.Editor; |
62 | 63 | private editorsResizeCaf: CancelAnimationFrame; |
63 | - private editorResizeListener: any; | |
64 | + private editorResize$: ResizeObserver; | |
64 | 65 | |
65 | 66 | toastTargetId = `jsFuncEditor-${guid()}`; |
66 | 67 | |
... | ... | @@ -152,16 +153,15 @@ export class JsFuncComponent implements OnInit, OnDestroy, ControlValueAccessor, |
152 | 153 | this.cleanupJsErrors(); |
153 | 154 | this.updateView(); |
154 | 155 | }); |
155 | - this.editorResizeListener = this.onAceEditorResize.bind(this); | |
156 | - // @ts-ignore | |
157 | - addResizeListener(editorElement, this.editorResizeListener); | |
156 | + this.editorResize$ = new ResizeObserver(() => { | |
157 | + this.onAceEditorResize(); | |
158 | + }); | |
159 | + this.editorResize$.observe(editorElement); | |
158 | 160 | } |
159 | 161 | |
160 | 162 | ngOnDestroy(): void { |
161 | - if (this.editorResizeListener) { | |
162 | - const editorElement = this.javascriptEditorElmRef.nativeElement; | |
163 | - // @ts-ignore | |
164 | - removeResizeListener(editorElement, this.editorResizeListener); | |
163 | + if (this.editorResize$) { | |
164 | + this.editorResize$.disconnect(); | |
165 | 165 | } |
166 | 166 | } |
167 | 167 | ... | ... |
... | ... | @@ -20,10 +20,10 @@ import { |
20 | 20 | forwardRef, |
21 | 21 | Input, |
22 | 22 | OnChanges, |
23 | + OnDestroy, | |
23 | 24 | OnInit, |
24 | - ViewChild, | |
25 | 25 | SimpleChanges, |
26 | - OnDestroy | |
26 | + ViewChild | |
27 | 27 | } from '@angular/core'; |
28 | 28 | import { ControlValueAccessor, FormControl, NG_VALIDATORS, NG_VALUE_ACCESSOR, Validator } from '@angular/forms'; |
29 | 29 | import * as ace from 'ace-builds'; |
... | ... | @@ -34,6 +34,7 @@ import { AppState } from '@core/core.state'; |
34 | 34 | import { ContentType, contentTypesMap } from '@shared/models/constants'; |
35 | 35 | import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; |
36 | 36 | import { guid } from '@core/utils'; |
37 | +import { ResizeObserver } from '@juggle/resize-observer'; | |
37 | 38 | |
38 | 39 | @Component({ |
39 | 40 | selector: 'tb-json-content', |
... | ... | @@ -59,7 +60,7 @@ export class JsonContentComponent implements OnInit, ControlValueAccessor, Valid |
59 | 60 | |
60 | 61 | private jsonEditor: ace.Ace.Editor; |
61 | 62 | private editorsResizeCaf: CancelAnimationFrame; |
62 | - private editorResizeListener: any; | |
63 | + private editorResize$: ResizeObserver; | |
63 | 64 | |
64 | 65 | toastTargetId = `jsonContentEditor-${guid()}`; |
65 | 66 | |
... | ... | @@ -97,8 +98,6 @@ export class JsonContentComponent implements OnInit, ControlValueAccessor, Valid |
97 | 98 | |
98 | 99 | contentValid: boolean; |
99 | 100 | |
100 | - validationError: string; | |
101 | - | |
102 | 101 | errorShowed = false; |
103 | 102 | |
104 | 103 | private propagateChange = null; |
... | ... | @@ -135,16 +134,15 @@ export class JsonContentComponent implements OnInit, ControlValueAccessor, Valid |
135 | 134 | this.cleanupJsonErrors(); |
136 | 135 | this.updateView(); |
137 | 136 | }); |
138 | - this.editorResizeListener = this.onAceEditorResize.bind(this); | |
139 | - // @ts-ignore | |
140 | - addResizeListener(editorElement, this.editorResizeListener); | |
137 | + this.editorResize$ = new ResizeObserver(() => { | |
138 | + this.onAceEditorResize(); | |
139 | + }); | |
140 | + this.editorResize$.observe(editorElement); | |
141 | 141 | } |
142 | 142 | |
143 | 143 | ngOnDestroy(): void { |
144 | - if (this.editorResizeListener) { | |
145 | - const editorElement = this.jsonEditorElmRef.nativeElement; | |
146 | - // @ts-ignore | |
147 | - removeResizeListener(editorElement, this.editorResizeListener); | |
144 | + if (this.editorResize$) { | |
145 | + this.editorResize$.disconnect(); | |
148 | 146 | } |
149 | 147 | } |
150 | 148 | ... | ... |
... | ... | @@ -15,7 +15,6 @@ |
15 | 15 | /// |
16 | 16 | |
17 | 17 | import { |
18 | - ChangeDetectionStrategy, | |
19 | 18 | Component, |
20 | 19 | ElementRef, |
21 | 20 | forwardRef, |
... | ... | @@ -106,6 +105,8 @@ export class JsonFormComponent implements OnInit, ControlValueAccessor, Validato |
106 | 105 | |
107 | 106 | private propagateChange = null; |
108 | 107 | private propagateChangePending = false; |
108 | + private writingValue = false; | |
109 | + private updateViewPending = false; | |
109 | 110 | |
110 | 111 | constructor(public elementRef: ElementRef, |
111 | 112 | private translate: TranslateService, |
... | ... | @@ -143,6 +144,7 @@ export class JsonFormComponent implements OnInit, ControlValueAccessor, Validato |
143 | 144 | } |
144 | 145 | |
145 | 146 | writeValue(data: JsonFormComponentData): void { |
147 | + this.writingValue = true; | |
146 | 148 | this.data = data; |
147 | 149 | this.schema = this.data && this.data.schema ? deepClone(this.data.schema) : { |
148 | 150 | type: 'object' |
... | ... | @@ -154,19 +156,29 @@ export class JsonFormComponent implements OnInit, ControlValueAccessor, Validato |
154 | 156 | this.model = inspector.sanitize(this.schema, this.model).data; |
155 | 157 | this.updateAndRender(); |
156 | 158 | this.isModelValid = this.validateModel(); |
157 | - if (!this.isModelValid) { | |
159 | + this.writingValue = false; | |
160 | + if (!this.isModelValid || this.updateViewPending) { | |
158 | 161 | this.updateView(); |
159 | 162 | } |
160 | 163 | } |
161 | 164 | |
162 | 165 | updateView() { |
163 | - if (this.data) { | |
164 | - this.data.model = this.model; | |
165 | - if (this.propagateChange) { | |
166 | - this.propagateChange(this.data); | |
167 | - } else { | |
168 | - this.propagateChangePending = true; | |
166 | + if (!this.writingValue) { | |
167 | + this.updateViewPending = false; | |
168 | + if (this.data) { | |
169 | + this.data.model = this.model; | |
170 | + if (this.propagateChange) { | |
171 | + try { | |
172 | + this.propagateChange(this.data); | |
173 | + } catch (e) { | |
174 | + this.propagateChangePending = true; | |
175 | + } | |
176 | + } else { | |
177 | + this.propagateChangePending = true; | |
178 | + } | |
169 | 179 | } |
180 | + } else { | |
181 | + this.updateViewPending = true; | |
170 | 182 | } |
171 | 183 | } |
172 | 184 | ... | ... |
... | ... | @@ -14,16 +14,8 @@ |
14 | 14 | /// limitations under the License. |
15 | 15 | /// |
16 | 16 | |
17 | -import { | |
18 | - Attribute, ChangeDetectionStrategy, | |
19 | - Component, | |
20 | - ElementRef, | |
21 | - forwardRef, | |
22 | - Input, OnDestroy, | |
23 | - OnInit, | |
24 | - ViewChild | |
25 | -} from '@angular/core'; | |
26 | -import { ControlValueAccessor, NG_VALUE_ACCESSOR, FormControl, Validator, NG_VALIDATORS } from '@angular/forms'; | |
17 | +import { Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; | |
18 | +import { ControlValueAccessor, FormControl, NG_VALIDATORS, NG_VALUE_ACCESSOR, Validator } from '@angular/forms'; | |
27 | 19 | import * as ace from 'ace-builds'; |
28 | 20 | import { coerceBooleanProperty } from '@angular/cdk/coercion'; |
29 | 21 | import { ActionNotificationHide, ActionNotificationShow } from '@core/notification/notification.actions'; |
... | ... | @@ -31,6 +23,7 @@ import { Store } from '@ngrx/store'; |
31 | 23 | import { AppState } from '@core/core.state'; |
32 | 24 | import { CancelAnimationFrame, RafService } from '@core/services/raf.service'; |
33 | 25 | import { guid } from '@core/utils'; |
26 | +import { ResizeObserver } from '@juggle/resize-observer'; | |
34 | 27 | |
35 | 28 | @Component({ |
36 | 29 | selector: 'tb-json-object-edit', |
... | ... | @@ -56,7 +49,7 @@ export class JsonObjectEditComponent implements OnInit, ControlValueAccessor, Va |
56 | 49 | |
57 | 50 | private jsonEditor: ace.Ace.Editor; |
58 | 51 | private editorsResizeCaf: CancelAnimationFrame; |
59 | - private editorResizeListener: any; | |
52 | + private editorResize$: ResizeObserver; | |
60 | 53 | |
61 | 54 | toastTargetId = `jsonObjectEditor-${guid()}`; |
62 | 55 | |
... | ... | @@ -128,16 +121,15 @@ export class JsonObjectEditComponent implements OnInit, ControlValueAccessor, Va |
128 | 121 | this.cleanupJsonErrors(); |
129 | 122 | this.updateView(); |
130 | 123 | }); |
131 | - this.editorResizeListener = this.onAceEditorResize.bind(this); | |
132 | - // @ts-ignore | |
133 | - addResizeListener(editorElement, this.editorResizeListener); | |
124 | + this.editorResize$ = new ResizeObserver(() => { | |
125 | + this.onAceEditorResize(); | |
126 | + }); | |
127 | + this.editorResize$.observe(editorElement); | |
134 | 128 | } |
135 | 129 | |
136 | 130 | ngOnDestroy(): void { |
137 | - if (this.editorResizeListener) { | |
138 | - const editorElement = this.jsonEditorElmRef.nativeElement; | |
139 | - // @ts-ignore | |
140 | - removeResizeListener(editorElement, this.editorResizeListener); | |
131 | + if (this.editorResize$) { | |
132 | + this.editorResize$.disconnect(); | |
141 | 133 | } |
142 | 134 | } |
143 | 135 | ... | ... |