Skip to content

Commit 0fa818b

Browse files
mheveryIgorMinar
authored andcommitted
feat(core): Moving Renderer3 into @angular/core (#20855)
PR Close #20855
1 parent bc66d27 commit 0fa818b

39 files changed

Lines changed: 8544 additions & 4 deletions

karma-js.conf.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ module.exports = function(config) {
5757
'dist/all/@angular/**/*node_only_spec.js',
5858
'dist/all/@angular/benchpress/**',
5959
'dist/all/@angular/compiler-cli/**',
60+
'dist/all/@angular/core/test/render3/**',
6061
'dist/all/@angular/compiler/test/aot/**',
6162
'dist/all/@angular/examples/**/e2e_test/*',
6263
'dist/all/@angular/language-service/**',

packages/core/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ ng_module(
1414
module_name = "@angular/core",
1515
tsconfig = "//packages:tsconfig",
1616
deps = [
17+
"//packages:types",
1718
"@rxjs",
1819
],
1920
)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
function stringify(value: any) {
10+
return typeof value === 'string' ? `"${value}"` : '' + value;
11+
}
12+
13+
export function assertNumber(actual: any, condition: string) {
14+
(typeof actual != 'number') && assertThrow(actual, 'number', condition, 'typeof ==');
15+
}
16+
17+
export function assertEqual<T>(
18+
actual: T, expected: T, condition: string, serializer?: ((v: T) => string)) {
19+
(actual != expected) && assertThrow(actual, expected, condition, '==', serializer);
20+
}
21+
22+
export function assertLessThan<T>(actual: T, expected: T, condition: string) {
23+
(actual < expected) && assertThrow(actual, expected, condition, '>');
24+
}
25+
26+
export function assertNotNull<T>(actual: T, condition: string) {
27+
assertNotEqual(actual, null, condition);
28+
}
29+
30+
export function assertNotEqual<T>(actual: T, expected: T, condition: string) {
31+
(actual == expected) && assertThrow(actual, expected, condition, '!=');
32+
}
33+
34+
export function assertThrow<T>(
35+
actual: T, expected: T, condition: string, operator: string,
36+
serializer: ((v: T) => string) = stringify) {
37+
throw new Error(
38+
`ASSERT: expected ${condition} ${operator} ${serializer(expected)} but was ${serializer(actual)}!`);
39+
}
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {ComponentRef, EmbeddedViewRef, Injector} from '../core';
10+
import {assertNotNull} from './assert';
11+
import {NG_HOST_SYMBOL, createError, createViewState, directiveCreate, elementHost, enterView, leaveView} from './instructions';
12+
import {LElement} from './interfaces';
13+
import {ComponentDef, ComponentType} from './public_interfaces';
14+
import {RElement, Renderer3, RendererFactory3} from './renderer';
15+
import {stringify} from './util';
16+
17+
18+
19+
/**
20+
* Options which control how the component should be bootstrapped.
21+
*/
22+
export interface CreateComponentOptionArgs {
23+
/**
24+
* Which renderer to use.
25+
*/
26+
renderer?: Renderer3;
27+
28+
rendererFactory?: RendererFactory3;
29+
30+
/**
31+
* Which host element should the component be bootstrapped on. If not specified
32+
* the component definition's `tag` is used to query the existing DOM for the
33+
* element to bootstrap.
34+
*/
35+
host?: RElement|string;
36+
37+
/**
38+
* Optional Injector which is the Module Injector for the component.
39+
*/
40+
injector?: Injector;
41+
42+
/**
43+
* a set of features which should be applied to this component.
44+
*/
45+
features?: (<T>(component: T, componentDef: ComponentDef<T>) => void)[];
46+
}
47+
48+
49+
/**
50+
* Bootstrap a Component into an existing host element and return `ComponentRef`.
51+
*
52+
* @param componentType Component to bootstrap
53+
* @param options Optional parameters which control bootstrapping
54+
*/
55+
export function createComponentRef<T>(
56+
componentType: ComponentType<T>, opts: CreateComponentOptionArgs): ComponentRef<T> {
57+
const component = renderComponent(componentType, opts);
58+
const hostView = createViewRef(detectChanges.bind(component), component);
59+
return {
60+
location: {nativeElement: getHostElement(component)},
61+
injector: opts.injector || NULL_INJECTOR,
62+
instance: component,
63+
hostView: hostView,
64+
changeDetectorRef: hostView,
65+
componentType: componentType,
66+
destroy: function() {},
67+
onDestroy: function(cb: Function): void {}
68+
};
69+
}
70+
71+
function createViewRef<T>(detectChanges: () => void, context: T): EmbeddedViewRef<T> {
72+
return addDestroyable(
73+
{
74+
rootNodes: null !,
75+
// inherited from core/ChangeDetectorRef
76+
markForCheck: () => {
77+
if (ngDevMode) {
78+
implement();
79+
}
80+
},
81+
detach: () => {
82+
if (ngDevMode) {
83+
implement();
84+
}
85+
},
86+
detectChanges: detectChanges,
87+
checkNoChanges: () => {
88+
if (ngDevMode) {
89+
implement();
90+
}
91+
},
92+
reattach: () => {
93+
if (ngDevMode) {
94+
implement();
95+
}
96+
},
97+
},
98+
context);
99+
}
100+
101+
interface DestroyRef<T> {
102+
context: T;
103+
destroyed: boolean;
104+
destroy(): void;
105+
onDestroy(cb: Function): void;
106+
}
107+
108+
function implement() {
109+
throw new Error('NotImplemented');
110+
}
111+
112+
function addDestroyable<T, C>(obj: any, context: C): T&DestroyRef<C> {
113+
let destroyFn: Function[]|null = null;
114+
obj.destroyed = false;
115+
obj.destroy = function() {
116+
destroyFn && destroyFn.forEach((fn) => fn());
117+
this.destroyed = true;
118+
};
119+
obj.onDestroy = (fn: Function) => (destroyFn || (destroyFn = [])).push(fn);
120+
return obj;
121+
}
122+
123+
124+
// TODO: A hack to not pull in the NullInjector from @angular/core.
125+
export const NULL_INJECTOR: Injector = {
126+
get: function(token: any, notFoundValue?: any) {
127+
throw new Error('NullInjector: Not found: ' + stringify(token));
128+
}
129+
};
130+
131+
132+
/**
133+
* Bootstrap a Component into an existing host element and return `NgComponent`.
134+
*
135+
* NgComponent is a light weight Custom Elements inspired API for bootstrapping and
136+
* interacting with bootstrapped component.
137+
*
138+
* @param componentType Component to bootstrap
139+
* @param options Optional parameters which control bootstrapping
140+
*/
141+
export function renderComponent<T>(
142+
componentType: ComponentType<T>, opts: CreateComponentOptionArgs = {}): T {
143+
const renderer = opts.renderer || document;
144+
const componentDef = componentType.ngComponentDef;
145+
let component: T;
146+
const oldView = enterView(createViewState(-1, renderer), null);
147+
try {
148+
elementHost(opts.host || componentDef.tag);
149+
component = directiveCreate(0, componentDef.n(), componentDef);
150+
} finally {
151+
leaveView(oldView);
152+
}
153+
154+
opts.features && opts.features.forEach((feature) => feature(component, componentDef));
155+
detectChanges(component);
156+
return component;
157+
}
158+
159+
export function detectChanges<T>(component: T) {
160+
ngDevMode && assertNotNull(component, 'component');
161+
const hostNode = (component as any)[NG_HOST_SYMBOL] as LElement;
162+
if (ngDevMode && !hostNode) {
163+
createError('Not a directive instance', component);
164+
}
165+
ngDevMode && assertNotNull(hostNode.data, 'hostNode.data');
166+
const oldView = enterView(hostNode.view !, hostNode);
167+
try {
168+
(component.constructor as ComponentType<T>).ngComponentDef.r(0, 0);
169+
isDirty = false;
170+
} finally {
171+
leaveView(oldView);
172+
}
173+
}
174+
175+
let isDirty = false;
176+
export function markDirty<T>(
177+
component: T, scheduler: (fn: () => void) => void = requestAnimationFrame) {
178+
ngDevMode && assertNotNull(component, 'component');
179+
if (!isDirty) {
180+
isDirty = true;
181+
scheduler(detectChanges.bind(null, component));
182+
}
183+
}
184+
185+
export function getHostElement<T>(component: T): RElement {
186+
return ((component as any)[NG_HOST_SYMBOL] as LElement).native;
187+
}

0 commit comments

Comments
 (0)