https://github.com/angular/angular
Tip revision: 096fdd0f59541afab5d66518ce605734efa71d82 authored by Dylan Hunn on 09 August 2023, 20:56:01 UTC
release: cut the v16.2.0 release
release: cut the v16.2.0 release
Tip revision: 096fdd0
render.ts
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {retrieveHydrationInfo} from '../../hydration/utils';
import {assertEqual} from '../../util/assert';
import {RenderFlags} from '../interfaces/definition';
import {CONTEXT, FLAGS, HOST, HYDRATION, INJECTOR, LView, LViewFlags, TVIEW, TView} from '../interfaces/view';
import {enterView, leaveView} from '../state';
import {getComponentLViewByIndex, isCreationMode} from '../util/view_utils';
import {executeTemplate, executeViewQueryFn, refreshContentQueries} from './shared';
export function renderComponent(hostLView: LView, componentHostIdx: number) {
ngDevMode && assertEqual(isCreationMode(hostLView), true, 'Should be run in creation mode');
const componentView = getComponentLViewByIndex(componentHostIdx, hostLView);
const componentTView = componentView[TVIEW];
syncViewWithBlueprint(componentTView, componentView);
const hostRNode = componentView[HOST];
// Populate an LView with hydration info retrieved from the DOM via TransferState.
if (hostRNode !== null && componentView[HYDRATION] === null) {
componentView[HYDRATION] = retrieveHydrationInfo(hostRNode, componentView[INJECTOR]!);
}
renderView(componentTView, componentView, componentView[CONTEXT]);
}
/**
* Syncs an LView instance with its blueprint if they have gotten out of sync.
*
* Typically, blueprints and their view instances should always be in sync, so the loop here
* will be skipped. However, consider this case of two components side-by-side:
*
* App template:
* ```
* <comp></comp>
* <comp></comp>
* ```
*
* The following will happen:
* 1. App template begins processing.
* 2. First <comp> is matched as a component and its LView is created.
* 3. Second <comp> is matched as a component and its LView is created.
* 4. App template completes processing, so it's time to check child templates.
* 5. First <comp> template is checked. It has a directive, so its def is pushed to blueprint.
* 6. Second <comp> template is checked. Its blueprint has been updated by the first
* <comp> template, but its LView was created before this update, so it is out of sync.
*
* Note that embedded views inside ngFor loops will never be out of sync because these views
* are processed as soon as they are created.
*
* @param tView The `TView` that contains the blueprint for syncing
* @param lView The view to sync
*/
export function syncViewWithBlueprint(tView: TView, lView: LView) {
for (let i = lView.length; i < tView.blueprint.length; i++) {
lView.push(tView.blueprint[i]);
}
}
/**
* Processes a view in the creation mode. This includes a number of steps in a specific order:
* - creating view query functions (if any);
* - executing a template function in the creation mode;
* - updating static queries (if any);
* - creating child components defined in a given view.
*/
export function renderView<T>(tView: TView, lView: LView<T>, context: T): void {
ngDevMode && assertEqual(isCreationMode(lView), true, 'Should be run in creation mode');
enterView(lView);
try {
const viewQuery = tView.viewQuery;
if (viewQuery !== null) {
executeViewQueryFn<T>(RenderFlags.Create, viewQuery, context);
}
// Execute a template associated with this view, if it exists. A template function might not be
// defined for the root component views.
const templateFn = tView.template;
if (templateFn !== null) {
executeTemplate<T>(tView, lView, templateFn, RenderFlags.Create, context);
}
// This needs to be set before children are processed to support recursive components.
// This must be set to false immediately after the first creation run because in an
// ngFor loop, all the views will be created together before update mode runs and turns
// off firstCreatePass. If we don't set it here, instances will perform directive
// matching, etc again and again.
if (tView.firstCreatePass) {
tView.firstCreatePass = false;
}
// We resolve content queries specifically marked as `static` in creation mode. Dynamic
// content queries are resolved during change detection (i.e. update mode), after embedded
// views are refreshed (see block above).
if (tView.staticContentQueries) {
refreshContentQueries(tView, lView);
}
// We must materialize query results before child components are processed
// in case a child component has projected a container. The LContainer needs
// to exist so the embedded views are properly attached by the container.
if (tView.staticViewQueries) {
executeViewQueryFn<T>(RenderFlags.Update, tView.viewQuery!, context);
}
// Render child component views.
const components = tView.components;
if (components !== null) {
renderChildComponents(lView, components);
}
} catch (error) {
// If we didn't manage to get past the first template pass due to
// an error, mark the view as corrupted so we can try to recover.
if (tView.firstCreatePass) {
tView.incompleteFirstPass = true;
tView.firstCreatePass = false;
}
throw error;
} finally {
lView[FLAGS] &= ~LViewFlags.CreationMode;
leaveView();
}
}
/** Renders child components in the current view (creation mode). */
function renderChildComponents(hostLView: LView, components: number[]): void {
for (let i = 0; i < components.length; i++) {
renderComponent(hostLView, components[i]);
}
}