Commit 516ae3d8142c4c554cd20499bc6a2774300b48eb
Committed by
GitHub
1 parent
25ba8139
Create ui to support attribute type JSON (#2471)
Showing
17 changed files
with
384 additions
and
14 deletions
... | ... | @@ -168,7 +168,7 @@ |
168 | 168 | class="tb-value-cell" |
169 | 169 | (click)="editAttribute($event, attribute)"> |
170 | 170 | <div fxLayout="row"> |
171 | - <span fxFlex>{{attribute.value}}</span> | |
171 | + <span fxFlex>{{attribute.value | tbJson}}</span> | |
172 | 172 | <span [fxShow]="!isClientSideTelemetryTypeMap.get(attributeScope)"> |
173 | 173 | <mat-icon>edit</mat-icon> |
174 | 174 | </span> | ... | ... |
1 | +<!-- | |
2 | + | |
3 | + Copyright © 2016-2020 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<form [formGroup]="jsonFormGroup" (ngSubmit)="add()" style="min-width: 400px;"> | |
19 | + <mat-toolbar fxLayout="row" color="primary"> | |
20 | + <h2>{{ (this.data.title ? this.data.title : 'details.edit-json') | translate }}</h2> | |
21 | + <span fxFlex></span> | |
22 | + <button mat-button mat-icon-button | |
23 | + (click)="cancel()" | |
24 | + type="button"> | |
25 | + <mat-icon class="material-icons">close</mat-icon> | |
26 | + </button> | |
27 | + </mat-toolbar> | |
28 | + <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async"> | |
29 | + </mat-progress-bar> | |
30 | + <div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div> | |
31 | + <div mat-dialog-content> | |
32 | + <fieldset [disabled]="isLoading$ | async"> | |
33 | + <tb-json-object-edit | |
34 | + formControlName="json" | |
35 | + label="{{ 'value.json-value' | translate }}" | |
36 | + validateContent="true" | |
37 | + [required]="true" | |
38 | + [fillHeight]="false"> | |
39 | + </tb-json-object-edit> | |
40 | + </fieldset> | |
41 | + </div> | |
42 | + <div mat-dialog-actions fxLayout="row" fxLayoutAlign="end center"> | |
43 | + <span fxFlex></span> | |
44 | + <button mat-button mat-raised-button color="primary" | |
45 | + type="submit" | |
46 | + [disabled]="(isLoading$ | async) || jsonFormGroup.invalid || !jsonFormGroup.dirty"> | |
47 | + {{ 'action.save' | translate }} | |
48 | + </button> | |
49 | + <button mat-button color="primary" | |
50 | + style="margin-right: 20px;" | |
51 | + type="button" | |
52 | + [disabled]="(isLoading$ | async)" | |
53 | + (click)="cancel()" cdkFocusInitial> | |
54 | + {{ 'action.cancel' | translate }} | |
55 | + </button> | |
56 | + </div> | |
57 | +</form> | ... | ... |
1 | +/// | |
2 | +/// Copyright © 2016-2020 The Thingsboard Authors | |
3 | +/// | |
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | +/// you may not use this file except in compliance with the License. | |
6 | +/// You may obtain a copy of the License at | |
7 | +/// | |
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | |
9 | +/// | |
10 | +/// Unless required by applicable law or agreed to in writing, software | |
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | |
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | +/// See the License for the specific language governing permissions and | |
14 | +/// limitations under the License. | |
15 | +/// | |
16 | + | |
17 | +import {Component, Inject, OnInit} from "@angular/core"; | |
18 | +import {DialogComponent} from "@shared/components/dialog.component"; | |
19 | +import {Store} from "@ngrx/store"; | |
20 | +import {AppState} from "@core/core.state"; | |
21 | +import {Router} from "@angular/router"; | |
22 | +import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog"; | |
23 | +import {FormBuilder, FormGroup} from "@angular/forms"; | |
24 | + | |
25 | +export interface JsonObjectEdittDialogData { | |
26 | + jsonValue: Object; | |
27 | + title?: string; | |
28 | +} | |
29 | + | |
30 | +@Component({ | |
31 | + selector: 'tb-object-edit-dialog', | |
32 | + templateUrl: './json-object-edit-dialog.component.html', | |
33 | + styleUrls: [] | |
34 | +}) | |
35 | +export class JsonObjectEditDialogComponent extends DialogComponent<JsonObjectEditDialogComponent, Object> | |
36 | + implements OnInit { | |
37 | + | |
38 | + jsonFormGroup: FormGroup; | |
39 | + | |
40 | + submitted = false; | |
41 | + | |
42 | + constructor(protected store: Store<AppState>, | |
43 | + protected router: Router, | |
44 | + @Inject(MAT_DIALOG_DATA) public data: JsonObjectEdittDialogData, | |
45 | + public dialogRef: MatDialogRef<JsonObjectEditDialogComponent, Object>, | |
46 | + public fb: FormBuilder) { | |
47 | + super(store, router, dialogRef); | |
48 | + } | |
49 | + | |
50 | + ngOnInit(): void { | |
51 | + this.jsonFormGroup = this.fb.group({ | |
52 | + json: [this.data.jsonValue, []] | |
53 | + }); | |
54 | + } | |
55 | + | |
56 | + cancel(): void { | |
57 | + this.dialogRef.close(undefined); | |
58 | + } | |
59 | + | |
60 | + add(): void { | |
61 | + this.dialogRef.close(this.jsonFormGroup.get('json').value); | |
62 | + } | |
63 | +} | ... | ... |
1 | +/// | |
2 | +/// Copyright © 2016-2020 The Thingsboard Authors | |
3 | +/// | |
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | +/// you may not use this file except in compliance with the License. | |
6 | +/// You may obtain a copy of the License at | |
7 | +/// | |
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | |
9 | +/// | |
10 | +/// Unless required by applicable law or agreed to in writing, software | |
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | |
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | +/// See the License for the specific language governing permissions and | |
14 | +/// limitations under the License. | |
15 | +/// | |
16 | + | |
17 | +import {Directive, ElementRef, forwardRef, HostListener, Renderer2, SkipSelf} from "@angular/core"; | |
18 | +import { | |
19 | + ControlValueAccessor, | |
20 | + FormControl, FormGroupDirective, | |
21 | + NG_VALIDATORS, | |
22 | + NG_VALUE_ACCESSOR, NgForm, | |
23 | + ValidationErrors, | |
24 | + Validator | |
25 | +} from "@angular/forms"; | |
26 | +import {ErrorStateMatcher} from "@angular/material/core"; | |
27 | + | |
28 | +@Directive({ | |
29 | + selector: '[tb-json-to-string]', | |
30 | + providers: [{ | |
31 | + provide: NG_VALUE_ACCESSOR, | |
32 | + useExisting: forwardRef(() => TbJsonToStringDirective), | |
33 | + multi: true | |
34 | + }, | |
35 | + { | |
36 | + provide: NG_VALIDATORS, | |
37 | + useExisting: forwardRef(() => TbJsonToStringDirective), | |
38 | + multi: true, | |
39 | + }, | |
40 | + { | |
41 | + provide: ErrorStateMatcher, | |
42 | + useExisting: TbJsonToStringDirective | |
43 | + }] | |
44 | +}) | |
45 | + | |
46 | +export class TbJsonToStringDirective implements ControlValueAccessor, Validator, ErrorStateMatcher { | |
47 | + private propagateChange = null; | |
48 | + private parseError: boolean; | |
49 | + private data: any; | |
50 | + | |
51 | + @HostListener('input', ['$event.target.value']) input(newValue: any): void { | |
52 | + try { | |
53 | + this.data = JSON.parse(newValue); | |
54 | + this.parseError = false; | |
55 | + } catch (e) { | |
56 | + this.parseError = true; | |
57 | + } | |
58 | + | |
59 | + this.propagateChange(this.data); | |
60 | + } | |
61 | + | |
62 | + constructor(private render: Renderer2, | |
63 | + private element: ElementRef, | |
64 | + @SkipSelf() private errorStateMatcher: ErrorStateMatcher) { | |
65 | + | |
66 | + } | |
67 | + | |
68 | + isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean { | |
69 | + const originalErrorState = this.errorStateMatcher.isErrorState(control, form); | |
70 | + const customErrorState = !!(control && control.invalid && this.parseError); | |
71 | + return originalErrorState || customErrorState; | |
72 | + } | |
73 | + | |
74 | + validate(c: FormControl): ValidationErrors { | |
75 | + return (!this.parseError) ? null : { | |
76 | + invalidJSON: { | |
77 | + valid: false | |
78 | + } | |
79 | + }; | |
80 | + } | |
81 | + | |
82 | + writeValue(obj: any): void { | |
83 | + if (obj) { | |
84 | + this.data = obj; | |
85 | + this.parseError = false; | |
86 | + this.render.setProperty(this.element.nativeElement, 'value', JSON.stringify(obj)); | |
87 | + } | |
88 | + } | |
89 | + | |
90 | + registerOnChange(fn: any): void { | |
91 | + this.propagateChange = fn; | |
92 | + } | |
93 | + | |
94 | + registerOnTouched(fn: any): void { | |
95 | + } | |
96 | +} | ... | ... |
... | ... | @@ -22,9 +22,13 @@ |
22 | 22 | <label class="tb-title no-padding">{{ label }}</label> |
23 | 23 | <span fxFlex></span> |
24 | 24 | <button type="button" |
25 | - mat-button *ngIf="!readonly" class="tidy" (click)="beautifyJson()"> | |
25 | + mat-button *ngIf="!readonly" class="tidy" (click)="beautifyJSON()"> | |
26 | 26 | {{'js-func.tidy' | translate }} |
27 | 27 | </button> |
28 | + <button type="button" | |
29 | + mat-button *ngIf="!readonly" class="tidy" (click)="minifyJSON()"> | |
30 | + {{'js-func.mini' | translate }} | |
31 | + </button> | |
28 | 32 | <button type='button' mat-button mat-icon-button (click)="fullscreen = !fullscreen" |
29 | 33 | class="tb-mat-32" |
30 | 34 | matTooltip="{{(fullscreen ? 'fullscreen.exit' : 'fullscreen.expand') | translate}}" | ... | ... |
... | ... | @@ -257,12 +257,18 @@ export class JsonContentComponent implements OnInit, ControlValueAccessor, Valid |
257 | 257 | } |
258 | 258 | } |
259 | 259 | |
260 | - beautifyJson() { | |
260 | + beautifyJSON() { | |
261 | 261 | const res = js_beautify(this.contentBody, {indent_size: 4, wrap_line_length: 60}); |
262 | 262 | this.jsonEditor.setValue(res ? res : '', -1); |
263 | 263 | this.updateView(); |
264 | 264 | } |
265 | 265 | |
266 | + minifyJSON() { | |
267 | + const res = JSON.stringify(this.contentBody); | |
268 | + this.jsonEditor.setValue(res ? res : '', -1); | |
269 | + this.updateView(); | |
270 | + } | |
271 | + | |
266 | 272 | onFullscreen() { |
267 | 273 | if (this.jsonEditor) { |
268 | 274 | setTimeout(() => { | ... | ... |
... | ... | @@ -18,12 +18,20 @@ |
18 | 18 | <div style="background: #fff;" [ngClass]="{'fill-height': fillHeight}" |
19 | 19 | tb-fullscreen |
20 | 20 | [fullscreen]="fullscreen" (fullscreenChanged)="onFullscreen()" fxLayout="column"> |
21 | - <div fxLayout="row" fxLayoutAlign="start center"> | |
21 | + <div fxLayout="row" fxLayoutAlign="start center" class="tb-json-object-toolbar"> | |
22 | 22 | <label class="tb-title no-padding" |
23 | 23 | ng-class="{'tb-required': required, |
24 | 24 | 'tb-readonly': readonly, |
25 | 25 | 'tb-error': !objectValid}">{{ label }}</label> |
26 | 26 | <span fxFlex></span> |
27 | + <button type="button" | |
28 | + mat-button *ngIf="!readonly" class="tidy" (click)="beautifyJSON()"> | |
29 | + {{'js-func.tidy' | translate }} | |
30 | + </button> | |
31 | + <button type="button" | |
32 | + mat-button *ngIf="!readonly" class="tidy" (click)="minifyJSON()"> | |
33 | + {{'js-func.mini' | translate }} | |
34 | + </button> | |
27 | 35 | <button mat-button mat-icon-button (click)="fullscreen = !fullscreen" |
28 | 36 | matTooltip="{{(fullscreen ? 'fullscreen.exit' : 'fullscreen.expand') | translate}}" |
29 | 37 | matTooltipPosition="above"> | ... | ... |
... | ... | @@ -21,6 +21,25 @@ |
21 | 21 | } |
22 | 22 | } |
23 | 23 | |
24 | +.tb-json-object-toolbar { | |
25 | + button.mat-button, button.mat-icon-button, button.mat-icon-button.tb-mat-32 { | |
26 | + align-items: center; | |
27 | + vertical-align: middle; | |
28 | + min-width: 32px; | |
29 | + min-height: 15px; | |
30 | + padding: 4px; | |
31 | + margin: 0; | |
32 | + font-size: .8rem; | |
33 | + line-height: 15px; | |
34 | + color: #7b7b7b; | |
35 | + background: rgba(220, 220, 220, .35); | |
36 | + | |
37 | + &:not(:last-child) { | |
38 | + margin-right: 4px; | |
39 | + } | |
40 | + } | |
41 | +} | |
42 | + | |
24 | 43 | .tb-json-object-panel { |
25 | 44 | height: 100%; |
26 | 45 | margin-left: 15px; | ... | ... |
... | ... | @@ -195,6 +195,22 @@ export class JsonObjectEditComponent implements OnInit, ControlValueAccessor, Va |
195 | 195 | } |
196 | 196 | } |
197 | 197 | |
198 | + beautifyJSON() { | |
199 | + const res = JSON.stringify(this.modelValue, null, 2); | |
200 | + if (this.jsonEditor) { | |
201 | + this.jsonEditor.setValue(res ? res : '', -1); | |
202 | + } | |
203 | + this.updateView(); | |
204 | + } | |
205 | + | |
206 | + minifyJSON() { | |
207 | + const res = JSON.stringify(this.modelValue); | |
208 | + if (this.jsonEditor) { | |
209 | + this.jsonEditor.setValue(res ? res : '', -1); | |
210 | + } | |
211 | + this.updateView(); | |
212 | + } | |
213 | + | |
198 | 214 | writeValue(value: any): void { |
199 | 215 | this.modelValue = value; |
200 | 216 | this.contentValue = ''; | ... | ... |
... | ... | @@ -59,5 +59,21 @@ |
59 | 59 | {{ (modelValue ? 'value.true' : 'value.false') | translate }} |
60 | 60 | </mat-checkbox> |
61 | 61 | </div> |
62 | + <div fxLayout="row" fxLayoutAlign="center" fxFlex="60" *ngIf="valueType === valueTypeEnum.JSON" class="mat-block"> | |
63 | + <mat-form-field fxFlex class="mat-block"> | |
64 | + <mat-label translate>value.json-value</mat-label> | |
65 | + <input [disabled]="disabled" matInput tb-json-to-string required name="value" #value="ngModel" | |
66 | + [(ngModel)]="modelValue" (ngModelChange)="onValueChanged()"/> | |
67 | + <button matSuffix mat-button mat-icon-button (click)="openEditJSONDialog($event)"> | |
68 | + <mat-icon>open_in_new</mat-icon> | |
69 | + </button> | |
70 | + <mat-error *ngIf="value.hasError('required')"> | |
71 | + {{ (requiredText ? requiredText : 'value.json-value-required') | translate }} | |
72 | + </mat-error> | |
73 | + <mat-error *ngIf="value.hasError('invalidJSON')"> | |
74 | + {{ 'value.json-value-invalid' | translate }} | |
75 | + </mat-error> | |
76 | + </mat-form-field> | |
77 | + </div> | |
62 | 78 | </section> |
63 | 79 | </form> | ... | ... |
... | ... | @@ -17,6 +17,12 @@ |
17 | 17 | import { Component, forwardRef, Input, OnInit, ViewChild } from '@angular/core'; |
18 | 18 | import { ControlValueAccessor, NG_VALUE_ACCESSOR, NgForm } from '@angular/forms'; |
19 | 19 | import { ValueType, valueTypesMap } from '@shared/models/constants'; |
20 | +import { isObject } from "@core/utils"; | |
21 | +import { MatDialog } from "@angular/material/dialog"; | |
22 | +import { | |
23 | + JsonObjectEditDialogComponent, | |
24 | + JsonObjectEdittDialogData | |
25 | +} from "@shared/components/dialog/json-object-edit-dialog.component"; | |
20 | 26 | |
21 | 27 | @Component({ |
22 | 28 | selector: 'tb-value-input', |
... | ... | @@ -50,13 +56,35 @@ export class ValueInputComponent implements OnInit, ControlValueAccessor { |
50 | 56 | |
51 | 57 | private propagateChange = null; |
52 | 58 | |
53 | - constructor() { | |
59 | + constructor( | |
60 | + public dialog: MatDialog, | |
61 | + ) { | |
54 | 62 | |
55 | 63 | } |
56 | 64 | |
57 | 65 | ngOnInit(): void { |
58 | 66 | } |
59 | 67 | |
68 | + openEditJSONDialog($event: Event) { | |
69 | + if ($event) { | |
70 | + $event.stopPropagation(); | |
71 | + } | |
72 | + this.dialog.open<JsonObjectEditDialogComponent, JsonObjectEdittDialogData, Object>(JsonObjectEditDialogComponent, { | |
73 | + disableClose: true, | |
74 | + panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], | |
75 | + data: { | |
76 | + jsonValue: this.modelValue | |
77 | + } | |
78 | + }).afterClosed().subscribe( | |
79 | + (res) => { | |
80 | + if (res) { | |
81 | + this.modelValue = res; | |
82 | + this.inputForm.control.patchValue({'value': this.modelValue}); | |
83 | + } | |
84 | + } | |
85 | + ); | |
86 | + } | |
87 | + | |
60 | 88 | registerOnChange(fn: any): void { |
61 | 89 | this.propagateChange = fn; |
62 | 90 | } |
... | ... | @@ -78,6 +106,8 @@ export class ValueInputComponent implements OnInit, ControlValueAccessor { |
78 | 106 | } else { |
79 | 107 | this.valueType = ValueType.DOUBLE; |
80 | 108 | } |
109 | + } else if (isObject(this.modelValue)) { | |
110 | + this.valueType = ValueType.JSON; | |
81 | 111 | } else { |
82 | 112 | this.valueType = ValueType.STRING; |
83 | 113 | } |
... | ... | @@ -94,6 +124,8 @@ export class ValueInputComponent implements OnInit, ControlValueAccessor { |
94 | 124 | onValueTypeChanged() { |
95 | 125 | if (this.valueType === ValueType.BOOLEAN) { |
96 | 126 | this.modelValue = false; |
127 | + } if (this.valueType === ValueType.JSON) { | |
128 | + this.modelValue = {}; | |
97 | 129 | } else { |
98 | 130 | this.modelValue = null; |
99 | 131 | } | ... | ... |
... | ... | @@ -121,7 +121,8 @@ export enum ValueType { |
121 | 121 | STRING = 'STRING', |
122 | 122 | INTEGER = 'INTEGER', |
123 | 123 | DOUBLE = 'DOUBLE', |
124 | - BOOLEAN = 'BOOLEAN' | |
124 | + BOOLEAN = 'BOOLEAN', | |
125 | + JSON = 'JSON' | |
125 | 126 | } |
126 | 127 | |
127 | 128 | export const valueTypesMap = new Map<ValueType, ValueTypeData>( |
... | ... | @@ -153,6 +154,13 @@ export const valueTypesMap = new Map<ValueType, ValueTypeData>( |
153 | 154 | name: 'value.boolean', |
154 | 155 | icon: 'mdi:checkbox-marked-outline' |
155 | 156 | } |
157 | + ], | |
158 | + [ | |
159 | + ValueType.JSON, | |
160 | + { | |
161 | + name: 'value.json', | |
162 | + icon: 'mdi:json' | |
163 | + } | |
156 | 164 | ] |
157 | 165 | ] |
158 | 166 | ); | ... | ... |
ui-ngx/src/app/shared/pipe/tbJson.pipe.ts
0 → 100644
1 | +/// | |
2 | +/// Copyright © 2016-2020 The Thingsboard Authors | |
3 | +/// | |
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | +/// you may not use this file except in compliance with the License. | |
6 | +/// You may obtain a copy of the License at | |
7 | +/// | |
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | |
9 | +/// | |
10 | +/// Unless required by applicable law or agreed to in writing, software | |
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | |
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | +/// See the License for the specific language governing permissions and | |
14 | +/// limitations under the License. | |
15 | +/// | |
16 | + | |
17 | +import {Pipe, PipeTransform} from '@angular/core'; | |
18 | +import {isObject, isNumber} from "@core/utils"; | |
19 | + | |
20 | +@Pipe({name: 'tbJson'}) | |
21 | +export class TbJsonPipe implements PipeTransform { | |
22 | + transform(value: any): string { | |
23 | + if (isObject(value)) { | |
24 | + return JSON.stringify(value); | |
25 | + } else if (isNumber(value)) { | |
26 | + return value.toString(); | |
27 | + } | |
28 | + return value; | |
29 | + } | |
30 | +} | ... | ... |
... | ... | @@ -104,6 +104,7 @@ import { TbErrorComponent } from '@shared/components/tb-error.component'; |
104 | 104 | import { EntityTypeListComponent } from '@shared/components/entity/entity-type-list.component'; |
105 | 105 | import { EntitySubTypeListComponent } from '@shared/components/entity/entity-subtype-list.component'; |
106 | 106 | import { TruncatePipe } from '@shared/pipe/truncate.pipe'; |
107 | +import { TbJsonPipe } from "@shared/pipe/tbJson.pipe"; | |
107 | 108 | import { ColorPickerDialogComponent } from '@shared/components/dialog/color-picker-dialog.component'; |
108 | 109 | import { MatChipDraggableDirective } from '@shared/components/mat-chip-draggable.directive'; |
109 | 110 | import { ColorInputComponent } from '@shared/components/color-input.component'; |
... | ... | @@ -124,6 +125,8 @@ import { TbCheatSheetComponent } from '@shared/components/cheatsheet.component'; |
124 | 125 | import { TbHotkeysDirective } from '@shared/components/hotkeys.directive'; |
125 | 126 | import { NavTreeComponent } from '@shared/components/nav-tree.component'; |
126 | 127 | import { LedLightComponent } from '@shared/components/led-light.component'; |
128 | +import { TbJsonToStringDirective } from "@shared/components/directives/tb-json-to-string.directive"; | |
129 | +import { JsonObjectEditDialogComponent } from "@shared/components/dialog/json-object-edit-dialog.component"; | |
127 | 130 | |
128 | 131 | @NgModule({ |
129 | 132 | providers: [ |
... | ... | @@ -132,6 +135,7 @@ import { LedLightComponent } from '@shared/components/led-light.component'; |
132 | 135 | EnumToArrayPipe, |
133 | 136 | HighlightPipe, |
134 | 137 | TruncatePipe, |
138 | + TbJsonPipe, | |
135 | 139 | { |
136 | 140 | provide: FlowInjectionToken, |
137 | 141 | useValue: Flow |
... | ... | @@ -202,7 +206,10 @@ import { LedLightComponent } from '@shared/components/led-light.component'; |
202 | 206 | EnumToArrayPipe, |
203 | 207 | HighlightPipe, |
204 | 208 | TruncatePipe, |
205 | - KeyboardShortcutPipe | |
209 | + TbJsonPipe, | |
210 | + KeyboardShortcutPipe, | |
211 | + TbJsonToStringDirective, | |
212 | + JsonObjectEditDialogComponent | |
206 | 213 | ], |
207 | 214 | imports: [ |
208 | 215 | CommonModule, |
... | ... | @@ -357,8 +364,10 @@ import { LedLightComponent } from '@shared/components/led-light.component'; |
357 | 364 | EnumToArrayPipe, |
358 | 365 | HighlightPipe, |
359 | 366 | TruncatePipe, |
367 | + TbJsonPipe, | |
360 | 368 | KeyboardShortcutPipe, |
361 | - TranslateModule | |
369 | + TranslateModule, | |
370 | + JsonObjectEditDialogComponent | |
362 | 371 | ] |
363 | 372 | }) |
364 | 373 | export class SharedModule { } | ... | ... |
... | ... | @@ -626,6 +626,7 @@ |
626 | 626 | "details": { |
627 | 627 | "details": "Details", |
628 | 628 | "edit-mode": "Edit mode", |
629 | + "edit-json": "Edit JSON", | |
629 | 630 | "toggle-edit-mode": "Toggle edit mode" |
630 | 631 | }, |
631 | 632 | "device": { |
... | ... | @@ -1298,7 +1299,8 @@ |
1298 | 1299 | "js-func": { |
1299 | 1300 | "no-return-error": "Function must return value!", |
1300 | 1301 | "return-type-mismatch": "Function must return value of '{{type}}' type!", |
1301 | - "tidy": "Tidy" | |
1302 | + "tidy": "Tidy", | |
1303 | + "mini": "Mini" | |
1302 | 1304 | }, |
1303 | 1305 | "key-val": { |
1304 | 1306 | "key": "Key", |
... | ... | @@ -1635,7 +1637,11 @@ |
1635 | 1637 | "boolean-value": "Boolean value", |
1636 | 1638 | "false": "False", |
1637 | 1639 | "true": "True", |
1638 | - "long": "Long" | |
1640 | + "long": "Long", | |
1641 | + "json": "JSON", | |
1642 | + "json-value": "JSON value", | |
1643 | + "json-value-invalid": "JSON value has an invalid format", | |
1644 | + "json-value-required": "JSON value is required." | |
1639 | 1645 | }, |
1640 | 1646 | "widget": { |
1641 | 1647 | "widget-library": "Widgets Library", | ... | ... |
... | ... | @@ -607,6 +607,7 @@ |
607 | 607 | }, |
608 | 608 | "details": { |
609 | 609 | "edit-mode": "Режим редактирования", |
610 | + "edit-json": "Редактировать JSON", | |
610 | 611 | "toggle-edit-mode": "Режим редактирования" |
611 | 612 | }, |
612 | 613 | "device": { |
... | ... | @@ -1191,8 +1192,7 @@ |
1191 | 1192 | }, |
1192 | 1193 | "js-func": { |
1193 | 1194 | "no-return-error": "Функция должна возвращать значение!", |
1194 | - "return-type-mismatch": "Функция должна возвращать значение типа '{{type}}'!", | |
1195 | - "tidy": "Tidy" | |
1195 | + "return-type-mismatch": "Функция должна возвращать значение типа '{{type}}'!" | |
1196 | 1196 | }, |
1197 | 1197 | "key-val": { |
1198 | 1198 | "key": "Ключ", | ... | ... |
... | ... | @@ -724,6 +724,7 @@ |
724 | 724 | "details": { |
725 | 725 | "details": "Деталі", |
726 | 726 | "edit-mode": "Режим редагування", |
727 | + "edit-json": "Редагувати JSON", | |
727 | 728 | "toggle-edit-mode": "Перемкнути режим редагування" |
728 | 729 | }, |
729 | 730 | "device": { |
... | ... | @@ -1606,8 +1607,7 @@ |
1606 | 1607 | }, |
1607 | 1608 | "js-func": { |
1608 | 1609 | "no-return-error": "Функція повинна повертати значення!", |
1609 | - "return-type-mismatch": "Функція повинна повернути значення типу '{{type}}'!", | |
1610 | - "tidy": "Tidy" | |
1610 | + "return-type-mismatch": "Функція повинна повернути значення типу '{{type}}'!" | |
1611 | 1611 | }, |
1612 | 1612 | "key-val": { |
1613 | 1613 | "key": "Ключ", | ... | ... |