@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样式值。
视图的更新检测入口:core.js里的tick()函数:
位于ApplicationRef内:setHostBindingsByExecutingExpandoInstructions(tView, lView);
/** * 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; }
/** * 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); } }
/** * 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); } }
最重要的platform-browser.js里的setStyle函数:这是HTML element的样式改变最后执行的语句: