Angular @Hostbinding工作原理

简介: Angular @Hostbinding工作原理

@HostBinding()可以为指令的宿主元素添加类、样式、属性等。

使用@HostBinding的一个例子:

import { Directive, HostBinding, HostListener } from '@angular/core';
@Directive({
  selector: '[appRainbow]'
})
export class RainbowDirective{
  possibleColors = [
    'darksalmon', 'hotpink', 'lightskyblue', 'goldenrod', 'peachpuff',
    'mediumspringgreen', 'cornflowerblue', 'blanchedalmond', 'lightslategrey'
  ];
  @HostBinding('style.color') color: string;
  @HostBinding('style.borderColor') borderColor: string;
  @HostListener('keydown') onKeydown(){
    const colorPick = Math.floor(Math.random() * this.possibleColors.length);
    console.log('Jerry colorPick: ' + colorPick);
    this.color = this.borderColor = this.possibleColors[colorPick];
  }
}

通过这行语句:

 @HostBinding('style.color') color: string;

我们将directive内部的color属性值同host元素的css样式style.color建立了绑定关系。这样,用TypeScript修改directive的color属性值,会自动更新host元素的css样式值。image.png

视图的更新检测入口:core.js里的tick()函数:

image.png

位于ApplicationRef内:image.pngsetHostBindingsByExecutingExpandoInstructions(tView, lView);image.png

/**
 * Update a style binding on an element with the provided value.
 *
 * If the style value is falsy then it will be removed from the element
 * (or assigned a different value depending if there are any styles placed
 * on the element with `styleMap` or any static styles that are
 * present from when the element was created with `styling`).
 *
 * Note that the styling element is updated as part of `stylingApply`.
 *
 * \@codeGenApi
 * @param {?} prop A valid CSS property.
 * @param {?} value New value to write (`null` or an empty string to remove).
 * @param {?=} suffix Optional suffix. Used with scalar values to add unit such as `px`.
 *        Note that when a suffix is provided then the underlying sanitizer will
 *        be ignored.
 *
 * Note that this will apply the provided style value to the host element if this function is called
 * within a host binding function.
 *
 * @return {?}
 */
function ɵɵstyleProp(prop, value, suffix) {
    checkStylingProperty(prop, value, suffix, false);
    return ɵɵstyleProp;
}

image.png

/**
 * Common code between `ɵɵclassProp` and `ɵɵstyleProp`.
 *
 * @param {?} prop property name.
 * @param {?} value binding value.
 * @param {?} suffixOrSanitizer suffix or sanitization function
 * @param {?} isClassBased `true` if `class` change (`false` if `style`)
 * @return {?}
 */
function checkStylingProperty(prop, value, suffixOrSanitizer, isClassBased) {
    /** @type {?} */
    const lView = getLView();
    /** @type {?} */
    const tView = getTView();
    // Styling instructions use 2 slots per binding.
    // 1. one for the value / TStylingKey
    // 2. one for the intermittent-value / TStylingRange
    /** @type {?} */
    const bindingIndex = incrementBindingIndex(2);
    if (tView.firstUpdatePass) {
        stylingFirstUpdatePass(tView, prop, bindingIndex, isClassBased);
    }
    if (value !== NO_CHANGE && bindingUpdated(lView, bindingIndex, value)) {
        // This is a work around. Once PR#34480 lands the sanitizer is passed explicitly and this line
        // can be removed.
        /** @type {?} */
        let styleSanitizer;
        if (suffixOrSanitizer == null) {
            if (styleSanitizer = getCurrentStyleSanitizer()) {
                suffixOrSanitizer = (/** @type {?} */ (styleSanitizer));
            }
        }
        /** @type {?} */
        const tNode = (/** @type {?} */ (tView.data[getSelectedIndex() + HEADER_OFFSET]));
        updateStyling(tView, tNode, lView, lView[RENDERER], prop, lView[bindingIndex + 1] = normalizeAndApplySuffixOrSanitizer(value, suffixOrSanitizer), isClassBased, bindingIndex);
    }
}

image.png

