简体   繁体   English

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

[英]Angular2 ngModel: Why can it change an immutable string?

I am kind of confused here. 我在这里有点困惑。 Using ngModel i can do the following: 使用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"};
}  

Now when i type in the input, the strInObj.str gets updated. 现在,当我输入输入内容时, strInObj.str会更新。 I am confused by this, because as far as i know strings are immutable, and there is no way of finding the parent of a reference. 我对此感到困惑,因为据我所知,字符串是不可变的,并且无法找到引用的父级。

In this case I pass the str property directly, meaning that ngModel gets a reference to the string. 在这种情况下,我直接传递了str属性,这意味着ngModel获取对该字符串的引用。 If it "changes" the string on that reference a new one is created therefore not changing the original string, which strInObj.str points to. 如果它“更改”了该引用上的字符串,则会创建一个新字符串,因此不会更改strInObj.str指向的原始字符串。 At least that is my understanding. 至少那是我的理解。
And there is no way of finding the parent of the reference passed to ngModel (there is no concept like str.parent() , which would return strInObj ) So how do they do this? 而且没有办法找到传递给ngModel的引用的父级(没有类似str.parent()概念,该概念将返回strInObj )那么他们如何做到这一点? I tried to understand the ts and js sources, but...well, here i am. 我试图了解ts和js的来源,但是...嗯,我在这里。

I tried to build a similar directive, but ended up only beeing able to pass objects that wrap strings, i have not found a way to modify the original object when passing the str property directly.... (in the example i would pass strInObj to my directive, which then would work with the str property of the passed object, which works fine). 我试图建立一个类似的指令,但最终只能通过传递包装字符串的对象来实现,我没有找到直接传递str属性时修改原始对象的方法。...(在示例中,我将传递strInObj到我的指令中,然后该指令将与传递的对象的str属性一起工作,效果很好)。

I'd be glad if someone could help me unriddle this :) 如果有人可以帮助我解决这个问题,我会很高兴:)

EDIT In this plunker i have my custom directive StrDirective and an input field with a NgModel directive. 编辑 在这个插件中,我有自定义指令StrDirective和带有NgModel指令的输入字段。 Both have the same binding exampleStr which is outputted in a simple span. 两者都具有相同的绑定exampleStr ,并在简单的跨度中输出。
Now, when i enter text in the input, you can see exampleStr being updated. 现在,当我在输入中输入文本时,您可以看到exampleStr正在更新。 This is expected behaviour. 这是预期的行为。 I know that this works. 我知道这可行。
The StrDirective updates its binding when clicked on. 单击时, StrDirective更新其绑定。 You can see that it updates its "working copy" of the string, but exampleStr is not getting updated. 您可以看到它更新了字符串的“工作副本”,但是exampleStr没有得到更新。
My Question is now: How do they do it / How can i get my directive to update ExampleStr without having to wrap it in an Object? 我现在的问题是:他们如何做到/如何在不将其包装到Object中的情况下获取更新ExampleStr指令?

In Javascript, all strings are immutable. 在Javascript中,所有字符串都是不可变的。 When someone types in an input field, it updates the "working copy" of the string so that the working copy points to a new reference. 当有人在输入字段中键入内容时,它会更新字符串的“工作副本”,以便工作副本指向新的引用。 Or put another way, every time the string changes, its a new string reference. 或者换种说法,每当字符串更改时,它都会引用一个新的字符串。

When a key is pressed that changes the model, ngModelChange output event is triggered, which then updates parent component's model with the new reference. 当按下更改模型的键时,会触发ngModelChange输出事件,然后使用新的引用更新父组件的模型。 The references are now in sync. 现在,参考已同步。 When you say "modify the string that was passed to it", that's not possible because strings are immutable. 当您说“修改传递给它的字符串”时,这是不可能的,因为字符串是不可变的。

When there is two-way binding to a model: 对模型进行双向绑定时:

[(ngModel)]="str"

The binding is equivalent to: 绑定等效于:

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

