Browse Source

UI add rule-chain autocompleted

pull/3477/head
Vladyslav_Prykhodko 6 years ago
parent
commit
70ca190f59
  1. 2
      application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java
  2. 6
      ui-ngx/src/app/core/http/rule-chain.service.ts
  3. 7
      ui-ngx/src/app/modules/home/components/home-components.module.ts
  4. 5
      ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html
  5. 57
      ui-ngx/src/app/modules/home/components/rule-chain/rule-chain-autocomplete.component.html
  6. 206
      ui-ngx/src/app/modules/home/components/rule-chain/rule-chain-autocomplete.component.ts

2
application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java

@ -85,7 +85,7 @@ public class InstallScripts {
}
public Path getDeviceProfileDefaultRuleChainTemplateFilePath() {
return Paths.get(getDataDir(), JSON_DIR, DEVICE_PROFILE_DIR, "rule_chain_template.json");
return Paths.get(getDataDir(), JSON_DIR, TENANT_DIR, DEVICE_PROFILE_DIR, "rule_chain_template.json");
}
public String getDataDir() {

6
ui-ngx/src/app/core/http/rule-chain.service.ts

@ -68,6 +68,12 @@ export class RuleChainService {
return this.http.get<RuleChain>(`/api/ruleChain/${ruleChainId}`, defaultHttpOptionsFromConfig(config));
}
public createDefaultRuleChain(ruleChainName: string, config?: RequestConfig): Observable<RuleChain> {
return this.http.post<RuleChain>('/api/ruleChain/device/default', {
name: ruleChainName
}, defaultHttpOptionsFromConfig(config));
}
public saveRuleChain(ruleChain: RuleChain, config?: RequestConfig): Observable<RuleChain> {
return this.http.post<RuleChain>('/api/ruleChain', ruleChain, defaultHttpOptionsFromConfig(config));
}

7
ui-ngx/src/app/modules/home/components/home-components.module.ts

@ -106,6 +106,7 @@ import { AlarmRuleConditionComponent } from './profile/alarm/alarm-rule-conditio
import { AlarmRuleKeyFiltersDialogComponent } from './profile/alarm/alarm-rule-key-filters-dialog.component';
import { FilterTextComponent } from './filter/filter-text.component';
import { AddDeviceProfileDialogComponent } from './profile/add-device-profile-dialog.component';
import { RuleChainAutocompleteComponent } from './rule-chain/rule-chain-autocomplete.component';
@NgModule({
declarations:
@ -194,7 +195,8 @@ import { AddDeviceProfileDialogComponent } from './profile/add-device-profile-di
DeviceProfileDataComponent,
DeviceProfileComponent,
DeviceProfileDialogComponent,
AddDeviceProfileDialogComponent
AddDeviceProfileDialogComponent,
RuleChainAutocompleteComponent
],
imports: [
CommonModule,
@ -272,7 +274,8 @@ import { AddDeviceProfileDialogComponent } from './profile/add-device-profile-di
DeviceProfileDataComponent,
DeviceProfileComponent,
DeviceProfileDialogComponent,
AddDeviceProfileDialogComponent
AddDeviceProfileDialogComponent,
RuleChainAutocompleteComponent
],
providers: [
WidgetComponentService,

5
ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html

@ -41,11 +41,10 @@
{{ 'device-profile.name-required' | translate }}
</mat-error>
</mat-form-field>
<tb-entity-autocomplete
<tb-rule-chain-autocomplete
labelText="device-profile.default-rule-chain"
[entityType]="entityType.RULE_CHAIN"
formControlName="defaultRuleChainId">
</tb-entity-autocomplete>
</tb-rule-chain-autocomplete>
<mat-form-field class="mat-block">
<mat-label translate>device-profile.type</mat-label>
<mat-select formControlName="type" required>

57
ui-ngx/src/app/modules/home/components/rule-chain/rule-chain-autocomplete.component.html

@ -0,0 +1,57 @@
<!--
Copyright © 2016-2020 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<mat-form-field [formGroup]="selectRuleChainFormGroup" class="mat-block">
<input matInput type="text" placeholder="{{ ruleChainLabel | translate }}"
#entityInput
formControlName="ruleChainId"
(focusin)="onFocus()"
[required]="required"
[matAutocomplete]="entityAutocomplete">
<button *ngIf="selectRuleChainFormGroup.get('ruleChainId').value && !disabled"
type="button"
matSuffix mat-button mat-icon-button aria-label="Clear"
(click)="clear()">
<mat-icon class="material-icons">close</mat-icon>
</button>
<mat-autocomplete class="tb-autocomplete"
#entityAutocomplete="matAutocomplete"
[displayWith]="displayEntityFn">
<mat-option *ngFor="let entity of filteredRuleChains | async" [value]="entity">
<span [innerHTML]="entity.name | highlight:searchText"></span>
</mat-option>
<mat-option *ngIf="!(filteredRuleChains | async)?.length" [value]="null" class="tb-not-found">
<div class="tb-not-found-content" (click)="$event.stopPropagation()">
<div *ngIf="!textIsNotEmpty(searchText); else searchNotEmpty">
<span translate>device-profile.no-device-profiles-found</span>
</div>
<ng-template #searchNotEmpty>
<span>
{{ translate.get('rulechain.no-rulechains-matching',
{entity: truncate.transform(searchText, true, 6, &apos;...&apos;)}) | async }}
</span>
</ng-template>
<span>
<a translate (click)="createDefaultRuleChain($event, searchText)">rulechain.create-new-rulechain</a>
</span>
</div>
</mat-option>
</mat-autocomplete>
<mat-error *ngIf="selectRuleChainFormGroup.get('ruleChainId').hasError('required')">
{{ 'rulechain.rulechain-required' | translate }}
</mat-error>
</mat-form-field>

206
ui-ngx/src/app/modules/home/components/rule-chain/rule-chain-autocomplete.component.ts

@ -0,0 +1,206 @@
///
/// Copyright © 2016-2020 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
import { Observable } from 'rxjs';
import { map, mergeMap, share, tap } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { TranslateService } from '@ngx-translate/core';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { EntityId } from '@shared/models/id/entity-id';
import { EntityType } from '@shared/models/entity-type.models';
import { BaseData } from '@shared/models/base-data';
import { EntityService } from '@core/http/entity.service';
import { TruncatePipe } from '@shared/pipe/truncate.pipe';
import { RuleChainService } from '@core/http/rule-chain.service';
import { MatAutocompleteTrigger } from '@angular/material/autocomplete';
@Component({
selector: 'tb-rule-chain-autocomplete',
templateUrl: './rule-chain-autocomplete.component.html',
styleUrls: [],
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => RuleChainAutocompleteComponent),
multi: true
}]
})
export class RuleChainAutocompleteComponent implements ControlValueAccessor, OnInit {
selectRuleChainFormGroup: FormGroup;
ruleChainLabel = 'rulechain.rulechain';
modelValue: string | null;
@Input()
labelText: string;
@Input()
requiredText: string;
private requiredValue: boolean;
get required(): boolean {
return this.requiredValue;
}
@Input()
set required(value: boolean) {
this.requiredValue = coerceBooleanProperty(value);
}
@Input()
disabled: boolean;
@ViewChild('entityInput', {static: true}) entityInput: ElementRef;
filteredRuleChains: Observable<Array<BaseData<EntityId>>>;
searchText = '';
private dirty = false;
private propagateChange = (v: any) => { };
constructor(private store: Store<AppState>,
public translate: TranslateService,
public truncate: TruncatePipe,
private entityService: EntityService,
private ruleChainService: RuleChainService,
private fb: FormBuilder) {
this.selectRuleChainFormGroup = this.fb.group({
ruleChainId: [null]
});
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {
}
ngOnInit() {
this.filteredRuleChains = this.selectRuleChainFormGroup.get('ruleChainId').valueChanges
.pipe(
tap(value => {
let modelValue;
if (typeof value === 'string' || !value) {
modelValue = null;
} else {
modelValue = value.id.id;
}
this.updateView(modelValue);
if (value === null) {
this.clear();
}
}), map(value => value ? (typeof value === 'string' ? value : value.name) : ''),
mergeMap(name => this.fetchRuleChain(name) ),
share()
);
}
ngAfterViewInit(): void {}
getCurrentEntity(): BaseData<EntityId> | null {
const currentEntity = this.selectRuleChainFormGroup.get('ruleChainId').value;
if (currentEntity && typeof currentEntity !== 'string') {
return currentEntity as BaseData<EntityId>;
} else {
return null;
}
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (this.disabled) {
this.selectRuleChainFormGroup.disable({emitEvent: false});
} else {
this.selectRuleChainFormGroup.enable({emitEvent: false});
}
}
textIsNotEmpty(text: string): boolean {
return (text && text.length > 0);
}
writeValue(value: string | null): void {
this.searchText = '';
if (value != null) {
const targetEntityType = EntityType.RULE_CHAIN;
this.entityService.getEntity(targetEntityType, value, {ignoreLoading: true, ignoreErrors: true}).subscribe(
(entity) => {
this.modelValue = entity.id.id;
this.selectRuleChainFormGroup.get('ruleChainId').patchValue(entity, {emitEvent: false});
},
() => {
this.modelValue = null;
this.selectRuleChainFormGroup.get('ruleChainId').patchValue('', {emitEvent: false});
if (value !== null) {
this.propagateChange(this.modelValue);
}
}
);
} else {
this.modelValue = null;
this.selectRuleChainFormGroup.get('ruleChainId').patchValue('', {emitEvent: false});
}
this.dirty = true;
}
onFocus() {
if (this.dirty) {
this.selectRuleChainFormGroup.get('ruleChainId').updateValueAndValidity({onlySelf: true, emitEvent: true});
this.dirty = false;
}
}
reset() {
this.selectRuleChainFormGroup.get('ruleChainId').patchValue('', {emitEvent: false});
}
updateView(value: string | null) {
if (this.modelValue !== value) {
this.modelValue = value;
this.propagateChange(this.modelValue);
}
}
displayEntityFn(entity?: BaseData<EntityId>): string | undefined {
return entity ? entity.name : undefined;
}
fetchRuleChain(searchText?: string): Observable<Array<BaseData<EntityId>>> {
this.searchText = searchText;
return this.entityService.getEntitiesByNameFilter(EntityType.RULE_CHAIN, searchText,
50, null, {ignoreLoading: true});
}
clear() {
this.selectRuleChainFormGroup.get('ruleChainId').patchValue('', {emitEvent: true});
setTimeout(() => {
this.entityInput.nativeElement.blur();
this.entityInput.nativeElement.focus();
}, 0);
}
createDefaultRuleChain($event: Event, ruleChainName: string) {
this.ruleChainService.createDefaultRuleChain(ruleChainName).subscribe((ruleChain) => {
this.updateView(ruleChain.id.id);
});
}
}
Loading…
Cancel
Save