/**
 * Update a simple (property name) styling.
 *
 * This function takes `prop` and updates the DOM to that value. The function takes the binding
 * value as well as binding priority into consideration to determine which value should be written
 * to DOM. (For example it may be determined that there is a higher priority overwrite which blocks
 * the DOM write, or if the value goes to `undefined` a lower priority overwrite may be consulted.)
 *
 * @param {?} tView Associated `TView.data` contains the linked list of binding priorities.
 * @param {?} tNode `TNode` where the binding is located.
 * @param {?} lView `LView` contains the values associated with other styling binding at this `TNode`.
 * @param {?} renderer Renderer to use if any updates.
 * @param {?} prop Either style property name or a class name.
 * @param {?} value Either style value for `prop` or `true`/`false` if `prop` is class.
 * @param {?} isClassBased `true` if `class` (`false` if `style`)
 * @param {?} bindingIndex Binding index of the binding.
 * @return {?}
 */
function updateStyling(tView, tNode, lView, renderer, prop, value, isClassBased, bindingIndex) {
    if (tNode.type !== 3 /* Element */) {
        // It is possible to have styling on non-elements (such as ng-container).
        // This is rare, but it does happen. In such a case, just ignore the binding.
        return;
    }
    /** @type {?} */
    const tData = tView.data;
    /** @type {?} */
    const tRange = (/** @type {?} */ (tData[bindingIndex + 1]));
    /** @type {?} */
    const higherPriorityValue = getTStylingRangeNextDuplicate(tRange) ?
        findStylingValue(tData, tNode, lView, prop, getTStylingRangeNext(tRange), isClassBased) :
        undefined;
    if (!isStylingValuePresent(higherPriorityValue)) {
        // We don't have a next duplicate, or we did not find a duplicate value.
        if (!isStylingValuePresent(value)) {
            // We should delete current value or restore to lower priority value.
            if (getTStylingRangePrevDuplicate(tRange)) {
                // We have a possible prev duplicate, let's retrieve it.
                value = findStylingValue(tData, null, lView, prop, bindingIndex, isClassBased);
            }
        }
        /** @type {?} */
        const rNode = (/** @type {?} */ (getNativeByIndex(getSelectedIndex(), lView)));
        applyStyling(renderer, isClassBased, rNode, prop, value);
    }
}

image.png

最重要的platform-browser.js里的setStyle函数:这是HTML element的样式改变最后执行的语句:

相关文章
|
2月前
|
Java Spring
🔥JSF 与 Spring 强强联手:打造高效、灵活的 Web 应用新标杆!💪 你还不知道吗?
【8月更文挑战第31天】JavaServer Faces(JSF)与 Spring 框架是常用的 Java Web 技术。本文介绍如何整合两者,发挥各自优势,构建高效灵活的 Web 应用。首先通过 `web.xml` 和 `ContextLoaderListener` 配置 Spring 上下文,在 `applicationContext.xml` 定义 Bean。接着使用 `@Autowired` 将 Spring 管理的 Bean 注入到 JSF 管理的 Bean 中。
40 0
|
5月前
|
JavaScript 前端开发 编译器
Angular 中的结构指令运行时的工作原理
Angular 中的结构指令运行时的工作原理
|
5月前
|
JavaScript 前端开发 API
vue的双向绑定的原理,和angular的对比
vue的双向绑定的原理,和angular的对比
97 0
|
11月前
|
JSON 搜索推荐 数据格式
Angular SSR 应用中 serverApp-state script 的工作原理介绍
Angular SSR 应用中 serverApp-state script 的工作原理介绍
|
12月前
|
JavaScript
关于 Angular 的 HostBinding 装饰器
关于 Angular 的 HostBinding 装饰器
|
JavaScript
Angular @Inject 注解的实际应用例子和工作原理浅析
Angular @Inject 注解的实际应用例子和工作原理浅析
|
JavaScript 前端开发
Angular @Injectable 注解的工作原理浅析
Angular @Injectable 注解的工作原理浅析
|
JavaScript 前端开发 容器
从编译后的代码,分析 Angular @Injectable 的工作原理
从编译后的代码,分析 Angular @Injectable 的工作原理
|
JavaScript 前端开发
Angular @Injectable 注解的工作原理浅析
Angular @Injectable 注解的工作原理浅析
246 0
Angular @Injectable 注解的工作原理浅析
|
JavaScript 前端开发 容器
从编译后的代码,分析 Angular @Injectable 的工作原理
从编译后的代码,分析 Angular @Injectable 的工作原理
99 0