Angular2 ngModel:为什么它可以更改不可变的字符串

Angular2 ngModel: Why can it change an immutable string?

本文关键字:不可变 字符串 ngModel 为什么 Angular2      更新时间:2023-09-26

我在这里有点困惑。使用 ngModel 我可以执行以下操作:

import {NgModel} from "angular2/common";
@Component({
  selector: "test",
  template: `<input tpye="text" [(ngModel)]="strInObj.str">  {{strInObj|json}}`,
  directives: [NgModel]
})
class Test{
  strInObj: {str: string} = {str: "this is a string"};
}  

现在,当我输入输入时,strInObj.str会更新。我对此感到困惑,因为据我所知,字符串是不可变的,并且无法找到引用的父级。

在本例中,我直接传递 str 属性,这意味着ngModel获取对字符串的引用。如果它"更改"该引用上的字符串,则会创建一个新字符串,因此不会更改strInObj.str指向的原始字符串。至少这是我的理解。
并且没有办法找到传递给ngModel的引用的父级(没有像str.parent()这样的概念,它会返回strInObj)那么他们是怎么做到的呢?我试图了解 ts 和 js 源代码,但是...好吧,我来了。

我试图构建一个类似的指令,但最终只能传递包装字符串的对象,在直接传递 str 属性时,我没有找到修改原始对象的方法...... (在示例中,我将strInObj传递给我的指令,然后它将与传递对象的 str 属性一起使用,该属性工作正常)。

如果有人能帮我解开这个谜语,我会很高兴:)

编辑在这个 plunker 中,我有我的自定义指令StrDirective和一个带有 NgModel 指令的输入字段。两者都具有相同的绑定exampleStr,该绑定在简单的跨度中输出。
现在,当我在输入中输入文本时,您可以看到exampleStr正在更新。这是预期行为。我知道这行得通。
单击StrDirective时会更新其绑定。您可以看到它更新了字符串的"工作副本",但exampleStr没有更新。
我现在的问题是:他们怎么做/我怎样才能让我的指令更新ExampleStr而不必将其包装在对象中?

在Javascript中,所有字符串都是不可变的。当有人输入输入字段时,它会更新字符串的"工作副本",以便工作副本指向新的引用。 或者换句话说,每次字符串更改时,它都是一个新的字符串引用。

当按下更改模型的键时,将触发ngModelChange输出事件,然后使用新引用更新父组件的模型。引用现在处于同步状态。当你说"修改传递给它的字符串"时,这是不可能的,因为字符串是不可变的。

当存在与模型的双向绑定时:

[(ngModel)]="str"

绑定等效于:

[ngModel]="str" (ngModelChange)="str=$event"

每当str(模型绑定到的)更改时,都会触发 @Output ngModelChange 事件。通过这种方式,参照中的更改将向上传播到设置了双向模型绑定的所有组件,以便每个模型指向同一参照。

[编辑]

在更新问题的 Plnkr 中,它显示用户在输入框中键入键后正在恢复双向绑定。问题是如何以及为什么?

为了了解正在发生的事情,让我们看一下两种情况:

  1. 用户单击标签并触发事件处理程序,从而更改绑定@Input值

  2. 用户在输入框中键入一个键。 两个标签会自动重新绑定到同一参照,双向模型绑定适用于两个标签。

在用户单击标签之前,所有绑定都是同步的:

                       S1 (app component)
                       /'
(exampleStr binding) S1  S1 (str component)

单击事件后,绑定@Input模型将更改。然后,从根开始进行一轮更改检测,并按顺序深入到子组件。 由于@Input绑定向下传播,因此其他绑定实际上没有任何变化。

在第一种情况下,这是 click 事件之后绑定的状态:

                       S1 (app component)
                       /'
(exampleStr binding) S1  S2 (str component)

当用户开始在具有双向绑定设置的文本框中键入内容时,它会触发一个 ngModelChange 事件,该事件将 exampleStr 的值更改为 S3

                       S3 (app component)
                       /'
(exampleStr binding) S3  S2 (str component)

然后,默认更改检测策略启动,该策略从根开始,并按顺序深入到子组件。

按下某个键后绑定的状态为:

                       S3 (app component)
                       /'
(exampleStr binding) S3  S3 (str component)

如您所见,所有绑定再次同步。默认更改检测策略检查所有组件;对模型的更改通过组件的@Input绑定以可预测的单向流从父级传播到子级。

若要了解更改检测的工作原理,请考虑它分阶段发生。这过于简化,但它可能有助于您的理解:

  1. 输入绑定从根传播到子绑定。Angular 跟踪哪些模型绑定到哪些输入属性。稍后需要执行此操作以进行更改检测。
  2. 触发一个事件(如单击事件),用于修改@Input属性。使用双向模型绑定,模型中的更改从子项向上传播到父项。
  3. 事件触发后,将从根目录开始触发一轮更改检测(重复步骤 1),以重新同步所有绑定。在此过程中,将更新所有应用程序和视图绑定。

注意:Angular 使用区域来修补猴子浏览器事件,因此它知道何时触发更改检测。

[编辑]

如果您希望在单击标签时更新消息,请像使用 ngModel 一样设置双向绑定:

@Input("str") value : string;
@Output("strChange") valueChange:EventEmitter<string> = new EventEmitter();

onClick(){
  this.value = "new string";
  this.valueChange.next(this.value);
}

.HTML

<span [(str)]="exampleStr"></span><br>

演示平台