The @Output ngModelChange event is triggered whenever str (which the model is bound to) changes. 每当str (绑定到模型)发生更改时,都会触发@Output ngModelChange事件。 In this way, a change in reference is propagated upwards to all components where two-way model binding is setup, so that each model points to the same reference. 这样,参考的更改会向上传播到设置了双向模型绑定的所有组件,以便每个模型都指向相同的参考。

[Edit] [编辑]

In the Plnkr from the updated question, it shows that two-way binding is being restored after the user types a key in the input box. 在更新后的问题的Plnkr中,它表明在用户在输入框中键入键后,将恢复双向绑定。 The question is how and why? 问题是如何以及为什么?

To understand what's happening let's look at the two scenarios: 要了解发生了什么,让我们看一下两种情况:

  1. User clicks the label and the event handler is triggered which changes the bound @Input value 用户单击标签,将触发事件处理程序,该事件处理程序将更改绑定的@Input值

  2. User types a key in the input box. 用户在输入框中键入一个键。 Both labels are automatically re-bound to the same reference, and two-way model binding works for both labels. 两个标签都自动重新绑定到相同的参考,并且两个标签的双向模型绑定有效。

Before the user clicks the label, all bindings are in sync: 在用户单击标签之前,所有绑定都是同步的:

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

After the click event, the bound @Input model changes. 单击事件之后,绑定的@Input模型发生更改。 Then, a round of change detection occurs starting from the root and working its way to the child components in depth first order. 然后,将进行一轮变更检测,从根开始,并以深度优先顺序处理其子组件。 Since the @Input binding propagates downwards, nothing really changes to the other bindings. 由于@Input绑定向下传播,因此其他绑定实际上没有任何变化。

In the first scenario, this is the state of the bindings after the click event: 在第一种情况下,这是单击事件后绑定的状态:

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

When the user starts to type in the textbox which has two-way binding setup, it triggers an ngModelChange event which changes the value of exampleStr to S3 . 当用户开始键入在具有双向绑定设置文本框,它触发一个ngModelChange事件而改变的值exampleStrS3

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

The default change detection strategy then kicks in, which starts from the root and works its way down to the child components in depth first order. 然后,将启动默认的更改检测策略,该策略从根开始,一直向下深入到子组件。

The state of the bindings after a key is pressed is: 按下键后的绑定状态为:

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

As you can see, all the bindings are in sync again. 如您所见,所有绑定再次同步。 The default change detection strategy checks all components; 默认的更改检测策略将检查所有组件。 changes to the model are propagated through the component's @Input bindings in a predictable and uni-directional flow from parent to child. 对模型的更改通过组件的@Input绑定在父级到子级的可预测的单向流中传播。

To understand how change detection works, think of it happening in phases. 要了解变更检测的工作原理,请分阶段进行。 This is over-simplified, but it may help with your understanding: 这被过分简化,但可能有助于您的理解:

  1. Input bindings are propagated from root to children. 输入绑定从根传播到子代。 Angular keeps track of which models are bound to which input properties. Angular跟踪哪些模型绑定到哪些输入属性。 This is needed later for change detection. 稍后需要进行更改检测。
  2. An event fires (such as a click event) which modifies @Input properties. 会触发一个事件(例如click事件),该事件会修改@Input属性。 With two-way model binding, the change in model is propagated upwards from child to parent. 通过双向模型绑定,模型更改从子级向上传播到父级。
  3. After the event fires, a single round of change detection is triggered starting from the root (repeat step 1) to re-sync all the bindings. 事件触发后,从根开始重复一轮更改检测(重复步骤1),以重新同步所有绑定。 During this process, all application and view bindings are updated. 在此过程中,将更新所有应用程序和视图绑定。

Note: Angular uses zones to monkey-patch browser events, so it knows to when to trigger change detection. 注意:Angular使用区域来猴子修补浏览器事件,因此它知道何时触发更改检测。

[Edit] [编辑]

If you want the message to be updated when the label is clicked, setup two-way binding like you would with ngModel: 如果要在单击标签时更新消息,请像使用ngModel一样设置双向绑定:

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


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

HTML HTML

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

Demo Plnkr 演示版

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM