如何在对象引用更改时强制执行ngOnit?

时间:2017-12-19 17:38:25

标签: angular typescript

我试图创建一个类似于ngModel的指令,但会让输入值与绑定模型不同步。我知道这听起来很奇怪,但用例是绑定属性可能有约束(即:不能小于25),但我想允许用户编辑字段而不改变它们的值进入一个受约束的,直到它们完成编辑(ngModel将在编辑时更改值,这很烦人)。

我遇到的问题是当我编辑的属性的对象发生变化时,指令所没有的输入值。我假设这是因为指令直接绑定到对象的原语,而不是对象本身。我需要的是当源父对象改变时该指令重新进入。

我尝试使用* ngIf强制执行此操作,希望它能检测到引用更改,并刷新所有子组件(我不确定为什么我希望这样做),但显然这不起作用。

我能够使其工作的唯一方法是首先将对象引用设置为null(强制* ngIf重新渲染),然后将对象设置为setTimeout中的不同引用。显然这很糟糕。

有没有一种好方法告诉Angular如果对象引用发生变化,页面的某一部分需要刷新?

这是一个显示我的问题根源的人(不是所有的输入编辑等):https://plnkr.co/edit/oEeFFLkSMyV0bF4UnEFc?p=preview

来自那个plunker的相关代码:

interface MyObject{
  value: number;
}

@Component({
  selector: 'my-app',
  template: `

    <div>
      <span>Doesn't Work</span><br>
      <button (click)="changeMyObject(myObject1)">2000</button>
      <button (click)="changeMyObject(myObject2)">3000</button>
    </div>
    <div *ngIf="myObject">    
      <div>
        <h2 [my-sample]="myObject"></h2>
      </div>
    </div>
    <div>
      <span>Works with Timeout</span><br>
      <button (click)="changeMyObjectWithTimeout(myObject1)">2000</button>
      <button (click)="changeMyObjectWithTimeout(myObject2)">3000</button>
    </div>
  `,
})
export class App {

  myObject: MyObject;

  private myObject1 : MyObject {
    value: 2000
  };

  private myObject2 : MyObject{
    value: 3000
  }

  constructor() {
    this.myObject = this.myObject1
  }

  changeMyObject(myObject: MyObject){
    this.myObject = myObject;
  }

  changeMyObjectWithTimeout(myObject: MyObject){
    this.myObject = null;
    setTimeout(() => {
      this.myObject = myObject;
    });
  }
}


@Directive({
  selector: '[my-sample]'
})
export class MySampleDirective implements OnInit{

  @Input('my-sample') myObject : MyObject;

  private _element: any;
  private _differ: KeyValueDiffer;

  constructor(element: ElementRef){
    this._element = element.nativeElement;
  }

  ngOnInit(){
    this._element.innerText = this.myObject.value;
  }
}

修改 前面的例子可能对于对象引用问题太具体了,没有完整的上下文并不清楚问题是什么,所以我提供了一个更全面的例子来说明我尝试的内容。完成。你可以在这里看到它:

https://plnkr.co/edit/YecDj100UVUALHHdnRpd?p=preview

重要位在src\property-value.directive.ts文件中。要重新迭代,问题是Angular没有(我认为理所当然,因为我没有在任何地方直接绑定它)识别对象引用的变化,因此它不会重新初始化模板。有人建议使用我以前尝试过的OnChanges。这种方法的问题是Angular不知道值的变化,因为父对象是不同的。它只知道它收到了一个新的不可变值,其值与前一个值不同(从逻辑上讲,这也是正确的)。因此,无论您是否正在编辑对象的当前值,或者您是否已切换对象,ngOnChanges都会触发。

要重新创建我遇到的问题: 单击myObj1.value旁边的编辑并输入新值 点击myObj2.value旁边的编辑,你会看到输入框的显示值没有改变(b / c ngOnInit没有被调用)。

但是,如果单击myObj1.value旁边的编辑,编辑该值,然后单击选择无,以便ngIf从DOM中删除该元素,然后单击myObj2.value旁边的编辑,调用ngOnInit方法,并且显示值设置正确。

我不想更新ngOnChanges方法中的显示值,因为在那时我基本上创建了更糟糕的ngModel版本(它没有解决我的用例)。 / p>

我希望显示模型的能力与支持值不同步,而输入具有焦点(将其视为编辑模式)。一旦输入失去焦点,输入中的显示值将更新以匹配背景模型,替换用户离开的任何无效值(在提供的示例中,任何小于500的值)。

2 个答案:

答案 0 :(得分:0)

好的,我明白了。这里有几件事情错了。

  1. 它与超时设置一起使用的唯一原因是因为你要破坏你的指令并通过将对象设置为Null来重建它,然后再给它一个值。

  2. 您的指令仅使用ngOnInit()构建标题,因此如果值已经呈现后更改,则标题中的值不会更改。 NgOnInit仅在元素生命周期中运行一次。您需要做的是改变ngOnChanges()中的标题。这将监视您的更改输入,并在检测到更改时运行您需要运行的任何代码。

  3. <强> MySampleDirective

    ngOnChanges() {
        this._element.innerText = this.myObject.value;
    }
    
    1. 最后但绝对最棘手的是Javascript通过引用传递对象。因此,将对象/数组与Angular的@Input变化检测系统结合使用可能非常棘手。因为它不会在发生变化时始终检测到变化,这可能导致下游的许多麻烦。我对数据绑定的建议要求将复杂的数据结构超出典型的字符串,数字,布尔值等,将它们移到服务中。一种可能的解决方案是每次更改对象时重建对象。
    2. <强>为MyObject

      changeMyObject(myObject: MyObject){
          this.myObject = Object.assign({}, myObject);
      }
      

答案 1 :(得分:0)

在阅读了一些反馈并结合我用ngIf看到的行为时,我手动将对象置零,这让我想到了创建一个新的结构指令。这就是我想出的:

diff

这将评估对象在更改时是否相同,如果不是,则将卸载模板,并使用绑定到模板的新对象重新加载。这纠正了我的属性值指令在切换对象时没有更新的问题。我仍然不知道这是不是最好的方式,但它正在发挥作用。