重置 AngularJS Material 将自定义组件设计为原始

reset AngularJS Material Design custom component to pristine

我有一个 AngularJS 自定义组件,它基本上围绕 Material 设计 mdSelect,但通过其绑定提供了可用、默认和当前值的简单配置。

但是该组件在 mdDialog 中用作通用编辑组件,可以根据正在编辑的内容更改其选项。因此有一个 "Next" 按钮可以转到下一个要编辑的 "thing"。按下按钮时,自定义组件将具有新的可用值、默认值和当前值——类似这样:

<foo-component default-value="dialog.getDefaultFoo()" current-foo="dialog.currentFoo">
</foo-component>

请注意组件,如果未给出可用值列表(如上例所示),则组件假定只有一个值的值列表,"default-value" 表示。

因此,当用户 selects "Next" 时,mdSelect 中的值列表将更改,因为 return 由 dialog.getDefaultFoo() 编辑的值.新的 selected 值将是 dialog.currentFoo.

但是如果 dialog.currentFoonull,我希望控件自动 select 指示的默认值,或者如果没有指示默认值,则第一个可用值。当使用 $onInit 创建组件时,这很容易。但是一旦它被创建,我怎么知道(在组件内部)用户已经 selected "Next" 并且可用值列表已经改变?

在 "Next" 按钮的代码中,我调用了 this.fooForm.$setPristine()。根据 documentation,调用此方法时表单控制器将 "propagate to all the controls contained in this form." 所以我考虑让我的自定义控件挂钩检测正在调用 $setPristine(),以便它可以自动 select 如果新值为 null,则为列表中的默认值。但是现在我又回到了同样的情况:我的自定义组件如何检测到 $setPristine() 在表单上被调用?

本质上,我的自定义组件需要检测其绑定值之一何时更改,并在特定条件下对其他值执行一些自定义更新。我知道我可以在 外部 自定义组件中使用 getter/settter,但是自定义组件如何检测到其绑定值之一已更改?

更复杂的是,dialog.currentFoo 实际上是一个函数,我的组件将其识别为 getter/setter 函数,它将根据对话框的状态 return/update 正确的值.所以我什至无法检测到这个值已经改变,因为实际函数永远不会改变——只有它 return 的值会改变。

实际上比这更复杂,因为 mdSelect 只是发送到 dialog.currentFoo 的对象的一个​​ 部分 ;否则它不会传播到组件外部。

总结一下,我需要在自定义组件中知道绑定 dialog.currentFoo(实际上是 getter/setter 方法)现在 return null 是否如此自定义组件可以 select 基于内部 mdSelect 中列出的当前项目(也是动态的)的默认值(也是动态的)。我会接受解决方法,例如检测 $setPristine() 已在封闭表单上调用。我什至会接受 hack,例如当某些外部状态发生变化时强制 AngularJS 重新创建自定义组件。

这很棘手,因为 AngularJS 很棘手,而且很难跟踪自定义 AngularJS control/input 组件的信息。

首先需要做的第一件事就是make the custom component use the normal ngModel framework; trying to "fake" it using a two-way bound currentValue doesn't cut it for this complexity, and besides, ngModel is the "correct" AngularJS way to do it. To pull this off you'll need to use NgModelController. Finding out how to do this with a component is difficult in itself, although one page给出神奇的公式。它是这样的:

require: {
  ngModelCtrl: "ngModel"
},
bindings: {
  ngModel: "<"
  //TODO add defaultValue or whatever other properties here
},

那么你不直接访问two-way值;您使用 NgModelController 接口:

//get the current value
const foo = this.ngModelCtrl.$modelValue;
//set the current value
this.ngModelCtrl.$setViewValue(foo);

据我所知,如果您将 ng-model-options="{getterSetter:true}" 添加到组件,ngModelOptions 将自动应用于您的模型,并自动调用 getters/setters。

现在您已连接到整个 ngModel 框架,并使用 NgModelController 到 add/access 验证器和各种高级功能。

回到我的需求,在某些情况下我需要 "reset the component",我可以修补到 NgModelController#$setPristine()。表单控制器识别出我的组件是 ngModel 框架的一部分,并在表单重置为原始状态时调用 $setPristine()

  this.$onInit = () => {

    this.ngModelCtrl.$setPristine = () => {
      myInternalValue1 = null;
      myInternalValue2 = this.defaultValue;
    };

    this.ngModelCtrl.$setPristine(); //initialize default value
  };

这解释了我对表单控制器的疑虑 "propagat[ing $setPristine()] to all the controls contained in this form",我在原来的问题中提到过。秘诀在于该组件必须成为真实 ngModel 系统的一部分,以便它可以像 AngularJS 期望的那样进行交互,并获得对重要内部方法的访问权。