昨天介绍了自制 two-way binding 的方式,今天要接着看在背後 Angular 为我们做了哪些事!
今天会从以下的范例程序码开始,逐步找出 Angular 为什麽可以做到 two-way binding。
<app-slider-wrapper [(sliderValue)]="slider"></app-slider-wrapper>
<div>
<label>Slider value: </label><label>{{ slider }}</label>
</div>
↑ Block 1
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineComponent"]({
// ... 略
consts: [[3, "sliderValue", "sliderValueChange"]],
template: function AppComponent_Template(rf, ctx) {
if (rf & 1) {
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵelementStart"](
0,
"app-slider-wrapper",
0
);
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵlistener"](
"sliderValueChange",
function AppComponent_Template_app_slider_wrapper_sliderValueChange_0_listener(
$event
) {
return (ctx.slider = $event);
}
);
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵelementEnd"]();
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵelementStart"](1, "div");
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵelementStart"](2, "label");
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵtext"](3, "Slider value: ");
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵelementEnd"]();
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵelementStart"](4, "label");
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵtext"](5);
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵelementEnd"]();
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵelementEnd"]();
}
if (rf & 2) {
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵproperty"](
"sliderValue",
ctx.slider
);
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵadvance"](5);
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵtextInterpolate"](
ctx.slider
);
}
},
directives: [
_slider_wrapper_slider_wrapper_component__WEBPACK_IMPORTED_MODULE_1__[
"SliderWrapperComponent"
],
]
});
↑ Block 2:被编译後的程序码片段
Block 2 是 Block 1 程序码被编译後的一小片段。可以发现在 consts
属性内,有一个阵列包含着 sliderValue
与 sliderValueChange
,以目前现有的结果来看,我们可以大胆的推测 Angular 在编译时期,会将 [(property)]
翻成 property
以及对应的 propertyChange
。
而相关的证据如下:
if (bindParts[IDENT_BANANA_BOX_IDX]) {
this._bindingParser.parsePropertyBinding(
bindParts[IDENT_BANANA_BOX_IDX],
value,
false,
srcSpan,
absoluteOffset,
attr.valueSpan,
targetMatchableAttrs,
targetProps
);
this._parseAssignmentEvent(
bindParts[IDENT_BANANA_BOX_IDX],
value,
srcSpan,
attr.valueSpan || srcSpan,
targetMatchableAttrs,
boundEvents
);
}
↑ Block 3:Angular template parser 与 two-way binding 有关的部分(Link)
在 Block 3 呈现的程序码可以看出当 Angular parser 发现 template 中有 [( )]
的配对时,他会先呼叫 parsePropertyBinding
的方法来处理 property 本身,接着会呼叫 _praseAssignmentEvent
函式来处理对应的 event:
private _parseAssignmentEvent(
name: string,
expression: string,
sourceSpan: ParseSourceSpan,
valueSpan: ParseSourceSpan,
targetMatchableAttrs: string[][],
targetEvents: ParsedEvent[]
) {
this._bindingParser.parseEvent(
`${name}Change`,
`${expression}=$event`,
sourceSpan,
valueSpan,
targetMatchableAttrs,
targetEvents
);
}
↑ Block 4:_parseAssignmentEvent 函式内容(Link)
有看到那个熟悉的 Change
字样吗?这边就是 Angular 在编译时期将 two-way binding 从 banana in a box 的语法糖,转换成 property 以及 propertyChange 的起始点,後续再经由 compiler 的努力,就可以看到在 Block 2 中的 consts 的内容罗!
今天先解开 [( )]
Banana in a box 的语法糖为什麽在 Angular 产生的 main.js 内会不见踪影,以及为什麽只需要符合前一篇文章所述的两个条件後,就能够做到 two-way binding 的原因。
至於 Angular 是怎麽处理 event 与 property binding 的部分,就是明天的内容罗!
以下按照入团顺序列出我们团队夥伴的系列文章!
请自由参阅 ?
课程对象 软件专案开发商之业务人员与Pre-Sales、软件系统专案经理、软件系统系统分析师。 详细...
今天要介绍的是grid()方法,这个方法就像表格式排版,控件是依据指定的索引位置, 放入表格中,很适...
今天笔者想聊聊TensorFlow的矩阵运算模式,笔者向来的数学都不是太好,此次30天的尾声想要与大...
若您对於 Git 相当熟悉,你应该对於建立分支(Branch) 应该不陌生。依据 GitHub 官方...
前言 前面我们介绍了几种变数容器,例如阵列和切片,这些皆以数字做为索引,而今天要介绍的映射(map)...