@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的样式改变最后执行的语句:
要获取更多Jerry的原创文章,请关注公众号"汪子熙":