@@ -10,7 +10,11 @@ import './ng_dev_mode';
1010
1111import { Type } from '../core' ;
1212import { assertEqual , assertLessThan , assertNotEqual , assertNotNull } from './assert' ;
13- import { CSSSelector , ContainerState , InitialInputData , InitialInputs , LContainer , LContainerStatic , LElement , LNode , LNodeFlags , LNodeInjector , LNodeStatic , LProjection , LText , LView , MinificationData , MinificationDataValue , ProjectionState , QueryState , ViewState } from './interfaces' ;
13+ import {
14+ CSSSelector , ContainerState , InitialInputData , InitialInputs , LContainer , LContainerStatic , LElement , LNode ,
15+ LNodeFlags , LNodeInjector , LNodeStatic , LProjection , LText , LView , MinificationData , MinificationDataValue ,
16+ ProjectionState , QueryState , ViewState , NgStaticData
17+ } from './interfaces' ;
1418import { assertNodeType } from './node_assert' ;
1519import { appendChild , insertChild , insertView , processProjectedNode , removeView } from './node_manipulation' ;
1620import { isNodeMatchingSelector } from './node_selector_matcher' ;
@@ -48,12 +52,12 @@ let isParent: boolean;
4852 * in the data array. Any nodes that do not have static data store a null
4953 * value to avoid a sparse array.
5054 */
51- let ngStaticData : ( LNodeStatic | null ) [ ] ;
55+ let ngStaticData : NgStaticData ;
5256
5357/**
5458 * State of the current view being processed.
5559 */
56- let currentView : ViewState = createViewState ( null ! , null ! ) ;
60+ let currentView : ViewState = createViewState ( null ! , null ! , [ ] ) ;
5761
5862let currentQuery : QueryState | null ;
5963
@@ -68,18 +72,6 @@ let creationMode: boolean;
6872 */
6973let data : any [ ] ;
7074
71- /**
72- * An array of directives in the current view
73- *
74- * even indices: contain the directive instance.
75- * odd indices: contain the directive def
76- *
77- * We must store the directive def (rather than token | null)
78- * because we need to be able to access the inputs and outputs
79- * of directives that aren't diPublic.
80- */
81- let directives : any [ ] ;
82-
8375/**
8476 * Points to the next binding index to read or write to.
8577 */
@@ -117,9 +109,9 @@ let cleanup: any[]|null;
117109 */
118110export function enterView ( newViewState : ViewState , host : LElement | LView | null ) : ViewState {
119111 const oldViewState = currentView ;
120- directives = newViewState . directives ;
121112 data = newViewState . data ;
122113 bindingIndex = newViewState . bindingStartIndex || 0 ;
114+ ngStaticData = newViewState . ngStaticData ;
123115
124116 if ( creationMode = ! data ) {
125117 // Absence of data implies creationMode.
@@ -139,13 +131,13 @@ export function enterView(newViewState: ViewState, host: LElement | LView | null
139131
140132export const leaveView : ( newViewState : ViewState ) => void = enterView as any ;
141133
142- export function createViewState ( viewId : number , renderer : Renderer3 ) : ViewState {
134+ export function createViewState ( viewId : number , renderer : Renderer3 , ngStaticData : NgStaticData ) : ViewState {
143135 const newView = {
144136 parent : currentView ,
145137 id : viewId , // -1 for component views
146138 node : null ! , // until we initialize it in createNode.
147139 data : null ! , // Hack use as a marker for creationMode
148- directives : [ ] ,
140+ ngStaticData : ngStaticData ,
149141 cleanup : null ,
150142 renderer : renderer ,
151143 child : null ,
@@ -206,10 +198,10 @@ export function createLNode(
206198 data [ index ] = node ;
207199
208200 // Every node adds a value to the static data array to avoid a sparse array
209- if ( ngStaticData && index >= ngStaticData . length ) {
201+ if ( index >= ngStaticData . length ) {
210202 ngStaticData [ index ] = null ;
211- } else if ( ngStaticData ) {
212- node . staticData = ngStaticData [ index ] ;
203+ } else {
204+ node . staticData = ngStaticData [ index ] as LNodeStatic ;
213205 }
214206
215207 // Now link ourselves into the tree.
@@ -248,9 +240,9 @@ export function createLNode(
248240export function renderTemplate < T > ( host : LElement , template : ComponentTemplate < T > , context : T ) {
249241 const hostView = host . data ! ;
250242 ngDevMode && assertNotEqual ( hostView , null , 'hostView' ) ;
243+ hostView . ngStaticData = getTemplateStatic ( template ) ;
251244 const oldView = enterView ( hostView , host ) ;
252245 try {
253- ngStaticData = template . ngStaticData || ( template . ngStaticData = [ ] as never ) ;
254246 template ( context , creationMode ) ;
255247 } finally {
256248 leaveView ( oldView ) ;
@@ -345,13 +337,19 @@ export function elementCreate(
345337 throw 'for now name is required' ;
346338 } else {
347339 native = renderer . createElement ( name ) ;
340+
341+ let componentView : ViewState | null = null ;
342+ if ( isHostElement ) {
343+ const ngStaticData = getTemplateStatic ( ( nameOrComponentDef as ComponentDef < any > ) . template ) ;
344+ componentView = addToViewTree ( createViewState ( - 1 , renderer , ngStaticData ) ) ;
345+ }
346+
348347 // Only component views should be added to the view tree directly. Embedded views are
349348 // accessed through their containers because they may be removed / re-added later.
350- node = createLNode (
351- index , LNodeFlags . Element , native ,
352- isHostElement ? addToViewTree ( createViewState ( - 1 , renderer ) ) : null ) ;
349+ node = createLNode ( index , LNodeFlags . Element , native , componentView ) ;
353350
354351 if ( node . staticData == null ) {
352+ ngDevMode && assertDataInRange ( index - 1 ) ;
355353 node . staticData = ngStaticData [ index ] = createStaticData ( name , attrs || null , null ) ;
356354 }
357355
@@ -362,6 +360,17 @@ export function elementCreate(
362360 return native ;
363361}
364362
363+ /**
364+ * Gets static data from a template function or creates a new static
365+ * data array if it doesn't already exist.
366+ *
367+ * @param template The template from which to get static data
368+ * @returns NgStaticData
369+ */
370+ function getTemplateStatic ( template : ComponentTemplate < any > ) : NgStaticData {
371+ return template . ngStaticData || ( template . ngStaticData = [ ] as never ) ;
372+ }
373+
365374function setUpAttributes ( native : RElement , attrs : string [ ] ) : void {
366375 ngDevMode && assertEqual ( attrs . length % 2 , 0 , 'attrs.length % 2' ) ;
367376 const isFnRenderer = ( renderer as Renderer3Fn ) . setAttribute ;
@@ -381,7 +390,7 @@ export function createError(text: string, token: any) {
381390 *
382391 * @param elementOrSelector Render element or CSS selector to locate the element.
383392 */
384- export function elementHost ( elementOrSelector : RElement | string ) {
393+ export function elementHost ( elementOrSelector : RElement | string , def : ComponentDef < any > ) {
385394 ngDevMode && assertDataInRange ( - 1 ) ;
386395 const rNode = typeof elementOrSelector === 'string' ?
387396 ( ( renderer as Renderer3Fn ) . selectRootElement ?
@@ -395,7 +404,7 @@ export function elementHost(elementOrSelector: RElement | string) {
395404 throw createError ( 'Host node is required:' , elementOrSelector ) ;
396405 }
397406 }
398- createLNode ( 0 , LNodeFlags . Element , rNode , createViewState ( - 1 , renderer ) ) ;
407+ createLNode ( 0 , LNodeFlags . Element , rNode , createViewState ( - 1 , renderer , getTemplateStatic ( def . template ) ) ) ;
399408}
400409
401410
@@ -446,9 +455,9 @@ export function listenerCreate(
446455 */
447456function outputCreate ( outputs : ( number | string ) [ ] , listener : Function ) : void {
448457 for ( let i = 0 ; i < outputs . length ; i += 2 ) {
449- ngDevMode && assertDirectivesInRange ( ( outputs [ i ] as number ) << 1 ) ;
458+ ngDevMode && assertDataInRange ( outputs [ i ] as number ) ;
450459 const subscription =
451- directives [ ( outputs [ i ] as number ) << 1 ] [ outputs [ i | 1 ] ] . subscribe ( listener ) ;
460+ data [ outputs [ i ] as number ] [ outputs [ i | 1 ] ] . subscribe ( listener ) ;
452461 cleanup ! . push ( subscription . unsubscribe , subscription ) ;
453462 }
454463}
@@ -512,8 +521,7 @@ export function elementProperty<T>(index: number, propName: string, value: T | N
512521
513522 let staticData : LNodeStatic | null = node . staticData ! ;
514523 // if staticData.inputs is undefined, a listener has created output staticData, but inputs haven't
515- // yet been
516- // checked
524+ // yet been checked
517525 if ( staticData . inputs === undefined ) {
518526 // mark inputs as checked
519527 staticData . inputs = null ;
@@ -541,7 +549,8 @@ function createStaticData(
541549 attrs,
542550 initialInputs : undefined ,
543551 inputs : undefined ,
544- outputs : undefined , containerStatic
552+ outputs : undefined ,
553+ containerStatic : containerStatic
545554 } ;
546555}
547556
@@ -551,8 +560,8 @@ function createStaticData(
551560 */
552561function setInputsForProperty ( inputs : ( number | string ) [ ] , value : any ) : void {
553562 for ( let i = 0 ; i < inputs . length ; i += 2 ) {
554- ngDevMode && assertDirectivesInRange ( inputs [ i ] as number << 1 ) ;
555- directives [ ( inputs [ i ] as number ) << 1 ] [ inputs [ i | 1 ] ] = value ;
563+ ngDevMode && assertDataInRange ( inputs [ i ] as number ) ;
564+ data [ inputs [ i ] as number ] [ inputs [ i | 1 ] ] = value ;
556565 }
557566}
558567
@@ -568,7 +577,7 @@ function generateMinifiedData(flags: number, data: LNodeStatic, isInputData = fa
568577 const size = ( flags & LNodeFlags . SIZE_MASK ) >> LNodeFlags . SIZE_SHIFT ;
569578
570579 for ( let i = start , ii = start + size ; i < ii ; i ++ ) {
571- const directiveDef : DirectiveDef < any > = directives [ ( i << 1 ) | 1 ] ;
580+ const directiveDef : DirectiveDef < any > = ngStaticData ! [ i ] as DirectiveDef < any > ;
572581 const minifiedPropertyMap : { [ minifiedKey : string ] : string } =
573582 isInputData ? directiveDef . inputs : directiveDef . outputs ;
574583 for ( let unminifiedKey in minifiedPropertyMap ) {
@@ -716,11 +725,10 @@ export function directiveCreate<T>(index: number, directive: T, directiveDef: Di
716725export function directiveCreate < T > (
717726 index : number , directive ?: T , directiveDef ?: DirectiveDef < T > ) : T {
718727 let instance ;
719- const index2 = index << 1 ;
720728 if ( directive == null ) {
721729 // return existing
722- ngDevMode && assertDirectivesInRange ( index2 ) ;
723- instance = directives [ index2 ] ;
730+ ngDevMode && assertDataInRange ( index ) ;
731+ instance = data [ index ] ;
724732 } else {
725733 ngDevMode && assertEqual ( currentView . bindingStartIndex , null , 'bindingStartIndex' ) ;
726734 ngDevMode && assertPreviousIsParent ( ) ;
@@ -734,19 +742,26 @@ export function directiveCreate<T>(
734742 }
735743 previousOrParentNode ! . flags = flags ;
736744
737- ngDevMode && assertDirectivesInRange ( index2 - 1 ) ;
745+ ngDevMode && assertDataInRange ( index - 1 ) ;
738746 Object . defineProperty (
739747 directive , NG_HOST_SYMBOL , { enumerable : false , value : previousOrParentNode } ) ;
740- directives [ index2 ] = instance = directive ;
741- directives [ index2 | 1 ] = directiveDef ;
748+ data [ index ] = instance = directive ;
749+
750+ if ( index >= ngStaticData . length ) {
751+ ngStaticData [ index ] = directiveDef ! ;
752+ }
753+
742754 const diPublic = directiveDef ! . diPublic ;
743755 if ( diPublic ) {
744756 diPublic ( directiveDef ! ) ;
745757 }
746- const nodeBindings : LNodeStatic | null = previousOrParentNode . staticData ;
747- if ( nodeBindings && nodeBindings . attrs )
748- setInputsFromAttrs < T > ( instance , directiveDef ! . inputs , nodeBindings ) ;
758+
759+ const staticData : LNodeStatic | null = previousOrParentNode . staticData ! ;
760+ if ( staticData && staticData . attrs ) {
761+ setInputsFromAttrs < T > ( instance , directiveDef ! . inputs , staticData ) ;
762+ }
749763 }
764+
750765 return instance ;
751766}
752767
@@ -936,31 +951,33 @@ export function viewCreate(viewBlockId: number): boolean {
936951 enterView ( ( existingView as LView ) . data , previousOrParentNode as LView ) ;
937952 } else {
938953 // When we create a new View, we always reset the state of the instructions.
939- const newViewState = createViewState ( viewBlockId , renderer ) ;
954+ const newViewState = createViewState ( viewBlockId , renderer , initViewStaticData ( viewBlockId , container ) ) ;
940955 enterView ( newViewState , createLNode ( null , LNodeFlags . View , null , newViewState ) ) ;
941956 containerState . nextIndex ++ ;
942957 }
943- setNgStaticDataForView ( viewBlockId ) ;
944958
945959 return ! viewUpdateMode ;
946960}
947961
948962/**
949- * Each embedded view needs to set the global ngStaticData variable to the static data for that
950- * view.
951- * Otherwise, the view's static data for a particular node would overwrite the static
952- * data for a node in the view above it with the same index (since it's in the same template).
963+ * Initialize the static data for the active view.
964+ *
965+ * Each embedded view needs to set the global ngStaticData variable to the static data for
966+ * that view. Otherwise, the view's static data for a particular node would overwrite
967+ * the staticdata for a node in the view above it with the same index (since it's in the
968+ * same template).
953969 *
954970 * @param viewIndex The index of the view's static data in containerStatic
971+ * @param parent The parent container in which to look for the view's static data
972+ * @returns NgStaticData
955973 */
956- function setNgStaticDataForView ( viewIndex : number ) : void {
957- ngDevMode && assertNodeType ( previousOrParentNode . parent ! , LNodeFlags . Container ) ;
958- const containerStatic =
959- ( previousOrParentNode . parent ! . staticData as LContainerStatic ) . containerStatic ;
974+ function initViewStaticData ( viewIndex : number , parent : LContainer ) : NgStaticData {
975+ ngDevMode && assertNodeType ( parent , LNodeFlags . Container ) ;
976+ const containerStatic = ( parent ! . staticData as LContainerStatic ) . containerStatic ;
960977 if ( viewIndex >= containerStatic . length || containerStatic [ viewIndex ] == null ) {
961978 containerStatic [ viewIndex ] = [ ] ;
962979 }
963- ngStaticData = containerStatic [ viewIndex ] ;
980+ return containerStatic [ viewIndex ] ;
964981}
965982
966983/**
@@ -998,18 +1015,14 @@ export const refreshComponent:
9981015 const element = data ! [ elementIndex ] as LElement ;
9991016 ngDevMode && assertNodeType ( element , LNodeFlags . Element ) ;
10001017 ngDevMode && assertNotEqual ( element . data , null , 'isComponent' ) ;
1001- ngDevMode && assertDirectivesInRange ( directiveIndex << 1 ) ;
1018+ ngDevMode && assertDataInRange ( directiveIndex ) ;
10021019 const hostView = element . data ! ;
10031020 ngDevMode && assertNotEqual ( hostView , null , 'hostView' ) ;
1004- const directive = directives [ directiveIndex << 1 ] ;
1021+ const directive = data [ directiveIndex ] ;
10051022 const oldView = enterView ( hostView , element ) ;
1006- const oldNgStaticData = ngStaticData ;
10071023 try {
1008- const _template = template || this ! . template ;
1009- ngStaticData = _template . ngStaticData || ( _template . ngStaticData = [ ] as never ) ;
1010- _template ( directive , creationMode ) ;
1024+ ( template || this ! . template ) ( directive , creationMode ) ;
10111025 } finally {
1012- ngStaticData = oldNgStaticData ;
10131026 leaveView ( oldView ) ;
10141027 }
10151028} ;
@@ -1557,6 +1570,11 @@ function valueInData<T>(data: any[], index: number, value?: T): T {
15571570 if ( value === undefined ) {
15581571 value = data [ index ] ;
15591572 } else {
1573+ // We don't store any static data for local variables, so the first time
1574+ // we see the template, we should store as null to avoid a sparse array
1575+ if ( index >= ngStaticData . length ) {
1576+ ngStaticData [ index ] = null ;
1577+ }
15601578 data [ index ] = value ;
15611579 }
15621580 return value ! ;
@@ -1585,6 +1603,3 @@ function assertDataInRange(index: number, arr?: any[]) {
15851603 assertLessThan ( arr ? arr . length : 0 , index , 'data.length' ) ;
15861604}
15871605
1588- function assertDirectivesInRange ( index : number ) {
1589- assertLessThan ( directives ? directives . length : 0 , index , 'directives.length' ) ;
1590- }
0 commit comments