@@ -16,8 +16,6 @@ import {
1616import { Injector } from '../di/injector' ;
1717import { EnvironmentInjector } from '../di/r3_injector' ;
1818import { RuntimeError , RuntimeErrorCode } from '../errors' ;
19- import { DehydratedView } from '../hydration/interfaces' ;
20- import { retrieveHydrationInfo } from '../hydration/utils' ;
2119import { Type } from '../interface/type' ;
2220import {
2321 ComponentFactory as AbstractComponentFactory ,
@@ -28,7 +26,6 @@ import {createElementRef, ElementRef} from '../linker/element_ref';
2826import { NgModuleRef } from '../linker/ng_module_factory' ;
2927import { RendererFactory2 } from '../render/api' ;
3028import { Sanitizer } from '../sanitization/sanitizer' ;
31- import { assertDefined } from '../util/assert' ;
3229
3330import { assertComponentType } from './assert' ;
3431import { attachPatchData } from './context_discovery' ;
@@ -43,6 +40,7 @@ import {
4340 createDirectivesInstances ,
4441 createLView ,
4542 createTView ,
43+ getInitialLViewFlagsFromDef ,
4644 initializeDirectives ,
4745 locateHostElement ,
4846 resolveHostDirectives ,
@@ -58,7 +56,7 @@ import {
5856 TNode ,
5957 TNodeType ,
6058} from './interfaces/node' ;
61- import { RNode } from './interfaces/renderer_dom' ;
59+ import { RElement , RNode } from './interfaces/renderer_dom' ;
6260import {
6361 CONTEXT ,
6462 HEADER_OFFSET ,
@@ -70,16 +68,18 @@ import {
7068} from './interfaces/view' ;
7169import { MATH_ML_NAMESPACE , SVG_NAMESPACE } from './namespaces' ;
7270
71+ import { retrieveHydrationInfo } from '../hydration/utils' ;
7372import { ChainedInjector } from './chained_injector' ;
7473import { createElementNode , setupStaticAttributes } from './dom_node_manipulation' ;
7574import { unregisterLView } from './interfaces/lview_tracking' ;
75+ import { Renderer } from './interfaces/renderer' ;
7676import {
7777 extractAttrsAndClassesFromSelector ,
7878 stringifyCSSSelectorList ,
7979} from './node_selector_matcher' ;
8080
8181import { executeContentQueries } from './queries/query_execution' ;
82- import { enterView , getCurrentTNode , getLView , leaveView } from './state' ;
82+ import { enterView , leaveView } from './state' ;
8383import { computeStaticStyling } from './styling/static_styling' ;
8484import { getOrCreateTNode } from './tnode_manipulation' ;
8585import { mergeHostAttrs } from './util/attrs_utils' ;
@@ -149,9 +149,74 @@ function toRefArray<
149149 return array ;
150150}
151151
152- function getNamespace ( elementName : string ) : string | null {
153- const name = elementName . toLowerCase ( ) ;
154- return name === 'svg' ? SVG_NAMESPACE : name === 'math' ? MATH_ML_NAMESPACE : null ;
152+ function verifyNotAnOrphanComponent ( componentDef : ComponentDef < unknown > ) {
153+ // TODO(pk): create assert that verifies ngDevMode
154+ if (
155+ ( typeof ngJitMode === 'undefined' || ngJitMode ) &&
156+ componentDef . debugInfo ?. forbidOrphanRendering
157+ ) {
158+ if ( depsTracker . isOrphanComponent ( componentDef . type ) ) {
159+ throw new RuntimeError (
160+ RuntimeErrorCode . RUNTIME_DEPS_ORPHAN_COMPONENT ,
161+ `Orphan component found! Trying to render the component ${ debugStringifyTypeForError (
162+ componentDef . type ,
163+ ) } without first loading the NgModule that declares it. It is recommended to make this component standalone in order to avoid this error. If this is not possible now, import the component's NgModule in the appropriate NgModule, or the standalone component in which you are trying to render this component. If this is a lazy import, load the NgModule lazily as well and use its module injector.`,
164+ ) ;
165+ }
166+ }
167+ }
168+
169+ function createRootViewInjector (
170+ componentDef : ComponentDef < unknown > ,
171+ environmentInjector : EnvironmentInjector | NgModuleRef < any > | undefined ,
172+ injector : Injector ,
173+ ) : Injector {
174+ let realEnvironmentInjector =
175+ environmentInjector instanceof EnvironmentInjector
176+ ? environmentInjector
177+ : environmentInjector ?. injector ;
178+
179+ if ( realEnvironmentInjector && componentDef . getStandaloneInjector !== null ) {
180+ realEnvironmentInjector =
181+ componentDef . getStandaloneInjector ( realEnvironmentInjector ) || realEnvironmentInjector ;
182+ }
183+
184+ const rootViewInjector = realEnvironmentInjector
185+ ? new ChainedInjector ( injector , realEnvironmentInjector )
186+ : injector ;
187+ return rootViewInjector ;
188+ }
189+
190+ function createRootLViewEnvironment ( rootLViewInjector : Injector ) : LViewEnvironment {
191+ const rendererFactory = rootLViewInjector . get ( RendererFactory2 , null ) ;
192+ if ( rendererFactory === null ) {
193+ throw new RuntimeError (
194+ RuntimeErrorCode . RENDERER_NOT_FOUND ,
195+ ngDevMode &&
196+ 'Angular was not able to inject a renderer (RendererFactory2). ' +
197+ 'Likely this is due to a broken DI hierarchy. ' +
198+ 'Make sure that any injector used to create this component has a correct parent.' ,
199+ ) ;
200+ }
201+
202+ const sanitizer = rootLViewInjector . get ( Sanitizer , null ) ;
203+ const changeDetectionScheduler = rootLViewInjector . get ( ChangeDetectionScheduler , null ) ;
204+
205+ return {
206+ rendererFactory,
207+ sanitizer,
208+ changeDetectionScheduler,
209+ } ;
210+ }
211+
212+ function createHostElement ( componentDef : ComponentDef < unknown > , render : Renderer ) : RElement {
213+ // Determine a tag name used for creating host elements when this component is created
214+ // dynamically. Default to 'div' if this component did not specify any tag name in its
215+ // selector.
216+ const tagName = ( ( componentDef . selectors [ 0 ] [ 0 ] as string ) || 'div' ) . toLowerCase ( ) ;
217+ const namespace =
218+ tagName === 'svg' ? SVG_NAMESPACE : tagName === 'math' ? MATH_ML_NAMESPACE : null ;
219+ return createElementNode ( render , tagName , namespace ) ;
155220}
156221
157222/**
@@ -211,84 +276,8 @@ export class ComponentFactory<T> extends AbstractComponentFactory<T> {
211276 ) : AbstractComponentRef < T > {
212277 const prevConsumer = setActiveConsumer ( null ) ;
213278 try {
214- // Check if the component is orphan
215- if (
216- ngDevMode &&
217- ( typeof ngJitMode === 'undefined' || ngJitMode ) &&
218- this . componentDef . debugInfo ?. forbidOrphanRendering
219- ) {
220- if ( depsTracker . isOrphanComponent ( this . componentType ) ) {
221- throw new RuntimeError (
222- RuntimeErrorCode . RUNTIME_DEPS_ORPHAN_COMPONENT ,
223- `Orphan component found! Trying to render the component ${ debugStringifyTypeForError (
224- this . componentType ,
225- ) } without first loading the NgModule that declares it. It is recommended to make this component standalone in order to avoid this error. If this is not possible now, import the component's NgModule in the appropriate NgModule, or the standalone component in which you are trying to render this component. If this is a lazy import, load the NgModule lazily as well and use its module injector.`,
226- ) ;
227- }
228- }
229-
230- environmentInjector = environmentInjector || this . ngModule ;
231-
232- let realEnvironmentInjector =
233- environmentInjector instanceof EnvironmentInjector
234- ? environmentInjector
235- : environmentInjector ?. injector ;
236-
237- if ( realEnvironmentInjector && this . componentDef . getStandaloneInjector !== null ) {
238- realEnvironmentInjector =
239- this . componentDef . getStandaloneInjector ( realEnvironmentInjector ) ||
240- realEnvironmentInjector ;
241- }
242-
243- const rootViewInjector = realEnvironmentInjector
244- ? new ChainedInjector ( injector , realEnvironmentInjector )
245- : injector ;
246-
247- const rendererFactory = rootViewInjector . get ( RendererFactory2 , null ) ;
248- if ( rendererFactory === null ) {
249- throw new RuntimeError (
250- RuntimeErrorCode . RENDERER_NOT_FOUND ,
251- ngDevMode &&
252- 'Angular was not able to inject a renderer (RendererFactory2). ' +
253- 'Likely this is due to a broken DI hierarchy. ' +
254- 'Make sure that any injector used to create this component has a correct parent.' ,
255- ) ;
256- }
257- const sanitizer = rootViewInjector . get ( Sanitizer , null ) ;
258-
259- const changeDetectionScheduler = rootViewInjector . get ( ChangeDetectionScheduler , null ) ;
260-
261- const environment : LViewEnvironment = {
262- rendererFactory,
263- sanitizer,
264- changeDetectionScheduler,
265- } ;
266-
267- const hostRenderer = rendererFactory . createRenderer ( null , this . componentDef ) ;
268- // Determine a tag name used for creating host elements when this component is created
269- // dynamically. Default to 'div' if this component did not specify any tag name in its
270- // selector.
271- const elementName = ( this . componentDef . selectors [ 0 ] [ 0 ] as string ) || 'div' ;
272- const hostRNode = rootSelectorOrNode
273- ? locateHostElement (
274- hostRenderer ,
275- rootSelectorOrNode ,
276- this . componentDef . encapsulation ,
277- rootViewInjector ,
278- )
279- : createElementNode ( hostRenderer , elementName , getNamespace ( elementName ) ) ;
280-
281- let rootFlags = LViewFlags . IsRoot ;
282- if ( this . componentDef . signals ) {
283- rootFlags |= LViewFlags . SignalView ;
284- } else if ( ! this . componentDef . onPush ) {
285- rootFlags |= LViewFlags . CheckAlways ;
286- }
287-
288- let hydrationInfo : DehydratedView | null = null ;
289- if ( hostRNode !== null ) {
290- hydrationInfo = retrieveHydrationInfo ( hostRNode , rootViewInjector , true /* isRootView */ ) ;
291- }
279+ const cmpDef = this . componentDef ;
280+ ngDevMode && verifyNotAnOrphanComponent ( cmpDef ) ;
292281
293282 // Create the root view. Uses empty TView and ContentTemplate.
294283 const rootTView = createTView (
@@ -304,21 +293,39 @@ export class ComponentFactory<T> extends AbstractComponentFactory<T> {
304293 null ,
305294 null ,
306295 ) ;
296+
297+ const rootViewInjector = createRootViewInjector (
298+ cmpDef ,
299+ environmentInjector || this . ngModule ,
300+ injector ,
301+ ) ;
302+
303+ const environment = createRootLViewEnvironment ( rootViewInjector ) ;
304+ const hostRenderer = environment . rendererFactory . createRenderer ( null , cmpDef ) ;
305+ const hostElement = rootSelectorOrNode
306+ ? locateHostElement (
307+ hostRenderer ,
308+ rootSelectorOrNode ,
309+ cmpDef . encapsulation ,
310+ rootViewInjector ,
311+ )
312+ : createHostElement ( cmpDef , hostRenderer ) ;
313+
307314 const rootLView = createLView < T > (
308315 null ,
309316 rootTView ,
310317 null ,
311- rootFlags ,
318+ LViewFlags . IsRoot | getInitialLViewFlagsFromDef ( cmpDef ) ,
312319 null ,
313320 null ,
314321 environment ,
315322 hostRenderer ,
316323 rootViewInjector ,
317324 null ,
318- hydrationInfo ,
325+ retrieveHydrationInfo ( hostElement , rootViewInjector , true /* isRootView */ ) ,
319326 ) ;
320327
321- rootLView [ HEADER_OFFSET ] = hostRNode ;
328+ rootLView [ HEADER_OFFSET ] = hostElement ;
322329
323330 // rootView is the parent when bootstrapping
324331 // TODO(misko): it looks like we are entering view here but we don't really need to as
@@ -334,9 +341,9 @@ export class ComponentFactory<T> extends AbstractComponentFactory<T> {
334341 const tAttributes = rootSelectorOrNode
335342 ? [ 'ng-version' , '0.0.0-PLACEHOLDER' ]
336343 : // Extract attributes and classes from the first selector only to match VE behavior.
337- extractAttrsAndClassesFromSelector ( this . componentDef . selectors [ 0 ] ) ;
344+ extractAttrsAndClassesFromSelector ( cmpDef . selectors [ 0 ] ) ;
338345
339- // TODO: this logic is shared with the element instruction first create pass
346+ // TODO: this logic is shared with the element instruction first create pass - minus directive matching
340347 const hostTNode = getOrCreateTNode (
341348 rootTView ,
342349 HEADER_OFFSET ,
@@ -346,27 +353,19 @@ export class ComponentFactory<T> extends AbstractComponentFactory<T> {
346353 ) ;
347354
348355 const [ directiveDefs , hostDirectiveDefs ] = resolveHostDirectives ( rootTView , hostTNode , [
349- this . componentDef ,
356+ cmpDef ,
350357 ] ) ;
351358 initializeDirectives ( rootTView , rootLView , hostTNode , directiveDefs , { } , hostDirectiveDefs ) ;
352-
353- for ( const def of directiveDefs ) {
354- hostTNode . mergedAttrs = mergeHostAttrs ( hostTNode . mergedAttrs , def . hostAttrs ) ;
355- }
356359 hostTNode . mergedAttrs = mergeHostAttrs ( hostTNode . mergedAttrs , tAttributes ) ;
357360
358361 computeStaticStyling ( hostTNode , hostTNode . mergedAttrs , true ) ;
359362
360363 // TODO(crisbeto): in practice `hostRNode` should always be defined, but there are some
361364 // tests where the renderer is mocked out and `undefined` is returned. We should update the
362365 // tests so that this check can be removed.
363- if ( hostRNode ) {
364- setupStaticAttributes ( hostRenderer , hostRNode , hostTNode ) ;
365- attachPatchData ( hostRNode , rootLView ) ;
366- }
367-
368- if ( projectableNodes !== undefined ) {
369- projectNodes ( hostTNode , this . ngContentSelectors , projectableNodes ) ;
366+ if ( hostElement ) {
367+ setupStaticAttributes ( hostRenderer , hostElement , hostTNode ) ;
368+ attachPatchData ( hostElement , rootLView ) ;
370369 }
371370
372371 // TODO(pk): this logic is similar to the instruction code where a node can have directives
@@ -376,6 +375,10 @@ export class ComponentFactory<T> extends AbstractComponentFactory<T> {
376375 // TODO(pk): code / logic duplication with the elementEnd and similar instructions
377376 registerPostOrderHooks ( rootTView , hostTNode ) ;
378377
378+ if ( projectableNodes !== undefined ) {
379+ projectNodes ( hostTNode , this . ngContentSelectors , projectableNodes ) ;
380+ }
381+
379382 componentView = getComponentLViewByIndex ( hostTNode . index , rootLView ) ;
380383
381384 // TODO(pk): why do we need this logic?
0 commit comments