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&gt;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&gt;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]

Reply via email to