【问题标题】:Angular 7 [disabled] not detecting the change when a nested object's property values gets changed当嵌套对象的属性值更改时,Angular 7 [禁用] 未检测到更改
【发布时间】:2019-06-06 00:46:18
【问题描述】:

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

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 中,我有两个按钮“PURCHASE_LIEN”和“NOT_QUALIFIED”。这两个函数都有基于条件的条件数组,按钮应使用“禁用”属性启用/禁用。

VALIDATION.SERVICE.TS

以下函数将根据对象 (selectedRow) 的条件设置按钮的 DISABLE 属性。

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

 <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>

app.component.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,但按钮不启用。它没有检测到更改

【问题讨论】:

  • 你能创建stackblitz吗?
  • return buttonContainer; 之前的控制台日志以查看布尔值。
  • 我已经使用开发者控制台检查了对象,属性发生了变化,但 angular 没有检测到变化
  • 我不是在问 DOM 操作。我只是想知道 Angular 是如何知道对象属性已更改的,以便 Angular 检测到它并更新按钮禁用属性

标签: javascript angular


【解决方案1】:

我已经通过创建指令解决了这个问题。该指令将访问按钮的 nativeElement,它根据 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

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

【讨论】:

  • 为什么我们不能用 out 指令实现这一点
【解决方案2】:

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

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

  • @Output 是触发器
  • @Input 是变异的
  • 正在调度浏览器事件(点击、悬停等)
  • 超时
  • 间隔
  • ....

在这里,您通过调用 [disable] html 属性中的函数来改变您的对象。我怀疑默认的 changeDetectionStrategy 没有涵盖这种情况。

无论如何,角度团队不建议像这样操作对象。建议使用这两种方法中的任何一种: - 避免变异状态,更喜欢创建新对象并替换以前的对象。像这个有角度的简单必须做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 在时间轴上执行更改。

【讨论】:

    猜你喜欢
    • 2020-12-22
    • 2016-04-20
    • 2021-02-03
    • 1970-01-01
    • 1970-01-01
    • 2020-07-11
    • 2020-11-03
    • 1970-01-01
    相关资源
    最近更新 更多