This is an automated email from the ASF dual-hosted git repository.
gongchao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hertzbeat.git
The following commit(s) were added to refs/heads/master by this push:
new 86ca62c67 [bugfix] fixed the issue of missing form validation for the
"Rule(Expr)" item in the "Threshold" form (#2387)
86ca62c67 is described below
commit 86ca62c67af8455cb5a6dad5edcc40ba9b9688b2
Author: Kerwin Bryant <[email protected]>
AuthorDate: Sun Jul 28 09:32:15 2024 +0800
[bugfix] fixed the issue of missing form validation for the "Rule(Expr)"
item in the "Threshold" form (#2387)
Co-authored-by: tomsun28 <[email protected]>
---
.../alert-setting/alert-setting.component.html | 305 +++++++++++----------
.../alert-setting/alert-setting.component.less | 9 +
.../alert/alert-setting/alert-setting.component.ts | 17 +-
3 files changed, 179 insertions(+), 152 deletions(-)
diff --git
a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html
b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html
index bf7f94cf2..a4cde2a22 100644
--- a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html
+++ b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.html
@@ -204,8 +204,8 @@
></nz-cascader>
</nz-form-control>
</nz-form-item>
- <nz-form-item *ngIf="cascadeValues.length > 0 && cascadeValues.length !=
2">
- <nz-form-label [nzSpan]="7" nzFor="rule" nzRequired="true"
[nzTooltipTitle]="'alert.setting.rule.label' | i18n">
+ <nz-form-item *ngIf="cascadeValues.length > 0 && cascadeValues.length !=
2" [ngStyle]="{ marginBottom: '5px' }">
+ <nz-form-label [nzSpan]="7" nzFor="isExpr" nzRequired="true"
[nzTooltipTitle]="'alert.setting.rule.label' | i18n">
{{ 'alert.setting.rule' | i18n }}
</nz-form-label>
<nz-form-control [nzSpan]="12" [nzErrorTip]="'validation.required' |
i18n">
@@ -224,153 +224,164 @@
{{ 'alert.setting.rule.switch-expr.1' | i18n }}
</label>
</nz-radio-group>
- <div *ngIf="isExpr" id="rule" style="margin-top: 5px">
- <nz-textarea-count [nzMaxCharacterCount]="100">
- <textarea
- [(ngModel)]="define.expr"
- required
- rows="3"
- nz-input
- name="expr"
- id="expr"
- [placeholder]="('alert.setting.expr.example' | i18n) + ':
responseTime>40'"
+ </nz-form-control>
+ </nz-form-item>
+ <nz-form-item *ngIf="cascadeValues.length > 0 && cascadeValues.length !=
2 && isExpr">
+ <nz-form-label [nzSpan]="7" [nzNoColon]="true"
nzFor="expr"></nz-form-label>
+ <nz-form-control [nzSpan]="12" [nzErrorTip]="'validation.required' |
i18n">
+ <nz-textarea-count [nzMaxCharacterCount]="100">
+ <textarea
+ [(ngModel)]="define.expr"
+ required
+ rows="3"
+ nz-input
+ name="expr"
+ id="expr"
+ [placeholder]="('alert.setting.expr.example' | i18n) + ':
responseTime>40'"
+ >
+ </textarea>
+ </nz-textarea-count>
+ <nz-collapse style="margin-top: 20px">
+ <nz-collapse-panel [nzActive]="isManageModalAdd"
[nzHeader]="'alert.setting.expr.tip' | i18n">
+ <nz-list nzSize="small" nzSplit="false">
+ <nz-list-item *ngFor="let item of currentMetrics; let i =
index">
+ <code>
+ {{ item.value }} :
+ {{
+ item.value == item.label
+ ? i == 0
+ ? ('alert.setting.target.tip' | i18n)
+ : ('alert.setting.target.other' | i18n)
+ : item.label
+ }}
+ </code>
+ <nz-tag [nzColor]="item.type === 0 ? 'success' :
'processing'">
+ {{ item.type === 0 ? ('alert.setting.number' | i18n) :
('alert.setting.string' | i18n) }}
+ </nz-tag>
+ </nz-list-item>
+ <nz-list-item>
+ <code>
+ {{ 'alert.setting.operator' | i18n }} : equals(str1,str2),
contains(str1,str2), exists(keyName), matches(str,regex), ==,
+ <, <=, >, >=, !=, ( ), +, -, &&, ||
+ </code>
+ </nz-list-item>
+ </nz-list>
+ </nz-collapse-panel>
+ </nz-collapse>
+ </nz-form-control>
+ </nz-form-item>
+ <nz-form-item *ngIf="cascadeValues.length > 0 && cascadeValues.length !=
2 && !isExpr">
+ <nz-form-label [nzSpan]="7" [nzNoColon]="true"></nz-form-label>
+ <nz-form-control [nzSpan]="12" [nzErrorTip]="'validation.required' |
i18n" [nzValidateStatus]="qbFormCtrl">
+ <ngx-query-builder
+ [classNames]="qbClassNames"
+ [config]="qbConfig"
+ [formControl]="qbFormCtrl"
+ [ngStyle]="{ background: 'ghostwhite', borderRadius: '4px' }"
+ >
+ <ng-container *querySwitchGroup="let rule; let onChange =
onChange">
+ <nz-radio-group
+ style="white-space: nowrap"
+ [(ngModel)]="rule.condition"
+ (ngModelChange)="onChange(rule.condition)"
+ nzButtonStyle="solid"
+ [required]="true"
+ [ngModelOptions]="{ standalone: true }"
>
- </textarea>
- </nz-textarea-count>
- <nz-collapse style="margin-top: 20px">
- <nz-collapse-panel [nzActive]="isManageModalAdd"
[nzHeader]="'alert.setting.expr.tip' | i18n">
- <nz-list nzSize="small" nzSplit="false">
- <nz-list-item *ngFor="let item of currentMetrics; let i =
index">
- <code>
- {{ item.value }} :
- {{
- item.value == item.label
- ? i == 0
- ? ('alert.setting.target.tip' | i18n)
- : ('alert.setting.target.other' | i18n)
- : item.label
- }}
- </code>
- <nz-tag [nzColor]="item.type === 0 ? 'success' :
'processing'">
- {{ item.type === 0 ? ('alert.setting.number' | i18n) :
('alert.setting.string' | i18n) }}
- </nz-tag>
- </nz-list-item>
- <nz-list-item>
- <code>
- {{ 'alert.setting.operator' | i18n }} :
equals(str1,str2), contains(str1,str2), exists(keyName), matches(str,regex),
- ==, <, <=, >, >=, !=, ( ), +, -, &&, ||
- </code>
- </nz-list-item>
- </nz-list>
- </nz-collapse-panel>
- </nz-collapse>
- </div>
- <div *ngIf="!isExpr" id="rule" class="br-4" style="margin-top: 5px;
background: ghostwhite">
- <ngx-query-builder [classNames]="qbClassNames" [config]="qbConfig"
[formControl]="qbFormCtrl">
- <ng-container *querySwitchGroup="let rule; let onChange =
onChange">
- <nz-radio-group
- style="white-space: nowrap"
- [(ngModel)]="rule.condition"
- (ngModelChange)="onChange(rule.condition)"
- nzButtonStyle="solid"
- [required]="true"
- [ngModelOptions]="{ standalone: true }"
- >
- <label nz-radio-button [nzValue]="'and'">AND</label>
- <label nz-radio-button [nzValue]="'or'">OR</label>
- </nz-radio-group>
- </ng-container>
- <ng-container *queryRulesetAddRuleButton="let rule; let addRule
= addRule">
- <button style="margin-left: 0; flex-shrink: 0" nz-button
(click)="addRule()">
- <i nz-icon nzType="plus"></i>
- {{ 'Rule' }}
- </button>
- </ng-container>
- <ng-container *queryRulesetAddRulesetButton="let rule; let
addRuleSet = addRuleSet">
- <button style="margin-left: 0; flex-shrink: 0" nz-button
(click)="addRuleSet()">
- <i nz-icon nzType="plus"></i>
- {{ 'Ruleset' }}
- </button>
- </ng-container>
- <ng-container *queryRulesetRemoveButton="let rule; let
removeRuleSet = removeRuleSet">
- <button style="margin-left: 0; flex-shrink: 0" nz-button
nzDanger (click)="removeRuleSet(rule)">
- <i nz-icon nzType="minus"></i>
- </button>
- </ng-container>
- <ng-container *queryRuleRemoveButton="let rule; let removeRule =
removeRule">
- <button style="flex-shrink: 0" nz-button nzDanger
(click)="removeRule(rule)">
- <i nz-icon nzType="minus"></i>
- </button>
- </ng-container>
- <ng-container *queryField="let rule; let getFields = getFields;
let onChange = onChange">
- <nz-select
- [(ngModel)]="rule.field"
- (ngModelChange)="onChange($event, rule)"
- [ngModelOptions]="{ standalone: true }"
- [nzDropdownMatchSelectWidth]="false"
- [nzPlaceHolder]="'alert.setting.rule.metric.place-holder' |
i18n"
- style="width: auto"
- >
- <nz-option
- *ngFor="let field of getFields(rule.entity || '')"
- [nzValue]="field.value"
- [nzLabel]="field.name ? field.name : field.value"
- nzCustomContent
- >
- {{ field.name ? field.name : field.value }}
- <nz-tag [nzColor]="field.type === 0 ? 'success' :
'processing'">
- {{
- field.type === 0
- ? ('alert.setting.number' | i18n)
- : field.type === 3
- ? ('alert.setting.time' | i18n)
- : ('alert.setting.string' | i18n)
- }}
- </nz-tag>
- <nz-tag *ngIf="field.unit">
- {{ field.unit }}
- </nz-tag>
- </nz-option>
- </nz-select>
- </ng-container>
- <ng-container *queryOperator="let rule; let operators =
operators; let onChange = onChange">
- <nz-select
- [(ngModel)]="rule.operator"
- (ngModelChange)="onChange(rule)"
- [ngModelOptions]="{ standalone: true }"
- [nzShowArrow]="false"
- [nzDropdownMatchSelectWidth]="false"
- style="text-align: center; font-weight: bolder; width: auto"
- [nzDropdownStyle]="{ 'text-align': 'center', 'font-weight':
'bolder', 'font-size': 'larger' }"
- [nzPlaceHolder]="'alert.setting.rule.operator' | i18n"
+ <label nz-radio-button [nzValue]="'and'">AND</label>
+ <label nz-radio-button [nzValue]="'or'">OR</label>
+ </nz-radio-group>
+ </ng-container>
+ <ng-container *queryRulesetAddRuleButton="let rule; let addRule =
addRule">
+ <button style="margin-left: 0; flex-shrink: 0" nz-button
(click)="addRule()">
+ <i nz-icon nzType="plus"></i>
+ {{ 'Rule' }}
+ </button>
+ </ng-container>
+ <ng-container *queryRulesetAddRulesetButton="let rule; let
addRuleSet = addRuleSet">
+ <button style="margin-left: 0; flex-shrink: 0" nz-button
(click)="addRuleSet()">
+ <i nz-icon nzType="plus"></i>
+ {{ 'Ruleset' }}
+ </button>
+ </ng-container>
+ <ng-container *queryRulesetRemoveButton="let rule; let
removeRuleSet = removeRuleSet">
+ <button style="margin-left: 0; flex-shrink: 0" nz-button
nzDanger (click)="removeRuleSet(rule)">
+ <i nz-icon nzType="minus"></i>
+ </button>
+ </ng-container>
+ <ng-container *queryRuleRemoveButton="let rule; let removeRule =
removeRule">
+ <button style="flex-shrink: 0" nz-button nzDanger
(click)="removeRule(rule)">
+ <i nz-icon nzType="minus"></i>
+ </button>
+ </ng-container>
+ <ng-container *queryField="let rule; let getFields = getFields;
let onChange = onChange">
+ <nz-select
+ [(ngModel)]="rule.field"
+ (ngModelChange)="onChange($event, rule)"
+ [ngModelOptions]="{ standalone: true }"
+ [nzDropdownMatchSelectWidth]="false"
+ [nzPlaceHolder]="'alert.setting.rule.metric.place-holder' |
i18n"
+ style="width: auto"
+ >
+ <nz-option
+ *ngFor="let field of getFields(rule.entity || '')"
+ [nzValue]="field.value"
+ [nzLabel]="field.name ? field.name : field.value"
+ nzCustomContent
>
- <nz-option
- *ngFor="let operator of operators"
- [nzValue]="operator"
- [nzLabel]="getOperatorLabelByType(operator) | i18n"
- ></nz-option>
- </nz-select>
- </ng-container>
- <ng-container *queryInput="let rule; let field = field; let
onChange = onChange; type: 'custom'">
- <input
- nz-input
- [disabled]="rule.operator == 'exists' || rule.operator ==
'!exists'"
- [type]="field.type === 0 ? 'number' : 'text'"
- [(ngModel)]="rule.value"
- (ngModelChange)="onChange($event)"
- [ngModelOptions]="{ standalone: true }"
- [placeholder]="
- rule.operator == 'exists' || rule.operator == '!exists'
- ? ''
- : field.type === 0
- ? ('alert.setting.rule.numeric-value.place-holder' |
i18n)
- : ('alert.setting.rule.string-value.place-holder' | i18n)
- "
- style="flex: 1"
- />
- </ng-container>
- </ngx-query-builder>
- </div>
+ {{ field.name ? field.name : field.value }}
+ <nz-tag [nzColor]="field.type === 0 ? 'success' :
'processing'">
+ {{
+ field.type === 0
+ ? ('alert.setting.number' | i18n)
+ : field.type === 3
+ ? ('alert.setting.time' | i18n)
+ : ('alert.setting.string' | i18n)
+ }}
+ </nz-tag>
+ <nz-tag *ngIf="field.unit">
+ {{ field.unit }}
+ </nz-tag>
+ </nz-option>
+ </nz-select>
+ </ng-container>
+ <ng-container *queryOperator="let rule; let operators = operators;
let onChange = onChange">
+ <nz-select
+ [(ngModel)]="rule.operator"
+ (ngModelChange)="onChange(rule)"
+ [ngModelOptions]="{ standalone: true }"
+ [nzShowArrow]="false"
+ [nzDropdownMatchSelectWidth]="false"
+ style="text-align: center; font-weight: bolder; width: auto"
+ [nzDropdownStyle]="{ 'text-align': 'center', 'font-weight':
'bolder', 'font-size': 'larger' }"
+ [nzPlaceHolder]="'alert.setting.rule.operator' | i18n"
+ >
+ <nz-option
+ *ngFor="let operator of operators"
+ [nzValue]="operator"
+ [nzLabel]="getOperatorLabelByType(operator) | i18n"
+ ></nz-option>
+ </nz-select>
+ </ng-container>
+ <ng-container *queryInput="let rule; let field = field; let
onChange = onChange; type: 'custom'">
+ <input
+ nz-input
+ [disabled]="rule.operator == 'exists' || rule.operator ==
'!exists'"
+ [type]="field.type === 0 ? 'number' : 'text'"
+ [(ngModel)]="rule.value"
+ (ngModelChange)="onChange($event)"
+ [ngModelOptions]="{ standalone: true }"
+ [placeholder]="
+ rule.operator == 'exists' || rule.operator == '!exists'
+ ? ''
+ : field.type === 0
+ ? ('alert.setting.rule.numeric-value.place-holder' | i18n)
+ : ('alert.setting.rule.string-value.place-holder' | i18n)
+ "
+ style="flex: 1"
+ />
+ </ng-container>
+ </ngx-query-builder>
</nz-form-control>
</nz-form-item>
<nz-form-item *ngIf="cascadeValues.length > 0 && cascadeValues.length ==
2">
diff --git
a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.less
b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.less
index 50dfab550..c2e03f5d9 100644
--- a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.less
+++ b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.less
@@ -8,6 +8,15 @@
}
}
+ .tree {
+ list-style: none;
+ margin: 4px 0 2px;
+ }
+
+ .tree:empty {
+ margin: 0px;
+ }
+
.ruleset-invalid {
border: none!important;
diff --git
a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts
b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts
index 8dac902cc..7ad32d237 100644
--- a/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts
+++ b/web-app/src/app/routes/alert/alert-setting/alert-setting.component.ts
@@ -18,7 +18,7 @@
*/
import { Component, Inject, OnInit, ViewChild } from '@angular/core';
-import { FormBuilder, FormControl, NgForm } from '@angular/forms';
+import { AbstractControl, FormBuilder, FormControl, NgForm, ValidationErrors }
from '@angular/forms';
import { I18NService } from '@core';
import { ALAIN_I18N_TOKEN } from '@delon/theme';
import { Rule, RuleSet, QueryBuilderConfig, QueryBuilderClassNames } from
'@kerwin612/ngx-query-builder';
@@ -56,9 +56,9 @@ export class AlertSettingComponent implements OnInit {
@Inject(ALAIN_I18N_TOKEN) private i18nSvc: I18NService,
private formBuilder: FormBuilder
) {
- this.qbFormCtrl = this.formBuilder.control(this.qbData);
+ this.qbFormCtrl = this.formBuilder.control(this.qbData, this.qbValidator);
}
- @ViewChild('defineForm', { static: false }) defineForm: NgForm | undefined;
+ @ViewChild('defineForm', { static: false }) defineForm!: NgForm;
search!: string;
pageIndex: number = 1;
pageSize: number = 8;
@@ -76,6 +76,7 @@ export class AlertSettingComponent implements OnInit {
];
qbClassNames: QueryBuilderClassNames = {
row: 'row',
+ tree: 'tree',
rule: 'br-4 rule',
ruleSet: 'br-4 ruleset',
invalidRuleSet: 'br-4 ruleset-invalid'
@@ -90,6 +91,12 @@ export class AlertSettingComponent implements OnInit {
condition: 'and',
rules: []
};
+ qbValidator = (control: AbstractControl): ValidationErrors | null => {
+ if (!control.value || !control.value.rules || control.value.rules.length
=== 0) {
+ return { required: true };
+ }
+ return null;
+ };
qbFormCtrl: FormControl;
ngOnInit(): void {
@@ -651,8 +658,7 @@ export class AlertSettingComponent implements OnInit {
}
resetQbData(qbData: RuleSet) {
- this.qbData = qbData;
- this.qbFormCtrl = this.formBuilder.control(this.qbData);
+ this.qbFormCtrl.reset((this.qbData = qbData));
}
resetManageModalData() {
@@ -663,6 +669,7 @@ export class AlertSettingComponent implements OnInit {
}
onManageModalOk() {
+ this.defineForm.form.addControl('ruleset', this.qbFormCtrl);
if (this.defineForm?.invalid) {
Object.values(this.defineForm.controls).forEach(control => {
if (control.invalid) {
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]