Javascript Angular 7[禁用]在嵌套对象';s的属性值被更改

Javascript Angular 7[禁用]在嵌套对象';s的属性值被更改,javascript,angular,Javascript,Angular,我需要编写一个通用函数,根据JSON中的条件启用/禁用按钮 JSON: 在JSON中,我有两个按钮“PURCHASE_LIEN”和“NOT_QUALIFIED”。这两个函数都有基于条件的条件数组,按钮应使用“disable”属性启用/禁用 VALIDATION.SERVICE.TS 下面的函数将根据对象的条件设置按钮的禁用属性(selectedRow) validation service中的disableButton方法会根据条件将DISABLE属性值更改为true/false,但该按钮不会启

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

JSON:

在JSON中,我有两个按钮“PURCHASE_LIEN”和“NOT_QUALIFIED”。这两个函数都有基于条件的条件数组,按钮应使用“disable”属性启用/禁用

VALIDATION.SERVICE.TS

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


validation service中的disableButton方法会根据条件将DISABLE属性值更改为true/false,但该按钮不会启用。它不检测变化

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

因此,我们没有选择递归地检查对象的每个字段,并将其与以前的状态进行比较,以检测是否进行了任何更改。此步骤是调用摘要并标记为脏。这是angular在非常特殊的情况下执行此操作的原因(列表不完整仅用于演示):

  • @输出是触发器
  • @输入是变异的
  • 浏览器事件为分派(单击、悬停等)
  • 超时
  • 间歇
在这里,您可以通过从[disable]html属性调用函数来改变对象。我怀疑默认的changeDetectionStrategy没有涵盖这个案例

无论如何,angular团队不建议像这样操纵对象。建议使用以下两种方法中的任何一种: -避免改变状态,更喜欢创建新对象并替换以前的对象。像这样,我必须做
myPreviousObject!==myNewObject
而不是:

if (
    myPreviousObject.prop1 !== myNewObject.prop1 ||
    myPreviousObject.prop2 !== myPreviousObject.prop2 ||
    ....
)
  • 使用不可变状态的observatable
在我的演示中,我使用了第二种方法,您可以在这里找到简单的实现:

我的模型:

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

  NOT_QUALIFIED: {
    disable: boolean;
    disable$: BehaviorSubject<boolean>
  };
}

全部组成部分:
@组件({
选择器:“我的应用程序”,
templateUrl:“./app.component.html”,
样式URL:['./app.component.css'],
changeDetection:ChangeDetectionStrategy.OnPush
})
导出类AppComponent{
disableState:disableState={
不合格:{
禁用:false,
禁用$:新行为主体(false),
},
购买留置权:{
禁用:false,
禁用$:新行为主体(false),
},
}
showmodel(id:string){
log(`openmodal:${id}`);
}
dummyPropertyChange(){
/**
*致电您的服务,如:
*this.validation.disableButton()
*/
this.disableState['NOT_QUALIFIED'].disable=true;
this.disableState['NOT_QUALIFIED'].disable$.next(true);
}
}
请注意,我已经切换了
ChangeDetectionStrategy
,要求angular仅通过简单对象比较(而不是嵌套对象)执行脏检查。从现在起,如果我想更新数据,我应该:

  • 更改整个变量数据(而不仅仅是嵌套属性)
  • 使用observable在时间线上执行更改

我通过创建指令解决了这个问题。该指令将访问按钮的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;
  }
}
导入{
指令,
渲染器2,
ElementRef,
输入,
一旦改变
}从“@angular/core”开始;
@指示({
选择器:“[AppBndisable]”
})
导出类BtnDisableDirective实现OnChanges{
@Input()按钮容器:any={};
@Input()按钮ID='';
@Input()条件:any={};
构造函数(public ele:ElementRef,public renderer:render2){
如果(这个按钮){
this.disableButton(this.buttonContainer,this.buttonID,this.condition);
}
}
ngOnChanges(){
如果(这个按钮){
this.disableButton(this.buttonContainer,this.buttonID,this.condition);
}
}
公共禁用按钮(
按钮容器:任何,
buttonID:字符串,
selectedRow:任何
) {
for(设i=0;i1){
常量值='selectedRow.'+数据;
试一试{
返回eval(value);
}捕获(错误){
返回未定义;
}
}否则{
返回selectedRow[数据];
}
}
convertNulltoUndefined(数据){
返回(数据===null)?未定义:数据;
}
}
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>
  <button class="btn btn-primary btn-semi-circle" (click)="showModal('Purchase')"
            appBtnDisable [condition]="selectedRowData" [buttonContainer]="buttonGroup" [buttonID]="'PURCHASE_LIEN'">Purchase
              Lien</button>
购买
留置权

能否创建
堆栈闪电战
?控制台日志在
返回按钮容器之前以查看布尔值。我已使用开发人员控制台检查了对象,属性发生了更改,但angular未检测到更改可能的重复项。我没有询问DOM操作。我只想知道angular如何知道对象属性已经更改,以便angular检测到它并更新禁用按钮的属性为什么我们不能在没有out指令的情况下实现这一点
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);
@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);
  }
}
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;
  }
}
  <button class="btn btn-primary btn-semi-circle" (click)="showModal('Purchase')"
            appBtnDisable [condition]="selectedRowData" [buttonContainer]="buttonGroup" [buttonID]="'PURCHASE_LIEN'">Purchase
              Lien</button>