docs(core): add docs to instructions, minor renames (#20855)

PR Close #20855
This commit is contained in:
Kara Erickson 2017-12-14 15:50:01 -08:00 committed by Igor Minar
parent 83b27bac17
commit 6cc8f2298e
4 changed files with 199 additions and 88 deletions

View File

@ -168,7 +168,8 @@ function bloomHashBit(type: viewEngine.Type<any>): number|null {
export function bloomFindPossibleInjector(
startInjector: LNodeInjector, bloomBit: number): LNodeInjector|null {
// Create a mask that targets the specific bit associated with the directive we're looking for.
// This will be a number between 0 and 31, corresponding to a bit position in a 32 bit integer.
// JS bit operations are 32 bits, so this will be a number between 2^0 and 2^31, corresponding
// to bit positions 0 - 31 in a 32 bit integer.
const mask = 1 << bloomBit;
// Traverse up the injector tree until we find a potential match or until we know there *isn't* a

View File

@ -20,17 +20,58 @@ import {appendChild, insertChild, insertView, processProjectedNode, removeView}
import {isNodeMatchingSelector} from './node_selector_matcher';
import {ComponentDef, ComponentTemplate, DirectiveDef} from './public_interfaces';
import {QueryList, QueryState_} from './query';
import {RComment, RElement, RText, Renderer3, Renderer3Fn, Renderer3oo, RendererStyleFlags3} from './renderer';
import {RComment, RElement, RText, Renderer3, ProceduralRenderer3, ObjectOrientedRenderer3, RendererStyleFlags3} from './renderer';
import {isDifferent, stringify} from './util';
export {refreshQuery} from './query';
/**
* Enum used by the directiveLifecycle (l) instruction to determine which lifecycle
* hook is requesting processing and whether it should be allowed to run. It "guards"
* certain hooks, e.g. "ngOnInit" should only run once on creation.
*/
export const enum LifeCycleGuard {ON_INIT = 1, ON_DESTROY = 2, ON_CHANGES = 4}
/**
* directiveCreate (D) sets a property on all component instances using this constant
* as a key, and the component's host node (LElement) as the value. This is used in
* methods like detectChanges to facilitate jumping from an instance to the host node.
*/
export const NG_HOST_SYMBOL = '__ngHostLNode__';
/**
* If a directive is diPublic, bloomAdd sets a property on the instance with this constant as
* the key and the directive's unique ID as the value. This allows us to map directives to their
* bloom filter bit for DI.
*/
export const NG_ELEMENT_ID = '__NG_ELEMENT_ID__';
/**
* The number of slots in each bloom filter (used by DI). The larger this number, the fewer
* directives that will share slots, and thus, the fewer false positives when checking for
* the existence of a directive.
*/
export const BLOOM_SIZE = 128;
/** Counter used to generate unique IDs for directives. */
let nextNgElementId = 0;
/**
* This property gets set before entering a template.
*
* This renderer can be one of two varieties of Renderer3:
*
* - ObjectedOrientedRenderer3
*
* This is the native browser API style. e.g. operations are methods on individual objects
* like HTMLElement. With this style, no additional code is needed (reducing payload size)
* as a facade.
*
* - ProceduralRenderer3
*
* In non-native browser environments (e.g. platforms such as web-workers), this is the facade
* that facilitates element manipulation. This also facilitates backwards compatibility with
* Renderer2.
*/
let renderer: Renderer3;
@ -129,6 +170,10 @@ export function enterView(newViewState: ViewState, host: LElement | LView | null
return oldViewState !;
}
/**
* Used in lieu of enterView to make it clear when we are exiting a child view. This makes
* the direction of traversal (up or down the view tree) a bit clearer.
*/
export const leaveView: (newViewState: ViewState) => void = enterView as any;
export function createViewState(
@ -250,29 +295,47 @@ export function renderTemplate<T>(host: LElement, template: ComponentTemplate<T>
}
}
export const NG_ELEMENT_ID = '__NG_ELEMENT_ID__';
export const BLOOM_SIZE = 128;
let nextNgElementId = 0;
export function bloomAdd(di: LNodeInjector, type: Type<any>): void {
/**
* Registers this directive as present in its node's injector by flipping the directive's
* corresponding bit in the injector's bloom filter.
*
* @param injector The injector to which the type should be added
* @param type The directive type to add
*/
export function bloomAdd(injector: LNodeInjector, type: Type<any>): void {
let id: number|undefined = (type as any)[NG_ELEMENT_ID];
// Set a unique ID on the directive type, so if something tries to inject the directive,
// we can easily retrieve the ID and hash it into the bloom bit that should be checked.
if (id == null) {
id = (type as any)[NG_ELEMENT_ID] = nextNgElementId++;
}
// We only have BLOOM_SIZE (128) slots in our bloom filter (4 buckets * 32 bits each),
// so all unique IDs must be modulo-ed into a number from 0 - 127 to fit into the filter.
// This means that after 128, some directives will share slots, leading to some false positives
// when checking for a directive's presence.
const bloomBit = id % BLOOM_SIZE;
// JS bit operations are 32 bits
// Create a mask that targets the specific bit associated with the directive.
// JS bit operations are 32 bits, so this will be a number between 2^0 and 2^31, corresponding
// to bit positions 0 - 31 in a 32 bit integer.
const mask = 1 << bloomBit;
// Use the raw bloomBit number to determine which bloom filter bucket we should check
// e.g: bf0 = [0 - 31], bf1 = [32 - 63], bf2 = [64 - 95], bf3 = [96 - 127]
if (bloomBit < 64) {
if (bloomBit < 32) {
di.bf0 |= mask;
// Then use the mask to flip on the bit (0-31) associated with the directive in that bucket
injector.bf0 |= mask;
} else {
di.bf1 |= mask;
injector.bf1 |= mask;
}
} else {
if (bloomBit < 96) {
di.bf2 |= mask;
injector.bf2 |= mask;
} else {
di.bf3 |= mask;
injector.bf3 |= mask;
}
}
}
@ -305,7 +368,7 @@ export function getOrCreateNodeInjector(): LNodeInjector {
//////////////////////////
//// ELEMENT
//// Element
//////////////////////////
/**
@ -351,7 +414,7 @@ export function elementCreate(
if (node.staticData == null) {
ngDevMode && assertDataInRange(index - 1);
node.staticData = ngStaticData[index] = createStaticData(name, attrs || null, null);
node.staticData = ngStaticData[index] = createNodeStatic(name, attrs || null, null);
}
if (attrs) setUpAttributes(native, attrs);
@ -374,9 +437,9 @@ function getTemplateStatic(template: ComponentTemplate<any>): NgStaticData {
function setUpAttributes(native: RElement, attrs: string[]): void {
ngDevMode && assertEqual(attrs.length % 2, 0, 'attrs.length % 2');
const isFnRenderer = (renderer as Renderer3Fn).setAttribute;
const isFnRenderer = (renderer as ProceduralRenderer3).setAttribute;
for (let i = 0; i < attrs.length; i += 2) {
isFnRenderer ? (renderer as Renderer3Fn).setAttribute !(native, attrs[i], attrs[i | 1]) :
isFnRenderer ? (renderer as ProceduralRenderer3).setAttribute !(native, attrs[i], attrs[i | 1]) :
native.setAttribute(attrs[i], attrs[i | 1]);
}
}
@ -394,9 +457,9 @@ export function createError(text: string, token: any) {
export function elementHost(elementOrSelector: RElement | string, def: ComponentDef<any>) {
ngDevMode && assertDataInRange(-1);
const rNode = typeof elementOrSelector === 'string' ?
((renderer as Renderer3Fn).selectRootElement ?
(renderer as Renderer3Fn).selectRootElement(elementOrSelector) :
(renderer as Renderer3oo).querySelector !(elementOrSelector)) :
((renderer as ProceduralRenderer3).selectRootElement ?
(renderer as ProceduralRenderer3).selectRootElement(elementOrSelector) :
(renderer as ObjectOrientedRenderer3).querySelector !(elementOrSelector)) :
elementOrSelector;
if (ngDevMode && !rNode) {
if (typeof elementOrSelector === 'string') {
@ -426,25 +489,25 @@ export function listenerCreate(
const node = previousOrParentNode;
const native = node.native as RElement;
// In order to match current behavior, event listeners must be added for all events (including
// outputs).
if ((renderer as Renderer3Fn).listen) {
const cleanupFn = (renderer as Renderer3Fn).listen(native, eventName, listener);
// In order to match current behavior, native DOM event listeners must be added for all
// events (including outputs).
if ((renderer as ProceduralRenderer3).listen) {
const cleanupFn = (renderer as ProceduralRenderer3).listen(native, eventName, listener);
(cleanup || (cleanup = currentView.cleanup = [])).push(cleanupFn, null);
} else {
native.addEventListener(eventName, listener, useCapture);
(cleanup || (cleanup = currentView.cleanup = [])).push(eventName, native, listener, useCapture);
}
let mergeData: LNodeStatic|null = node.staticData !;
if (mergeData.outputs === undefined) {
let staticData: LNodeStatic|null = node.staticData !;
if (staticData.outputs === undefined) {
// if we create LNodeStatic here, inputs must be undefined so we know they still need to be
// checked
mergeData.outputs = null;
mergeData = generatePropertyAliases(node.flags, mergeData);
staticData.outputs = null;
staticData = generatePropertyAliases(node.flags, staticData);
}
const outputs = mergeData.outputs;
const outputs = staticData.outputs;
let outputData: (number | string)[]|undefined;
if (outputs && (outputData = outputs[eventName])) {
outputCreate(outputData, listener);
@ -488,15 +551,15 @@ export function elementEnd() {
*/
export function elementAttribute(index: number, attrName: string, value: any): void {
if (value !== NO_CHANGE) {
const lElement = data[index] as LElement;
const element = data[index] as LElement;
if (value == null) {
(renderer as Renderer3Fn).removeAttribute ?
(renderer as Renderer3Fn).removeAttribute(lElement.native, attrName) :
lElement.native.removeAttribute(attrName);
(renderer as ProceduralRenderer3).removeAttribute ?
(renderer as ProceduralRenderer3).removeAttribute(element.native, attrName) :
element.native.removeAttribute(attrName);
} else {
(renderer as Renderer3Fn).setAttribute ?
(renderer as Renderer3Fn).setAttribute(lElement.native, attrName, value) :
lElement.native.setAttribute(attrName, value);
(renderer as ProceduralRenderer3).setAttribute ?
(renderer as ProceduralRenderer3).setAttribute(element.native, attrName, value) :
element.native.setAttribute(attrName, value);
}
}
}
@ -535,14 +598,22 @@ export function elementProperty<T>(index: number, propName: string, value: T | N
setInputsForProperty(dataValue, value);
} else {
const native = node.native;
(renderer as Renderer3Fn).setProperty ?
(renderer as Renderer3Fn).setProperty(native, propName, value) :
(renderer as ProceduralRenderer3).setProperty ?
(renderer as ProceduralRenderer3).setProperty(native, propName, value) :
native.setProperty ? native.setProperty(propName, value) :
(native as any)[propName] = value;
}
}
function createStaticData(
/**
* Constructs a LNodeStatic object from the arguments.
*
* @param tagName
* @param attrs
* @param containerStatic
* @returns the LNodeStatic object
*/
function createNodeStatic(
tagName: string | null, attrs: string[] | null,
containerStatic: (LNodeStatic | null)[][] | null): LNodeStatic {
return {
@ -610,13 +681,13 @@ export function elementClass<T>(index: number, className: string, value: T | NO_
if (value !== NO_CHANGE) {
const lElement = data[index] as LElement;
if (value) {
(renderer as Renderer3Fn).addClass ?
(renderer as Renderer3Fn).addClass(lElement.native, className) :
(renderer as ProceduralRenderer3).addClass ?
(renderer as ProceduralRenderer3).addClass(lElement.native, className) :
lElement.native.classList.add(className);
} else {
(renderer as Renderer3Fn).removeClass ?
(renderer as Renderer3Fn).removeClass(lElement.native, className) :
(renderer as ProceduralRenderer3).removeClass ?
(renderer as ProceduralRenderer3).removeClass(lElement.native, className) :
lElement.native.classList.remove(className);
}
}
@ -636,13 +707,13 @@ export function elementStyle<T>(
if (value !== NO_CHANGE) {
const lElement = data[index] as LElement;
if (value == null) {
(renderer as Renderer3Fn).removeStyle ?
(renderer as Renderer3Fn)
(renderer as ProceduralRenderer3).removeStyle ?
(renderer as ProceduralRenderer3)
.removeStyle(lElement.native, styleName, RendererStyleFlags3.DashCase) :
lElement.native.style.removeProperty(styleName);
} else {
(renderer as Renderer3Fn).setStyle ?
(renderer as Renderer3Fn)
(renderer as ProceduralRenderer3).setStyle ?
(renderer as ProceduralRenderer3)
.setStyle(
lElement.native, styleName, suffix ? stringify(value) + suffix : stringify(value),
RendererStyleFlags3.DashCase) :
@ -655,7 +726,7 @@ export function elementStyle<T>(
//////////////////////////
//// TEXT
//// Text
//////////////////////////
/**
@ -668,9 +739,9 @@ export function elementStyle<T>(
export function textCreate(index: number, value?: any): void {
ngDevMode && assertEqual(currentView.bindingStartIndex, null, 'bindingStartIndex');
const textNode = value != null ?
((renderer as Renderer3Fn).createText ?
(renderer as Renderer3Fn).createText(stringify(value)) :
(renderer as Renderer3oo).createTextNode !(stringify(value))) :
((renderer as ProceduralRenderer3).createText ?
(renderer as ProceduralRenderer3).createText(stringify(value)) :
(renderer as ObjectOrientedRenderer3).createTextNode !(stringify(value))) :
null;
const node = createLNode(index, LNodeFlags.Element, textNode);
// Text nodes are self closing.
@ -691,15 +762,15 @@ export function textCreateBound<T>(index: number, value: T | NO_CHANGE): void {
if (existingNode && existingNode.native) {
// If DOM node exists and value changed, update textContent
value !== NO_CHANGE &&
((renderer as Renderer3Fn).setValue ?
(renderer as Renderer3Fn).setValue(existingNode.native, stringify(value)) :
((renderer as ProceduralRenderer3).setValue ?
(renderer as ProceduralRenderer3).setValue(existingNode.native, stringify(value)) :
existingNode.native.textContent = stringify(value));
} else if (existingNode) {
// Node was created but DOM node creation was delayed. Create and append now.
existingNode.native =
((renderer as Renderer3Fn).createText ?
(renderer as Renderer3Fn).createText(stringify(value)) :
(renderer as Renderer3oo).createTextNode !(stringify(value)));
((renderer as ProceduralRenderer3).createText ?
(renderer as ProceduralRenderer3).createText(stringify(value)) :
(renderer as ObjectOrientedRenderer3).createTextNode !(stringify(value)));
insertChild(existingNode, currentView);
} else {
textCreate(index, value);
@ -793,7 +864,15 @@ function setInputsFromAttrs<T>(
}
/**
* Generates the initialInputData for the template's static storage.
* Generates initialInputData for a node and stores it in the template's static storage
* so subsequent template invocations don't have to recalculate it.
*
* initialInputData is an array containing values that need to be set as input properties
* for directives on this node, but only once on creation. We need this array to support
* the case where you set an @Input property of a directive using attribute-like syntax.
* e.g. if you have a `name` @Input, you can set it once like this:
*
* <my-component name="Bess"></my-component>
*
* @param directiveIndex Index to store the initial input data
* @param inputs The list of inputs from the directive def
@ -819,10 +898,34 @@ function generateInitialInputs(
return initialInputData;
}
/**
* Makes a directive public to the DI system by adding it to an injector's bloom filter.
*
* @param def The definition of the directive to be made public
*/
export function diPublic(def: DirectiveDef<any>): void {
bloomAdd(getOrCreateNodeInjector(), def.type);
}
/**
* Accepts a lifecycle hook type and determines when and how the related lifecycle hook
* callback should run.
*
* For the onInit lifecycle hook, it will return whether or not the ngOnInit() function
* should run. If so, ngOnInit() will be called outside of this function.
*
* e.g. l(LifecycleGuard.ON_INIT) && ctx.ngOnInit();
*
* For the onDestroy lifecycle hook, this instruction also accepts an onDestroy
* method that should be stored and called internally when the parent view is being
* cleaned up.
*
* e.g. l(LifecycleGuard.ON_DESTROY, ctx, ctx.onDestroy);
*
* @param lifeCycle
* @param self
* @param method
*/
export function directiveLifeCycle(
lifeCycle: LifeCycleGuard.ON_DESTROY, self: any, method: Function): void;
export function directiveLifeCycle(lifeCycle: LifeCycleGuard): boolean;
@ -848,6 +951,8 @@ export function directiveLifeCycle(
*
* @param index The index of the container in the data array
* @param template Optional inline template
* @param tagName The name of the container element, if applicable
* @param attrs The attrs attached to the container, if applicable
*/
export function containerCreate(
index: number, template?: ComponentTemplate<any>, tagName?: string, attrs?: string[]): void {
@ -876,7 +981,7 @@ export function containerCreate(
});
if (node.staticData == null) {
node.staticData = ngStaticData[index] = createStaticData(tagName || null, attrs || null, []);
node.staticData = ngStaticData[index] = createNodeStatic(tagName || null, attrs || null, []);
}
// Containers are added to the current view tree instead of their embedded views
@ -1155,23 +1260,29 @@ export function addToViewTree<T extends ViewState|ContainerState>(state: T): T {
return state;
}
/** The type of the NO_CHANGE constant. */
export type NO_CHANGE = {
brand: 'no change detected'
};
//////////////////////////
//// Bindings
//////////////////////////
/**
* A special value which designates that a value has not changed.
* The type of the NO_CHANGE constant, which can never be structurally matched
* because of its private member.
*/
export const NO_CHANGE: NO_CHANGE = {
brand: 'no change detected'
};
export declare class NO_CHANGE_TYPE {
// This is a brand that ensures that this type can never match anything else
private _;
}
// This is an alias for NO_CHANGE_TYPE for brevity throughout the codebase.
export type NO_CHANGE = typeof NO_CHANGE_TYPE;
/** A special value which designates that a value has not changed. */
export const NO_CHANGE = {} as NO_CHANGE;
/**
* Create interpolation bindings with variable number of arguments.
*
* If any of the arguments change that the interpolation is concatenated
* If any of the arguments change, then the interpolation is concatenated
* and causes an update.
*
* @param values an array of values to diff.
@ -1206,6 +1317,11 @@ export function bindV(values: any[]): string|NO_CHANGE {
}
}
// For bindings that have 0 - 7 dynamic values to watch, we can use a bind function that
// matches the number of interpolations. This is faster than using the bindV function above
// because we know ahead of time how many interpolations we'll have and don't need to
// accept the values as an array that will need to be copied and looped over.
/**
* Create a single value binding without interpolation.
*

View File

@ -10,7 +10,7 @@ import {assertNotNull} from './assert';
import {ContainerState, ProjectionState, ViewOrContainerState, ViewState} from './interfaces';
import {LContainer, LElement, LNode, LNodeFlags, LProjection, LText, LView} from './l_node';
import {assertNodeType} from './node_assert';
import {RComment, RElement, RNode, RText, Renderer3Fn} from './renderer';
import {RComment, RElement, RNode, RText, ProceduralRenderer3} from './renderer';
/**
* Finds the closest DOM node above a given container in the hierarchy.
@ -91,15 +91,15 @@ export function addRemoveViewFromContainer(
const type = node.flags & LNodeFlags.TYPE_MASK;
let nextNode: LNode|null = null;
const renderer = container.view.renderer;
const isFnRenderer = (renderer as Renderer3Fn).listen;
const isFnRenderer = (renderer as ProceduralRenderer3).listen;
if (type === LNodeFlags.Element) {
insertMode ?
(isFnRenderer ?
(renderer as Renderer3Fn)
(renderer as ProceduralRenderer3)
.insertBefore !(parent, node.native !, beforeNode as RNode | null) :
parent.insertBefore(node.native !, beforeNode as RNode | null, true)) :
(isFnRenderer ?
(renderer as Renderer3Fn).removeChild !(parent as RElement, node.native !) :
(renderer as ProceduralRenderer3).removeChild !(parent as RElement, node.native !) :
parent.removeChild(node.native !));
nextNode = node.next;
} else if (type === LNodeFlags.Container) {
@ -108,10 +108,10 @@ export function addRemoveViewFromContainer(
const childContainerData: ContainerState = (node as LContainer).data;
insertMode ?
(isFnRenderer ?
(renderer as Renderer3Fn).appendChild !(parent as RElement, node.native !) :
(renderer as ProceduralRenderer3).appendChild !(parent as RElement, node.native !) :
parent.appendChild(node.native !)) :
(isFnRenderer ?
(renderer as Renderer3Fn).removeChild !(parent as RElement, node.native !) :
(renderer as ProceduralRenderer3).removeChild !(parent as RElement, node.native !) :
parent.removeChild(node.native !));
nextNode = childContainerData.views.length ? childContainerData.views[0].child : null;
} else if (type === LNodeFlags.Projection) {
@ -332,8 +332,8 @@ export function appendChild(parent: LNode, child: RNode | null, currentView: Vie
// We only add element if not in View or not projected.
const renderer = currentView.renderer;
(renderer as Renderer3Fn).listen ?
(renderer as Renderer3Fn).appendChild !(parent.native !as RElement, child) :
(renderer as ProceduralRenderer3).listen ?
(renderer as ProceduralRenderer3).appendChild !(parent.native !as RElement, child) :
parent.native !.appendChild(child);
return true;
}
@ -373,8 +373,8 @@ export function insertChild(node: LNode, currentView: ViewState): void {
sibling = sibling.next;
}
const renderer = currentView.renderer;
(renderer as Renderer3Fn).listen ?
(renderer as Renderer3Fn).insertBefore !(parent.native !, node.native !, nativeSibling) :
(renderer as ProceduralRenderer3).listen ?
(renderer as ProceduralRenderer3).insertBefore !(parent.native !, node.native !, nativeSibling) :
parent.native !.insertBefore(node.native !, nativeSibling, false);
}
}

View File

@ -24,12 +24,10 @@ export enum RendererStyleFlags3 {
DashCase = 1 << 1
}
export type Renderer3 = Renderer3oo | Renderer3Fn;
export type Renderer3 = ObjectOrientedRenderer3 | ProceduralRenderer3;
/**
* Object Oriented style of API needed to create elements and text nodes.
*/
export interface Renderer3oo {
/** Object Oriented style of API needed to create elements and text nodes. */
export interface ObjectOrientedRenderer3 {
createComment(data: string): RComment;
createElement(tagName: string): RElement;
createTextNode(data: string): RText;
@ -37,10 +35,8 @@ export interface Renderer3oo {
querySelector(selectors: string): RElement|null;
}
/**
* Functional style of API needed to create elements and text nodes.
*/
export interface Renderer3Fn {
/** Procedural style of API needed to create elements and text nodes. */
export interface ProceduralRenderer3 {
destroy(): void;
createElement(name: string, namespace?: string|null): RElement;
createComment(value: string): RComment;
@ -77,9 +73,7 @@ export interface RendererFactory3 {
end?(): void;
}
/**
* Subset of API needed for appending elements and text nodes.
*/
/** Subset of API needed for appending elements and text nodes. */
export interface RNode {
removeChild(oldChild: RNode): void;