通过运行时单步调试弄清楚[(ngModel)]的双向绑定的工作原理
时间:2022-07-27
本文章向大家介绍通过运行时单步调试弄清楚[(ngModel)]的双向绑定的工作原理,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
Component HTML:
<input type="text" maxlength="40" nz-input placeholder="Input Book Name to search" [(ngModel)]="bookName" (keyup)="search(bookName)"
/>
这个bookName是Component的一个属性:
运行时渲染到包含了[(ngModel)]的模板:
创建directive实例:
创建ngModel实例:
我在UI输入一个a,因为双向绑定,Component的bookName值也会变成a,这是怎么实现的呢?
原来每当UI发生变化,触发refreshView重绘界面,会执行checkhooks:
/**
* Executing hooks requires complex logic as we need to deal with 2 constraints.
*
* 1. Init hooks (ngOnInit, ngAfterContentInit, ngAfterViewInit) must all be executed once and only
* once, across many change detection cycles. This must be true even if some hooks throw, or if
* some recursively trigger a change detection cycle.
* To solve that, it is required to track the state of the execution of these init hooks.
* This is done by storing and maintaining flags in the view: the {@link InitPhaseState},
* and the index within that phase. They can be seen as a cursor in the following structure:
* [[onInit1, onInit2], [afterContentInit1], [afterViewInit1, afterViewInit2, afterViewInit3]]
* They are are stored as flags in LView[FLAGS].
*
* 2. Pre-order hooks can be executed in batches, because of the select instruction.
* To be able to pause and resume their execution, we also need some state about the hook's array
* that is being processed:
* - the index of the next hook to be executed
* - the number of init hooks already found in the processed part of the array
* They are are stored as flags in LView[PREORDER_HOOK_FLAGS].
*/
/**
* Executes pre-order check hooks ( OnChanges, DoChanges) given a view where all the init hooks were
* executed once. This is a light version of executeInitAndCheckPreOrderHooks where we can skip read
* / write of the init-hooks related flags.
* @param {?} lView The LView where hooks are defined
* @param {?} hooks Hooks to be run
* @param {?=} nodeIndex 3 cases depending on the value:
* - undefined: all hooks from the array should be executed (post-order case)
* - null: execute hooks only from the saved index until the end of the array (pre-order case, when
* flushing the remaining hooks)
* - number: execute hooks only from the saved index until that node index exclusive (pre-order
* case, when executing select(number))
* @return {?}
*/
function executeCheckHooks(lView, hooks, nodeIndex) {
callHooks(lView, hooks, 3 /* InitPhaseCompleted */, nodeIndex);
}
函数wrapOnChangesHook_inPreviousChangesStorage:
function ɵɵNgOnChangesFeature(definition) {
if (definition.type.prototype.ngOnChanges) {
definition.setInput = ngOnChangesSetInput;
((/** @type {?} */ (definition))).onChanges = wrapOnChanges();
}
}
// This option ensures that the ngOnChanges lifecycle hook will be inherited
// from superclasses (in InheritDefinitionFeature).
/** @nocollapse */
// tslint:disable-next-line:no-toplevel-property-access
((/** @type {?} */ (ɵɵNgOnChangesFeature))).ngInherit = true;
/**
* @return {?}
*/
function wrapOnChanges() {
return (/**
* @this {?}
* @return {?}
*/
function wrapOnChangesHook_inPreviousChangesStorage() {
/** @type {?} */
const simpleChangesStore = getSimpleChangesStore(this);
/** @type {?} */
const current = simpleChangesStore && simpleChangesStore.current;
if (current) {
/** @type {?} */
const previous = (/** @type {?} */ (simpleChangesStore)).previous;
if (previous === EMPTY_OBJ) {
(/** @type {?} */ (simpleChangesStore)).previous = current;
}
else {
// New changes are copied to the previous store, so that we don't lose history for inputs
// which were not changed this time
for (let key in current) {
previous[key] = current[key];
}
}
(/** @type {?} */ (simpleChangesStore)).current = null;
this.ngOnChanges(current);
}
});
}
/**
* Wraps an event listener with a function that marks ancestors dirty and prevents default behavior,
* if applicable.
*
* @param {?} tNode The TNode associated with this listener
* @param {?} lView The LView that contains this listener
* @param {?} listenerFn The listener function to call
* @param {?} wrapWithPreventDefault Whether or not to prevent default behavior
* (the procedural renderer does this already, so in those cases, we should skip)
* @return {?}
*/
function wrapListener(tNode, lView, listenerFn, wrapWithPreventDefault) {
// Note: we are performing most of the work in the listener function itself
// to optimize listener registration.
return (/**
* @param {?} e
* @return {?}
*/
function wrapListenerIn_markDirtyAndPreventDefault(e) {
// Ivy uses `Function` as a special token that allows us to unwrap the function
// so that it can be invoked programmatically by `DebugNode.triggerEventHandler`.
if (e === Function) {
return listenerFn;
}
// In order to be backwards compatible with View Engine, events on component host nodes
// must also mark the component view itself dirty (i.e. the view that it owns).
/** @type {?} */
const startView = tNode.flags & 2 /* isComponentHost */ ?
getComponentLViewByIndex(tNode.index, lView) :
lView;
// See interfaces/view.ts for more on LViewFlags.ManualOnPush
if ((lView[FLAGS] & 32 /* ManualOnPush */) === 0) {
markViewDirty(startView);
}
/** @type {?} */
let result = executeListenerWithErrorHandling(lView, listenerFn, e);
// A just-invoked listener function might have coalesced listeners so we need to check for
// their presence and invoke as needed.
/** @type {?} */
let nextListenerFn = ((/** @type {?} */ (wrapListenerIn_markDirtyAndPreventDefault))).__ngNextListenerFn__;
while (nextListenerFn) {
// We should prevent default if any of the listeners explicitly return false
result = executeListenerWithErrorHandling(lView, nextListenerFn, e) && result;
nextListenerFn = ((/** @type {?} */ (nextListenerFn))).__ngNextListenerFn__;
}
if (wrapWithPreventDefault && result === false) {
e.preventDefault();
// Necessary for legacy browsers that don't support preventDefault (e.g. IE)
e.returnValue = false;
}
return result;
});
}
来到forms.js的_handleInput(value):
- 自定义Unity对象生命周期管理集成ADO.NET Entity Framework
- Mono运行于IPhone之上
- Apache Hadoop入门
- 参数化(三):参数嗅探
- 什么是内存数据库以及它如何有效保存数据
- DATETIME类型和BIGINT 类型互相转换
- 参数化(二):执行查询的方式
- java 的序列化和反序列化的问题
- ASP.NET 5 Beta 8 发布
- 参数化(一):计划缓存
- 在TensorFlow中对比两大生成模型:VAE与GAN(附测试代码)
- NOSQL(一)--Redis
- WCF Web API 说再见,继承者ASP.NET Web API
- 三步教你搭建给黑白照片上色的神经网络 !(附代码)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- python爬虫----(6. scrapy框架,抓取亚马逊数据)
- 十五、这一次,彻底弄懂Promise的使用
- python爬虫----(5. scrapy框架,综合应用及其他)
- 十六、半小时掌握ES6常用知识,覆盖80%实践场景
- python爬虫----(4. scrapy框架,官方文档以及例子)
- python爬虫----(3. scrapy框架,简单应用)
- python爬虫----(2. scrapy框架)
- python爬虫----(1. 基本模块)
- 七日Python之路--第十二天(Django Web 开发指南)
- 三日php之路 -- 第一天(php语言参考)
- 三日php之路 -- 第一天(初识php)
- NoSQL数据库 -- MongoDB
- 数据抓取练习
- python基础 -- 简单实现HTTP协议
- RabbitMQ 学习