代码之家  ›  专栏  ›  技术社区  ›  Nithya Rajan

Angular 7[禁用]在嵌套对象的属性值更改时不检测更改

  •  0
  • Nithya Rajan  · 技术社区  · 6 年前

    我需要编写一个通用函数,它将根据JSON内部的条件启用/禁用按钮。

    杰森:

        {
            PURCHASE_LIEN: {
              LABEL: 'PURCHASE LIEN',
              DISABLE: true,
              CONDITION: [
                {
                  ReviewPurchaseDecisionStatus: true,
                  PurchaseDecisionStatus: false
                },
                {
                  ReviewPurchaseDecisionStatus: true,
                  'ReviewPurchaseDecision.Status': 'NOT QUALIFIED'
                }
              ]
            },
            NOT_QUALIFIED: {
              LABEL: 'NOT QUALIFIED',
              DISABLE: true,
              CONDITION: [
                {
                  ReviewPurchaseDecisionStatus: true,
                  PurchaseDecisionStatus: false
                },
                {
                  ReviewPurchaseDecisionStatus: true,
                  'ReviewPurchaseDecision.Status': 'PURCHASED'
                }
              ]
            }
      }
    

    在JSON中,我有两个按钮“购买留置权”和“不合格”。两个函数都有基于条件的条件数组,按钮应使用“禁用”属性启用/禁用。

    验证服务.ts

    下面的函数将根据对象(selectedrow)的条件设置按钮的禁用属性。

    public disableButton(buttonContainer: any, buttonID: string, selectedRow: any) {
        let status = true;
        for (let i = 0; i < buttonContainer[buttonID]['CONDITION'].length; i++) {
          const condition = buttonContainer[buttonID]['CONDITION'][i];
          for (const conditionName in condition) {
            if (condition[conditionName] !== selectedRow[condition]) {
              status = false;
            }
          }
          if (status) {
            buttonContainer[buttonID].DISABLE = false;
            break;
          } else {
            buttonContainer[buttonID].DISABLE = true;
          }
    
        }
    
        return buttonContainer;
      }
    

    app.component.html(应用程序组件.html)

     <div class="col-12 col-sm-6 col-md-4 col-lg-2">
            <button class="btn btn-primary btn-semi-circle" (click)="showModal('Purchase')"
             [disabled]="disableButton(buttonGroup, 'PURCHASE_LIEN', selectedRowData)">Purchase
              Lien</button>
              <!-- [disabled]="PURCHASE_LIEN_DISABLE" -->
          </div>
          <div class="col-12 col-sm-6 col-md-4 col-lg-2">
            <button class="btn btn-danger btn-semi-circle" (click)="showModal('Not Qualified')"
             [disabled]="disableButton(buttonGroup, 'NOT_QUALIFIED', selectedRowData)">Not
              Qualified</button>
              <!-- [disabled]="NOT_QUALIFIED_DISABLE" -->
          </div>
    

    应用组件.ts

    export class ReviewPurchaseDecisionComponent implements OnInit {
      public buttonGroup: any = {
        PURCHASE_LIEN: {
          LABEL: 'PURCHASE LIEN',
          DISABLE: true,
          CONDITION: [
            {
              ReviewPurchaseDecisionStatus: true,
              PurchaseDecisionStatus: false
            },
            {
              ReviewPurchaseDecisionStatus: true,
              'ReviewPurchaseDecision.Status': 'NOT QUALIFIED'
            }
          ]
        },
        NOT_QUALIFIED: {
          LABEL: 'NOT QUALIFIED',
          DISABLE: true,
          CONDITION: [
            {
              ReviewPurchaseDecisionStatus: true,
              PurchaseDecisionStatus: false
            },
            {
              ReviewPurchaseDecisionStatus: true,
              'ReviewPurchaseDecision.Status': 'PURCHASED'
            }
          ]
        }
      };
    
      constructor(
        public router: Router,
        public validation: ValidationService,
        private fb: FormBuilder,
        private http: HttpHelperService,
        private myMonitoringService: MyMonitoringService,
        private authentication: AuthenticationService,
        private sessionService: SessionService,
        public dialogService: DialogServiceService,
        private caseService: CaseService,
        private cookieService: CookieService
      ) {}
    
      disableButton(buttonContainer: any, buttonID: string, selectedRow: any) {
        this.buttonGroup = this.validation.disableButton(
          buttonContainer,
          buttonID,
          selectedRow
        );
        return this.buttonGroup[buttonID].DISABLE;
      }
    }
    

    验证服务中的disableButton方法根据条件将disable属性值更改为true/false,但该按钮不启用。它没有检测到变化

    2 回复  |  直到 6 年前
        1
  •  0
  •   Yanis-git    6 年前

    角度变化检测是一个巨大的课题。这里有一个非常嵌套的对象,角度必须跟踪以检测变化。

    对于这个角度,没有选择递归地检查对象的每个字段,并将其与以前的状态进行比较,以检测您是否做了任何更改。此步骤称为摘要并标记为脏。资源消耗非常大,这就是为什么在非常特殊的情况下,需要进行角度分析(列表不完整只是为了演示):

    • @输出为触发器
    • @输入发生变化
    • 浏览器事件为分派(单击、悬停等)
    • 超时
    • 间隔

    在这里,您可以通过从[disable]html属性调用函数来改变对象。我怀疑这个案例不包括在默认的变更检测策略中。

    无论如何,角度小组不建议操纵这样的物体。建议使用以下两种方法之一: -避免改变状态,最好创建新对象并替换前一个对象。像这样有棱角的简单得做 myPreviousObject !== myNewObject 而不是:

    if (
        myPreviousObject.prop1 !== myNewObject.prop1 ||
        myPreviousObject.prop2 !== myPreviousObject.prop2 ||
        ....
    )
    
    • 使用具有不变状态的Observable。

    对于我的演示,我使用了第二种方法,您可以在这里找到简单的实现:

    我的模型:

    export interface disableState {
      PURCHASE_LIEN: {
        disable: boolean;
        disable$: BehaviorSubject<boolean>
      };
    
      NOT_QUALIFIED: {
        disable: boolean;
        disable$: BehaviorSubject<boolean>
      };
    }
    

    在我的组件中,我有这样的属性:

    disableState: disableState = {
        NOT_QUALIFIED: { 
          disable: false, 
          disable$: new BehaviorSubject<boolean>(false),
          },
       PURCHASE_LIEN: { 
          disable: false, 
          disable$: new BehaviorSubject<boolean>(false),
          },
      }
    

    当我想改变这个值时,我可以这样做:

    /**
     * Call your service like :
     * this.validation.disableButton()
     */
    this.disableState['NOT_QUALIFIED'].disable = true;
    this.disableState['NOT_QUALIFIED'].disable$.next(true);
    

    live coding

    完整组件:

    @Component({
      selector: 'my-app',
      templateUrl: './app.component.html',
      styleUrls: [ './app.component.css' ],
      changeDetection: ChangeDetectionStrategy.OnPush
    })
    export class AppComponent  {
    
      disableState: disableState = {
        NOT_QUALIFIED: { 
          disable: false, 
          disable$: new BehaviorSubject<boolean>(false),
          },
       PURCHASE_LIEN: { 
          disable: false, 
          disable$: new BehaviorSubject<boolean>(false),
          },
      }
    
      showModal(id: string) {
        console.log(`Open modal : ${id}`);
      }
    
    
      dummyPropertyChange() {
        /**
         * Call your service like :
         * this.validation.disableButton()
         */
        this.disableState['NOT_QUALIFIED'].disable = true;
        this.disableState['NOT_QUALIFIED'].disable$.next(true);
      }
    }
    

    注意我换了 ChangeDetectionStrategy 要求Angular只通过简单对象比较(而不是嵌套对象比较)执行脏检查。从现在开始,如果我想更新数据,我应该:

    • 更改整个变量数据(而不是仅嵌套属性)
    • 使用Observable在时间线上执行更改。
        2
  •  0
  •   Nithya Rajan    6 年前

    我通过创建一个指令解决了这个问题。该指令将访问按钮的native元素,它根据JSON中的条件启用/禁用按钮。

    btn-disable.directive.ts指令

    import {
      Directive,
      Renderer2,
      ElementRef,
      Input,
      OnChanges
    } from '@angular/core';
    
    
    @Directive({
      selector: '[appBtnDisable]'
    })
    export class BtnDisableDirective implements OnChanges {
      @Input() buttonContainer: any = {};
      @Input() buttonID = '';
      @Input() condition: any = {};
    
      constructor(public ele: ElementRef, public renderer: Renderer2) {
        if (this.buttonID) {
          this.disableButton(this.buttonContainer, this.buttonID, this.condition);
        }
      }
    
      ngOnChanges() {
        if (this.buttonID) {
          this.disableButton(this.buttonContainer, this.buttonID, this.condition);
        }
      }
    
      public disableButton(
        buttonContainer: any,
        buttonID: string,
        selectedRow: any
      ) {
    
        for (let i = 0; i < buttonContainer[buttonID]['CONDITION'].length; i++) {
          const condition = buttonContainer[buttonID]['CONDITION'][i];
          let status = true;
          for (const conditionName in condition) {
            if (
              this.convertNulltoUndefined(condition[conditionName]) !== this.evaluate(conditionName, selectedRow)
            ) {
              status = false;
            }
          }
    
          if (status) {
            this.ele.nativeElement.disabled = false;
            break;
          } else {
            this.ele.nativeElement.disabled = true;
          }
        }
      }
    
    
    
      evaluate(data: string, selectedRow: any): any {
        if (data.split('.').length > 1) {
          const value = 'selectedRow.' + data;
          try {
            return eval(value);
          } catch (error) {
            return undefined;
          }
        } else {
          return selectedRow[data];
        }
      }
    
      convertNulltoUndefined(data) {
        return (data === null) ? undefined : data;
      }
    }
    

    app.component.html(应用程序组件.html)

      <button class="btn btn-primary btn-semi-circle" (click)="showModal('Purchase')"
                appBtnDisable [condition]="selectedRowData" [buttonContainer]="buttonGroup" [buttonID]="'PURCHASE_LIEN'">Purchase
                  Lien</button>