@@ -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,17 +68,19 @@ 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' ;
8080import { profiler } from './profiler' ;
8181import { ProfilerEvent } from './profiler_types' ;
8282import { executeContentQueries } from './queries/query_execution' ;
83- import { enterView , getCurrentTNode , getLView , leaveView } from './state' ;
83+ import { enterView , leaveView } from './state' ;
8484import { computeStaticStyling } from './styling/static_styling' ;
8585import { getOrCreateTNode } from './tnode_manipulation' ;
8686import { mergeHostAttrs } from './util/attrs_utils' ;
@@ -150,9 +150,74 @@ function toRefArray<
150150 return array ;
151151}
152152
153- function getNamespace ( elementName : string ) : string | null {
154- const name = elementName . toLowerCase ( ) ;
155- return name === 'svg' ? SVG_NAMESPACE : name === 'math' ? MATH_ML_NAMESPACE : null ;
153+ function verifyNotAnOrphanComponent ( componentDef : ComponentDef < unknown > ) {
154+ // TODO(pk): create assert that verifies ngDevMode
155+ if (
156+ ( typeof ngJitMode === 'undefined' || ngJitMode ) &&
157+ componentDef . debugInfo ?. forbidOrphanRendering
158+ ) {
159+ if ( depsTracker . isOrphanComponent ( componentDef . type ) ) {
160+ throw new RuntimeError (
161+ RuntimeErrorCode . RUNTIME_DEPS_ORPHAN_COMPONENT ,
162+ `Orphan component found! Trying to render the component ${ debugStringifyTypeForError (
163+ componentDef . type ,
164+ ) } 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.`,
165+ ) ;
166+ }
167+ }
168+ }
169+
170+ function createRootViewInjector (
171+ componentDef : ComponentDef < unknown > ,
172+ environmentInjector : EnvironmentInjector | NgModuleRef < any > | undefined ,
173+ injector : Injector ,
174+ ) : Injector {
175+ let realEnvironmentInjector =
176+ environmentInjector instanceof EnvironmentInjector
177+ ? environmentInjector
178+ : environmentInjector ?. injector ;
179+
180+ if ( realEnvironmentInjector && componentDef . getStandaloneInjector !== null ) {
181+ realEnvironmentInjector =
182+ componentDef . getStandaloneInjector ( realEnvironmentInjector ) || realEnvironmentInjector ;
183+ }
184+
185+ const rootViewInjector = realEnvironmentInjector
186+ ? new ChainedInjector ( injector , realEnvironmentInjector )
187+ : injector ;
188+ return rootViewInjector ;
189+ }
190+
191+ function createRootLViewEnvironment ( rootLViewInjector : Injector ) : LViewEnvironment {
192+ const rendererFactory = rootLViewInjector . get ( RendererFactory2 , null ) ;
193+ if ( rendererFactory === null ) {
194+ throw new RuntimeError (
195+ RuntimeErrorCode . RENDERER_NOT_FOUND ,
196+ ngDevMode &&
197+ 'Angular was not able to inject a renderer (RendererFactory2). ' +
198+ 'Likely this is due to a broken DI hierarchy. ' +
199+ 'Make sure that any injector used to create this component has a correct parent.' ,
200+ ) ;
201+ }
202+
203+ const sanitizer = rootLViewInjector . get ( Sanitizer , null ) ;
204+ const changeDetectionScheduler = rootLViewInjector . get ( ChangeDetectionScheduler , null ) ;
205+
206+ return {
207+ rendererFactory,
208+ sanitizer,
209+ changeDetectionScheduler,
210+ } ;
211+ }
212+
213+ function createHostElement ( componentDef : ComponentDef < unknown > , render : Renderer ) : RElement {
214+ // Determine a tag name used for creating host elements when this component is created
215+ // dynamically. Default to 'div' if this component did not specify any tag name in its
216+ // selector.
217+ const tagName = ( ( componentDef . selectors [ 0 ] [ 0 ] as string ) || 'div' ) . toLowerCase ( ) ;
218+ const namespace =
219+ tagName === 'svg' ? SVG_NAMESPACE : tagName === 'math' ? MATH_ML_NAMESPACE : null ;
220+ return createElementNode ( render , tagName , namespace ) ;
156221}
157222
158223/**
@@ -214,84 +279,8 @@ export class ComponentFactory<T> extends AbstractComponentFactory<T> {
214279
215280 const prevConsumer = setActiveConsumer ( null ) ;
216281 try {
217- // Check if the component is orphan
218- if (
219- ngDevMode &&
220- ( typeof ngJitMode === 'undefined' || ngJitMode ) &&
221- this . componentDef . debugInfo ?. forbidOrphanRendering
222- ) {
223- if ( depsTracker . isOrphanComponent ( this . componentType ) ) {
224- throw new RuntimeError (
225- RuntimeErrorCode . RUNTIME_DEPS_ORPHAN_COMPONENT ,
226- `Orphan component found! Trying to render the component ${ debugStringifyTypeForError (
227- this . componentType ,
228- ) } 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.`,
229- ) ;
230- }
231- }
232-
233- environmentInjector = environmentInjector || this . ngModule ;
234-
235- let realEnvironmentInjector =
236- environmentInjector instanceof EnvironmentInjector
237- ? environmentInjector
238- : environmentInjector ?. injector ;
239-
240- if ( realEnvironmentInjector && this . componentDef . getStandaloneInjector !== null ) {
241- realEnvironmentInjector =
242- this . componentDef . getStandaloneInjector ( realEnvironmentInjector ) ||
243- realEnvironmentInjector ;
244- }
245-
246- const rootViewInjector = realEnvironmentInjector
247- ? new ChainedInjector ( injector , realEnvironmentInjector )
248- : injector ;
249-
250- const rendererFactory = rootViewInjector . get ( RendererFactory2 , null ) ;
251- if ( rendererFactory === null ) {
252- throw new RuntimeError (
253- RuntimeErrorCode . RENDERER_NOT_FOUND ,
254- ngDevMode &&
255- 'Angular was not able to inject a renderer (RendererFactory2). ' +
256- 'Likely this is due to a broken DI hierarchy. ' +
257- 'Make sure that any injector used to create this component has a correct parent.' ,
258- ) ;
259- }
260- const sanitizer = rootViewInjector . get ( Sanitizer , null ) ;
261-
262- const changeDetectionScheduler = rootViewInjector . get ( ChangeDetectionScheduler , null ) ;
263-
264- const environment : LViewEnvironment = {
265- rendererFactory,
266- sanitizer,
267- changeDetectionScheduler,
268- } ;
269-
270- const hostRenderer = rendererFactory . createRenderer ( null , this . componentDef ) ;
271- // Determine a tag name used for creating host elements when this component is created
272- // dynamically. Default to 'div' if this component did not specify any tag name in its
273- // selector.
274- const elementName = ( this . componentDef . selectors [ 0 ] [ 0 ] as string ) || 'div' ;
275- const hostRNode = rootSelectorOrNode
276- ? locateHostElement (
277- hostRenderer ,
278- rootSelectorOrNode ,
279- this . componentDef . encapsulation ,
280- rootViewInjector ,
281- )
282- : createElementNode ( hostRenderer , elementName , getNamespace ( elementName ) ) ;
283-
284- let rootFlags = LViewFlags . IsRoot ;
285- if ( this . componentDef . signals ) {
286- rootFlags |= LViewFlags . SignalView ;
287- } else if ( ! this . componentDef . onPush ) {
288- rootFlags |= LViewFlags . CheckAlways ;
289- }
290-
291- let hydrationInfo : DehydratedView | null = null ;
292- if ( hostRNode !== null ) {
293- hydrationInfo = retrieveHydrationInfo ( hostRNode , rootViewInjector , true /* isRootView */ ) ;
294- }
282+ const cmpDef = this . componentDef ;
283+ ngDevMode && verifyNotAnOrphanComponent ( cmpDef ) ;
295284
296285 // Create the root view. Uses empty TView and ContentTemplate.
297286 const rootTView = createTView (
@@ -307,21 +296,39 @@ export class ComponentFactory<T> extends AbstractComponentFactory<T> {
307296 null ,
308297 null ,
309298 ) ;
299+
300+ const rootViewInjector = createRootViewInjector (
301+ cmpDef ,
302+ environmentInjector || this . ngModule ,
303+ injector ,
304+ ) ;
305+
306+ const environment = createRootLViewEnvironment ( rootViewInjector ) ;
307+ const hostRenderer = environment . rendererFactory . createRenderer ( null , cmpDef ) ;
308+ const hostElement = rootSelectorOrNode
309+ ? locateHostElement (
310+ hostRenderer ,
311+ rootSelectorOrNode ,
312+ cmpDef . encapsulation ,
313+ rootViewInjector ,
314+ )
315+ : createHostElement ( cmpDef , hostRenderer ) ;
316+
310317 const rootLView = createLView < T > (
311318 null ,
312319 rootTView ,
313320 null ,
314- rootFlags ,
321+ LViewFlags . IsRoot | getInitialLViewFlagsFromDef ( cmpDef ) ,
315322 null ,
316323 null ,
317324 environment ,
318325 hostRenderer ,
319326 rootViewInjector ,
320327 null ,
321- hydrationInfo ,
328+ retrieveHydrationInfo ( hostElement , rootViewInjector , true /* isRootView */ ) ,
322329 ) ;
323330
324- rootLView [ HEADER_OFFSET ] = hostRNode ;
331+ rootLView [ HEADER_OFFSET ] = hostElement ;
325332
326333 // rootView is the parent when bootstrapping
327334 // TODO(misko): it looks like we are entering view here but we don't really need to as
@@ -337,9 +344,9 @@ export class ComponentFactory<T> extends AbstractComponentFactory<T> {
337344 const tAttributes = rootSelectorOrNode
338345 ? [ 'ng-version' , '0.0.0-PLACEHOLDER' ]
339346 : // Extract attributes and classes from the first selector only to match VE behavior.
340- extractAttrsAndClassesFromSelector ( this . componentDef . selectors [ 0 ] ) ;
347+ extractAttrsAndClassesFromSelector ( cmpDef . selectors [ 0 ] ) ;
341348
342- // TODO: this logic is shared with the element instruction first create pass
349+ // TODO: this logic is shared with the element instruction first create pass - minus directive matching
343350 const hostTNode = getOrCreateTNode (
344351 rootTView ,
345352 HEADER_OFFSET ,
@@ -349,27 +356,19 @@ export class ComponentFactory<T> extends AbstractComponentFactory<T> {
349356 ) ;
350357
351358 const [ directiveDefs , hostDirectiveDefs ] = resolveHostDirectives ( rootTView , hostTNode , [
352- this . componentDef ,
359+ cmpDef ,
353360 ] ) ;
354361 initializeDirectives ( rootTView , rootLView , hostTNode , directiveDefs , { } , hostDirectiveDefs ) ;
355-
356- for ( const def of directiveDefs ) {
357- hostTNode . mergedAttrs = mergeHostAttrs ( hostTNode . mergedAttrs , def . hostAttrs ) ;
358- }
359362 hostTNode . mergedAttrs = mergeHostAttrs ( hostTNode . mergedAttrs , tAttributes ) ;
360363
361364 computeStaticStyling ( hostTNode , hostTNode . mergedAttrs , true ) ;
362365
363366 // TODO(crisbeto): in practice `hostRNode` should always be defined, but there are some
364367 // tests where the renderer is mocked out and `undefined` is returned. We should update the
365368 // tests so that this check can be removed.
366- if ( hostRNode ) {
367- setupStaticAttributes ( hostRenderer , hostRNode , hostTNode ) ;
368- attachPatchData ( hostRNode , rootLView ) ;
369- }
370-
371- if ( projectableNodes !== undefined ) {
372- projectNodes ( hostTNode , this . ngContentSelectors , projectableNodes ) ;
369+ if ( hostElement ) {
370+ setupStaticAttributes ( hostRenderer , hostElement , hostTNode ) ;
371+ attachPatchData ( hostElement , rootLView ) ;
373372 }
374373
375374 // TODO(pk): this logic is similar to the instruction code where a node can have directives
@@ -379,6 +378,10 @@ export class ComponentFactory<T> extends AbstractComponentFactory<T> {
379378 // TODO(pk): code / logic duplication with the elementEnd and similar instructions
380379 registerPostOrderHooks ( rootTView , hostTNode ) ;
381380
381+ if ( projectableNodes !== undefined ) {
382+ projectNodes ( hostTNode , this . ngContentSelectors , projectableNodes ) ;
383+ }
384+
382385 componentView = getComponentLViewByIndex ( hostTNode . index , rootLView ) ;
383386
384387 // TODO(pk): why do we need this logic?
0 commit comments