@@ -24,7 +24,7 @@ import {LContainerNode, LElementNode, LNode, LNodeFlags, LProjectionNode, LTextN
2424import { assertNodeType } from './node_assert' ;
2525import { appendChild , insertChild , insertView , processProjectedNode , removeView } from './node_manipulation' ;
2626import { isNodeMatchingSelector } from './node_selector_matcher' ;
27- import { ComponentDef , ComponentTemplate , ComponentType , DirectiveDef } from './interfaces/definition' ;
27+ import { ComponentDef , ComponentTemplate , ComponentType , DirectiveDef , DirectiveType } from './interfaces/definition' ;
2828import { InjectFlags , diPublicInInjector , getOrCreateNodeInjectorForNode , getOrCreateElementRef , getOrCreateTemplateRef , getOrCreateContainerRef , getOrCreateInjectable } from './di' ;
2929import { QueryList , LQuery_ } from './query' ;
3030import { RComment , RElement , RText , Renderer3 , RendererFactory3 , ProceduralRenderer3 , ObjectOrientedRenderer3 , RendererStyleFlags3 } from './interfaces/renderer' ;
@@ -411,29 +411,37 @@ export function injectViewContainerRef(): ViewContainerRef {
411411 * Create DOM element. The instruction must later be followed by `elementEnd()` call.
412412 *
413413 * @param index Index of the element in the data array
414- * @param nameOrComponentDef Name of the DOM Node or `ComponentDef` .
414+ * @param nameOrComponentType Name of the DOM Node or `ComponentType` to create .
415415 * @param attrs Statically bound set of attributes to be written into the DOM element on creation.
416- * @param localName A name under which a given element is exported.
416+ * @param directiveTypes A set of directives declared on this element.
417+ * @param localRefs A set of local reference bindings on the element.
417418 *
418- * Attributes are passed as an array of strings where elements with an even index hold an attribute
419- * name and elements with an odd index hold an attribute value, ex.:
419+ * Attributes and localRefs are passed as an array of strings where elements with an even index
420+ * hold an attribute name and elements with an odd index hold an attribute value, ex.:
420421 * ['id', 'warning5', 'class', 'alert']
421422 */
422423export function elementStart (
423- index : number , nameOrComponentDef ?: string | ComponentDef < any > , attrs ?: string [ ] | null ,
424- localName ?: string ) : RElement {
424+ index : number , nameOrComponentType ?: string | ComponentType < any > , attrs ?: string [ ] | null ,
425+ directiveTypes ?: DirectiveType < any > [ ] | null , localRefs ?: string [ ] | null ) : RElement {
425426 let node : LElementNode ;
426427 let native : RElement ;
427428
428- if ( nameOrComponentDef == null ) {
429+ if ( nameOrComponentType == null ) {
429430 // native node retrieval - used for exporting elements as tpl local variables (<div #foo>)
430431 const node = data [ index ] ! ;
431432 native = node && ( node as LElementNode ) . native ;
432433 } else {
433434 ngDevMode && assertEqual ( currentView . bindingStartIndex , null , 'bindingStartIndex' ) ;
434- const isHostElement = typeof nameOrComponentDef !== 'string' ;
435- const name = isHostElement ? ( nameOrComponentDef as ComponentDef < any > ) . tag :
436- nameOrComponentDef as string ;
435+ const isHostElement = typeof nameOrComponentType !== 'string' ;
436+ // MEGAMORPHIC: `ngComponentDef` is a megamorphic property access here.
437+ // This is OK, since we will refactor this code and store the result in `TView.data`
438+ // which means that we will be reading this value only once. We are trading clean/simple
439+ // template
440+ // code for slight startup(first run) performance. (No impact on subsequent runs)
441+ // TODO(misko): refactor this to store the `ComponentDef` in `TView.data`.
442+ const hostComponentDef =
443+ isHostElement ? ( nameOrComponentType as ComponentType < any > ) . ngComponentDef : null ;
444+ const name = isHostElement ? hostComponentDef ! . tag : nameOrComponentType as string ;
437445 if ( name === null ) {
438446 // TODO: future support for nameless components.
439447 throw 'for now name is required' ;
@@ -442,30 +450,83 @@ export function elementStart(
442450
443451 let componentView : LView | null = null ;
444452 if ( isHostElement ) {
445- const ngStaticData = getTemplateStatic ( ( nameOrComponentDef as ComponentDef < any > ) . template ) ;
453+ const ngStaticData = getTemplateStatic ( hostComponentDef ! . template ) ;
446454 componentView = addToViewTree ( createLView (
447- - 1 , rendererFactory . createRenderer (
448- native , ( nameOrComponentDef as ComponentDef < any > ) . rendererType ) ,
455+ - 1 , rendererFactory . createRenderer ( native , hostComponentDef ! . rendererType ) ,
449456 ngStaticData ) ) ;
450457 }
451458
452459 // Only component views should be added to the view tree directly. Embedded views are
453460 // accessed through their containers because they may be removed / re-added later.
454461 node = createLNode ( index , LNodeFlags . Element , native , componentView ) ;
455462
463+ // TODO(misko): implement code which caches the local reference resolution
464+ const queryName : string | null = hack_findQueryName ( hostComponentDef , localRefs , '' ) ;
465+
456466 if ( node . tNode == null ) {
457467 ngDevMode && assertDataInRange ( index - 1 ) ;
458468 node . tNode = ngStaticData [ index ] =
459- createTNode ( name , attrs || null , null , localName || null ) ;
469+ createTNode ( name , attrs || null , null , hostComponentDef ? null : queryName ) ;
460470 }
461471
462472 if ( attrs ) setUpAttributes ( native , attrs ) ;
463473 appendChild ( node . parent ! , native , currentView ) ;
474+
475+ if ( hostComponentDef ) {
476+ // TODO(mhevery): This assumes that the directives come in correct order, which
477+ // is not guaranteed. Must be refactored to take it into account.
478+ directiveCreate ( ++ index , hostComponentDef . n ( ) , hostComponentDef , queryName ) ;
479+ }
480+ hack_declareDirectives ( index , directiveTypes , localRefs ) ;
464481 }
465482 }
466483 return native ;
467484}
468485
486+ /**
487+ * This function instantiates a directive with a correct queryName. It is a hack since we should
488+ * compute the query value only once and store it with the template (rather than on each invocation)
489+ */
490+ function hack_declareDirectives (
491+ index : number , directiveTypes : DirectiveType < any > [ ] | null | undefined ,
492+ localRefs : string [ ] | null | undefined , ) {
493+ if ( directiveTypes ) {
494+ // TODO(mhevery): This assumes that the directives come in correct order, which
495+ // is not guaranteed. Must be refactored to take it into account.
496+ for ( let i = 0 ; i < directiveTypes . length ; i ++ ) {
497+ // MEGAMORPHIC: `ngDirectiveDef` is a megamorphic property access here.
498+ // This is OK, since we will refactor this code and store the result in `TView.data`
499+ // which means that we will be reading this value only once. We are trading clean/simple
500+ // template
501+ // code for slight startup(first run) performance. (No impact on subsequent runs)
502+ // TODO(misko): refactor this to store the `DirectiveDef` in `TView.data`.
503+ const directiveDef = directiveTypes [ i ] . ngDirectiveDef ;
504+ directiveCreate (
505+ ++ index , directiveDef . n ( ) , directiveDef , hack_findQueryName ( directiveDef , localRefs ) ) ;
506+ }
507+ }
508+ }
509+
510+ /**
511+ * This function returns the queryName for a directive. It is a hack since we should
512+ * compute the query value only once and store it with the template (rather than on each invocation)
513+ */
514+ function hack_findQueryName (
515+ directiveDef : DirectiveDef < any > | null , localRefs : string [ ] | null | undefined ,
516+ defaultExport ?: string , ) : string | null {
517+ const exportAs = directiveDef && directiveDef . exportAs || defaultExport ;
518+ if ( exportAs != null && localRefs ) {
519+ for ( let i = 0 ; i < localRefs . length ; i = i + 2 ) {
520+ const local = localRefs [ i ] ;
521+ const toExportAs = localRefs [ i | 1 ] ;
522+ if ( toExportAs === exportAs || toExportAs === defaultExport ) {
523+ return local ;
524+ }
525+ }
526+ }
527+ return null ;
528+ }
529+
469530/**
470531 * Gets static data from a template function or creates a new static
471532 * data array if it doesn't already exist.
@@ -836,7 +897,21 @@ export function textBinding<T>(index: number, value: T | NO_CHANGE): void {
836897//////////////////////////
837898
838899/**
839- * Create or retrieve the directive.
900+ * Retrieve a directive.
901+ *
902+ * NOTE: directives can be created in order other than the index order. They can also
903+ * be retrieved before they are created in which case the value will be null.
904+ *
905+ * @param index Each directive in a `View` will have a unique index. Directives can
906+ * be created or retrieved out of order.
907+ */
908+ export function directive < T > ( index : number ) : T {
909+ ngDevMode && assertDataInRange ( index ) ;
910+ return data [ index ] ;
911+ }
912+
913+ /**
914+ * Create a directive.
840915 *
841916 * NOTE: directives can be created in order other than the index order. They can also
842917 * be retrieved before they are created in which case the value will be null.
@@ -845,56 +920,47 @@ export function textBinding<T>(index: number, value: T | NO_CHANGE): void {
845920 * be created or retrieved out of order.
846921 * @param directive The directive instance.
847922 * @param directiveDef DirectiveDef object which contains information about the template.
923+ * @param queryName Name under which the query can retrieve the directive instance.
848924 */
849- export function directive < T > ( index : number ) : T ;
850- export function directive < T > (
851- index : number , directive : T , directiveDef : DirectiveDef < T > , localName ?: string ) : T ;
852- export function directive < T > (
853- index : number , directive ?: T , directiveDef ?: DirectiveDef < T > , localName ?: string ) : T {
925+ export function directiveCreate < T > (
926+ index : number , directive : T , directiveDef : DirectiveDef < T > , queryName ?: string | null ) : T {
854927 let instance ;
855- if ( directive == null ) {
856- // return existing
857- ngDevMode && assertDataInRange ( index ) ;
858- instance = data [ index ] ;
928+ ngDevMode && assertEqual ( currentView . bindingStartIndex , null , 'bindingStartIndex' ) ;
929+ ngDevMode && assertPreviousIsParent ( ) ;
930+ let flags = previousOrParentNode ! . flags ;
931+ let size = flags & LNodeFlags . SIZE_MASK ;
932+ if ( size === 0 ) {
933+ flags = ( index << LNodeFlags . INDX_SHIFT ) | LNodeFlags . SIZE_SKIP | flags & LNodeFlags . TYPE_MASK ;
859934 } else {
860- ngDevMode && assertEqual ( currentView . bindingStartIndex , null , 'bindingStartIndex' ) ;
861- ngDevMode && assertPreviousIsParent ( ) ;
862- let flags = previousOrParentNode ! . flags ;
863- let size = flags & LNodeFlags . SIZE_MASK ;
864- if ( size === 0 ) {
865- flags =
866- ( index << LNodeFlags . INDX_SHIFT ) | LNodeFlags . SIZE_SKIP | flags & LNodeFlags . TYPE_MASK ;
867- } else {
868- flags += LNodeFlags . SIZE_SKIP ;
869- }
870- previousOrParentNode ! . flags = flags ;
871-
872- ngDevMode && assertDataInRange ( index - 1 ) ;
873- Object . defineProperty (
874- directive , NG_HOST_SYMBOL , { enumerable : false , value : previousOrParentNode } ) ;
935+ flags += LNodeFlags . SIZE_SKIP ;
936+ }
937+ previousOrParentNode ! . flags = flags ;
875938
876- data [ index ] = instance = directive ;
939+ ngDevMode && assertDataInRange ( index - 1 ) ;
940+ Object . defineProperty (
941+ directive , NG_HOST_SYMBOL , { enumerable : false , value : previousOrParentNode } ) ;
877942
878- if ( index >= ngStaticData . length ) {
879- ngStaticData [ index ] = directiveDef ! ;
880- if ( localName ) {
881- ngDevMode && assertNotNull ( previousOrParentNode . tNode , 'previousOrParentNode.staticData' ) ;
882- const tNode = previousOrParentNode ! . tNode ! ;
883- ( tNode . localNames || ( tNode . localNames = [ ] ) ) . push ( localName , index ) ;
884- }
885- }
943+ data [ index ] = instance = directive ;
886944
887- const diPublic = directiveDef ! . diPublic ;
888- if ( diPublic ) {
889- diPublic ( directiveDef ! ) ;
945+ if ( index >= ngStaticData . length ) {
946+ ngStaticData [ index ] = directiveDef ! ;
947+ if ( queryName ) {
948+ ngDevMode &&
949+ assertNotNull ( previousOrParentNode . tNode , 'previousOrParentNode.staticData' ) ;
950+ const nodeStaticData = previousOrParentNode ! . tNode ! ;
951+ ( nodeStaticData . localNames || ( nodeStaticData . localNames = [ ] ) ) . push ( queryName , index ) ;
890952 }
953+ }
891954
892- const tNode : TNode | null = previousOrParentNode . tNode ! ;
893- if ( tNode && tNode . attrs ) {
894- setInputsFromAttrs < T > ( instance , directiveDef ! . inputs , tNode ) ;
895- }
955+ const diPublic = directiveDef ! . diPublic ;
956+ if ( diPublic ) {
957+ diPublic ( directiveDef ! ) ;
896958 }
897959
960+ const staticData : TNode | null = previousOrParentNode . tNode ! ;
961+ if ( staticData && staticData . attrs ) {
962+ setInputsFromAttrs < T > ( instance , directiveDef ! . inputs , staticData ) ;
963+ }
898964 return instance ;
899965}
900966
@@ -1039,10 +1105,11 @@ export function executeViewHooks(): void {
10391105 * @param template Optional inline template
10401106 * @param tagName The name of the container element, if applicable
10411107 * @param attrs The attrs attached to the container, if applicable
1108+ * @param localRefs A set of local reference bindings on the element.
10421109 */
10431110export function containerStart (
1044- index : number , template ?: ComponentTemplate < any > , tagName ?: string , attrs ?: string [ ] ,
1045- localName ?: string ) : void {
1111+ index : number , directiveTypes ?: DirectiveType < any > [ ] , template ?: ComponentTemplate < any > ,
1112+ tagName ?: string , attrs ?: string [ ] , localRefs ?: string [ ] | null ) : void {
10461113 ngDevMode && assertEqual ( currentView . bindingStartIndex , null , 'bindingStartIndex' ) ;
10471114
10481115 // If the direct parent of the container is a view, its views (including its comment)
@@ -1068,13 +1135,16 @@ export function containerStart(
10681135 } ) ;
10691136
10701137 if ( node . tNode == null ) {
1138+ // TODO(misko): implement queryName caching
1139+ const queryName : string | null = hack_findQueryName ( null , localRefs , '' ) ;
10711140 node . tNode = ngStaticData [ index ] =
1072- createTNode ( tagName || null , attrs || null , [ ] , localName || null ) ;
1141+ createTNode ( tagName || null , attrs || null , [ ] , queryName || null ) ;
10731142 }
10741143
10751144 // Containers are added to the current view tree instead of their embedded views
10761145 // because views can be removed and re-inserted.
10771146 addToViewTree ( node . data ) ;
1147+ hack_declareDirectives ( index , directiveTypes , localRefs ) ;
10781148}
10791149
10801150export function containerEnd ( ) {
0 commit comments