[英]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: 要了解发生了什么,让我们看一下两种情况:
User clicks the label and the event handler is triggered which changes the bound @Input value 用户单击标签,将触发事件处理程序,该事件处理程序将更改绑定的@Input值
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
事件而改变的值exampleStr
到S3
。
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:
这被过分简化,但可能有助于您的理解:
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>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.