Cracking the Angular Renderer

Chau Tran
twitter.com/@nartc1410
github.com/nartc
What is Angular Renderer?
Platform Browser
Platform Browser
@Component({
template: `
<div>This is inside the div</div>
<app-parent />
`,
imports: [ParentComponent],
})
export class AppComponent {
title = 'ng-renderer';
}
Platform Browser
Component
Template
Platform Browser
Component
Template
Compiler
Platform Browser
Component
Template
Compiler
Template
Instructions
Platform Browser
Component
Template
Compiler
Template
Instructions
Renderer
Platform Browser
Component
Template
Compiler
Template
Instructions
Renderer
Platform
Platform Browser
the bridge between your template and the platform
Component
Template
Compiler
Template
Instructions
Renderer
Platform
Platform Browser
the bridge between Angular Core and the platform
Angular
Core
Renderer
Platform
Platform Browser
class AppComponent {
constructor() {
this.title = 'ng-renderer';
}
}
AppComponent.ɵfac = function AppComponent_Factory(t) {
return new (t || AppComponent)();
};
AppComponent.ɵcmp = /*@__PURE__*/_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineComponent"]({
type: AppComponent,
selectors: [["app-root"]],
standalone: true,
features: [_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵStandaloneFeature"]],
decls: 3,
vars: 0,
template: function AppComponent_Template(rf, ctx) {
if (rf & 1) {
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelementStart"](0, "div");
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵtext"](1, "This is inside the div");
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelementEnd"]();
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelement"](2, "app-parent");
}
},
dependencies: [_parent_component__WEBPACK_IMPORTED_MODULE_0__.Parent],
encapsulation: 2
});`Platform Browser
class AppComponent {
constructor() {
this.title = 'ng-renderer';
}
}
AppComponent.ɵfac = function AppComponent_Factory(t) {
return new (t || AppComponent)();
};
AppComponent.ɵcmp = /*@__PURE__*/_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineComponent"]({
type: AppComponent,
selectors: [["app-root"]],
standalone: true,
features: [_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵStandaloneFeature"]],
decls: 3,
vars: 0,
template: function AppComponent_Template(rf, ctx) {
if (rf & 1) {
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelementStart"](0, "div");
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵtext"](1, "This is inside the div");
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelementEnd"]();
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelement"](2, "app-parent");
}
},
dependencies: [_parent_component__WEBPACK_IMPORTED_MODULE_0__.Parent],
encapsulation: 2
});`@Component({
template: `
<div>This is inside the div</div>
<app-parent />
`,
imports: [ParentComponent],
})
export class AppComponent {
title = 'ng-renderer';
}
Platform Browser
class AppComponent {
constructor() {
this.title = 'ng-renderer';
}
}
AppComponent.ɵfac = function AppComponent_Factory(t) {
return new (t || AppComponent)();
};
AppComponent.ɵcmp = /*@__PURE__*/_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineComponent"]({
type: AppComponent,
selectors: [["app-root"]],
standalone: true,
features: [_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵStandaloneFeature"]],
decls: 3,
vars: 0,
template: function AppComponent_Template(rf, ctx) {
if (rf & 1) {
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelementStart"](0, "div");
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵtext"](1, "This is inside the div");
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelementEnd"]();
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelement"](2, "app-parent");
}
},
dependencies: [_parent_component__WEBPACK_IMPORTED_MODULE_0__.Parent],
encapsulation: 2
});`Platform Browser
class AppComponent {
constructor() {
this.title = 'ng-renderer';
}
}
AppComponent.ɵfac = function AppComponent_Factory(t) {
return new (t || AppComponent)();
};
AppComponent.ɵcmp = /*@__PURE__*/_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineComponent"]({
type: AppComponent,
selectors: [["app-root"]],
standalone: true,
features: [_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵStandaloneFeature"]],
decls: 3,
vars: 0,
template: function AppComponent_Template(rf, ctx) {
if (rf & 1) {
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelementStart"](0, "div");
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵtext"](1, "This is inside the div");
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelementEnd"]();
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelement"](2, "app-parent");
}
},
dependencies: [_parent_component__WEBPACK_IMPORTED_MODULE_0__.Parent],
encapsulation: 2
});`Platform Browser
class AppComponent {
constructor() {
this.title = 'ng-renderer';
}
}
AppComponent.ɵfac = function AppComponent_Factory(t) {
return new (t || AppComponent)();
};
AppComponent.ɵcmp = /*@__PURE__*/_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineComponent"]({
type: AppComponent,
selectors: [["app-root"]],
standalone: true,
features: [_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵStandaloneFeature"]],
decls: 3,
vars: 0,
template: function AppComponent_Template(rf, ctx) {
if (rf & 1) {
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelementStart"](0, "div");
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵtext"](1, "This is inside the div");
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelementEnd"]();
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelement"](2, "app-parent");
}
},
dependencies: [_parent_component__WEBPACK_IMPORTED_MODULE_0__.Parent],
encapsulation: 2
});`function ɵɵelementStart(index, name, attrsIndex, localRefsIndex) {
/* ... */
const renderer = lView[RENDERER];
const native = (lView[adjustedIndex] = createElementNode(renderer, name, getNamespace$1()));
/* ... */
return ɵɵelementStart;
}
/* ... */
function createElementNode(renderer, name, namespace) {
ngDevMode && ngDevMode.rendererCreateElement++;
return renderer.createElement(name, namespace);
}Platform Browser
class AppComponent {
constructor() {
this.title = 'ng-renderer';
}
}
AppComponent.ɵfac = function AppComponent_Factory(t) {
return new (t || AppComponent)();
};
AppComponent.ɵcmp = /*@__PURE__*/_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineComponent"]({
type: AppComponent,
selectors: [["app-root"]],
standalone: true,
features: [_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵStandaloneFeature"]],
decls: 3,
vars: 0,
template: function AppComponent_Template(rf, ctx) {
if (rf & 1) {
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelementStart"](0, "div");
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵtext"](1, "This is inside the div");
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelementEnd"]();
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelement"](2, "app-parent");
}
},
dependencies: [_parent_component__WEBPACK_IMPORTED_MODULE_0__.Parent],
encapsulation: 2
});`function ɵɵelementStart(index, name, attrsIndex, localRefsIndex) {
/* ... */
const renderer = lView[RENDERER];
const native = (lView[adjustedIndex] = createElementNode(renderer, name, getNamespace$1()));
/* ... */
return ɵɵelementStart;
}
/* ... */
function createElementNode(renderer, name, namespace) {
ngDevMode && ngDevMode.rendererCreateElement++;
return renderer.createElement(name, namespace);
}Platform Browser
class AppComponent {
constructor() {
this.title = 'ng-renderer';
}
}
AppComponent.ɵfac = function AppComponent_Factory(t) {
return new (t || AppComponent)();
};
AppComponent.ɵcmp = /*@__PURE__*/_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineComponent"]({
type: AppComponent,
selectors: [["app-root"]],
standalone: true,
features: [_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵStandaloneFeature"]],
decls: 3,
vars: 0,
template: function AppComponent_Template(rf, ctx) {
if (rf & 1) {
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelementStart"](0, "div");
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵtext"](1, "This is inside the div");
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelementEnd"]();
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelement"](2, "app-parent");
}
},
dependencies: [_parent_component__WEBPACK_IMPORTED_MODULE_0__.Parent],
encapsulation: 2
});`function ɵɵelementStart(index, name, attrsIndex, localRefsIndex) {
/* ... */
const renderer = lView[RENDERER];
const native = (lView[adjustedIndex] = createElementNode(renderer, name, getNamespace$1()));
/* ... */
return ɵɵelementStart;
}
/* ... */
function createElementNode(renderer, name, namespace) {
ngDevMode && ngDevMode.rendererCreateElement++;
return renderer.createElement(name, namespace);
}Platform Browser
class AppComponent {
constructor() {
this.title = 'ng-renderer';
}
}
AppComponent.ɵfac = function AppComponent_Factory(t) {
return new (t || AppComponent)();
};
AppComponent.ɵcmp = /*@__PURE__*/_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineComponent"]({
type: AppComponent,
selectors: [["app-root"]],
standalone: true,
features: [_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵStandaloneFeature"]],
decls: 3,
vars: 0,
template: function AppComponent_Template(rf, ctx) {
if (rf & 1) {
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelementStart"](0, "div");
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵtext"](1, "This is inside the div");
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelementEnd"]();
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelement"](2, "app-parent");
}
},
dependencies: [_parent_component__WEBPACK_IMPORTED_MODULE_0__.Parent],
encapsulation: 2
});`function ɵɵelementStart(index, name, attrsIndex, localRefsIndex) {
/* ... */
const renderer = lView[RENDERER];
const native = (lView[adjustedIndex] = createElementNode(renderer, name, getNamespace$1()));
/* ... */
return ɵɵelementStart;
}
/* ... */
function createElementNode(renderer, name, namespace) {
ngDevMode && ngDevMode.rendererCreateElement++;
return renderer.createElement(name, namespace);
}Platform Browser
class AppComponent {
constructor() {
this.title = 'ng-renderer';
}
}
AppComponent.ɵfac = function AppComponent_Factory(t) {
return new (t || AppComponent)();
};
AppComponent.ɵcmp = /*@__PURE__*/_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineComponent"]({
type: AppComponent,
selectors: [["app-root"]],
standalone: true,
features: [_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵStandaloneFeature"]],
decls: 3,
vars: 0,
template: function AppComponent_Template(rf, ctx) {
if (rf & 1) {
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelementStart"](0, "div");
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵtext"](1, "This is inside the div");
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelementEnd"]();
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelement"](2, "app-parent");
}
},
dependencies: [_parent_component__WEBPACK_IMPORTED_MODULE_0__.Parent],
encapsulation: 2
});`function ɵɵelementStart(index, name, attrsIndex, localRefsIndex) {
/* ... */
const renderer = lView[RENDERER];
const native = (lView[adjustedIndex] = createElementNode(renderer, name, getNamespace$1()));
/* ... */
return ɵɵelementStart;
}
/* ... */
function createElementNode(renderer, name, namespace) {
ngDevMode && ngDevMode.rendererCreateElement++;
return renderer.createElement(name, namespace);
}Platform Browser
class AppComponent {
constructor() {
this.title = 'ng-renderer';
}
}
AppComponent.ɵfac = function AppComponent_Factory(t) {
return new (t || AppComponent)();
};
AppComponent.ɵcmp = /*@__PURE__*/_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineComponent"]({
type: AppComponent,
selectors: [["app-root"]],
standalone: true,
features: [_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵStandaloneFeature"]],
decls: 3,
vars: 0,
template: function AppComponent_Template(rf, ctx) {
if (rf & 1) {
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelementStart"](0, "div");
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵtext"](1, "This is inside the div");
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelementEnd"]();
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelement"](2, "app-parent");
}
},
dependencies: [_parent_component__WEBPACK_IMPORTED_MODULE_0__.Parent],
encapsulation: 2
});`function ɵɵelementStart(index, name, attrsIndex, localRefsIndex) {
/* ... */
const renderer = lView[RENDERER];
const native = (lView[adjustedIndex] = createElementNode(renderer, name, getNamespace$1()));
/* ... */
return ɵɵelementStart;
}
/* ... */
function createElementNode(renderer, name, namespace) {
ngDevMode && ngDevMode.rendererCreateElement++;
// feels similar? document.createElement('div') anyone?
return renderer.createElement(name, namespace);
}Platform Browser
class AppComponent {
constructor() {
this.title = 'ng-renderer';
}
}
AppComponent.ɵfac = function AppComponent_Factory(t) {
return new (t || AppComponent)();
};
AppComponent.ɵcmp = /*@__PURE__*/_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵdefineComponent"]({
type: AppComponent,
selectors: [["app-root"]],
standalone: true,
features: [_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵStandaloneFeature"]],
decls: 3,
vars: 0,
template: function AppComponent_Template(rf, ctx) {
if (rf & 1) {
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelementStart"](0, "div");
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵtext"](1, "This is inside the div");
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelementEnd"]();
_angular_core__WEBPACK_IMPORTED_MODULE_1__["ɵɵelement"](2, "app-parent");
}
},
dependencies: [_parent_component__WEBPACK_IMPORTED_MODULE_0__.Parent],
encapsulation: 2
});`function ɵɵelementStart(index, name, attrsIndex, localRefsIndex) {
/* ... */
const renderer = lView[RENDERER];
const native = (lView[adjustedIndex] = createElementNode(renderer, name, getNamespace$1()));
/* ... */
return ɵɵelementStart;
}
/* ... */
function createElementNode(renderer, name, namespace) {
ngDevMode && ngDevMode.rendererCreateElement++;
return renderer.createElement(name, namespace);
}Platform Browser
Property Binding: [property]="expr"
Platform Browser
Property Binding: [property]="expr"
Attribute Binding: attrName="static-value"
Platform Browser
Property Binding: [property]="expr"
Attribute Binding: attrName="static-value"
Event Binding: (eventName)="handler()"
Platform Browser
<div [id]="title" title="my-title" (click)="onClick()">This is inside the div</div>Platform Browser
AppComponent.ɵcmp = /*@__PURE__*/ _angular_core__WEBPACK_IMPORTED_MODULE_1__['ɵɵdefineComponent']({
/* ... */
consts: [['title', 'my-title', 3, 'id']],
template: function AppComponent_Template(rf, ctx) {
if (rf & 1) {
/* ... */
_angular_core__WEBPACK_IMPORTED_MODULE_1__['ɵɵelementStart'](0, 'div', 0);
_angular_core__WEBPACK_IMPORTED_MODULE_1__['ɵɵlistener'](
'click',
function AppComponent_Template_div_click_0_listener() {
return ctx.onClick();
}
);
/* ... */
}
if (rf & 2) {
_angular_core__WEBPACK_IMPORTED_MODULE_1__['ɵɵproperty']('id', ctx.title);
}
},
/* ... */
});
Platform Browser
AppComponent.ɵcmp = /*@__PURE__*/ _angular_core__WEBPACK_IMPORTED_MODULE_1__['ɵɵdefineComponent']({
/* ... */
consts: [['title', 'my-title', 3, 'id']],
template: function AppComponent_Template(rf, ctx) {
if (rf & 1) {
/* ... */
_angular_core__WEBPACK_IMPORTED_MODULE_1__['ɵɵelementStart'](0, 'div', 0);
_angular_core__WEBPACK_IMPORTED_MODULE_1__['ɵɵlistener'](
'click',
function AppComponent_Template_div_click_0_listener() {
return ctx.onClick();
}
);
/* ... */
}
if (rf & 2) {
_angular_core__WEBPACK_IMPORTED_MODULE_1__['ɵɵproperty']('id', ctx.title);
}
},
/* ... */
});
<div [id]="title" title="my-title" (click)="onClick()">This is inside the div</div>Platform Browser
AppComponent.ɵcmp = /*@__PURE__*/ _angular_core__WEBPACK_IMPORTED_MODULE_1__['ɵɵdefineComponent']({
/* ... */
consts: [['title', 'my-title', 3, 'id']],
template: function AppComponent_Template(rf, ctx) {
if (rf & 1) {
/* ... */
_angular_core__WEBPACK_IMPORTED_MODULE_1__['ɵɵelementStart'](0, 'div', 0);
_angular_core__WEBPACK_IMPORTED_MODULE_1__['ɵɵlistener'](
'click',
function AppComponent_Template_div_click_0_listener() {
return ctx.onClick();
}
);
/* ... */
}
if (rf & 2) {
_angular_core__WEBPACK_IMPORTED_MODULE_1__['ɵɵproperty']('id', ctx.title);
}
},
/* ... */
});
<div [id]="title" title="my-title" (click)="onClick()">This is inside the div</div>Platform Browser
AppComponent.ɵcmp = /*@__PURE__*/ _angular_core__WEBPACK_IMPORTED_MODULE_1__['ɵɵdefineComponent']({
/* ... */
consts: [['title', 'my-title', 3, 'id']],
template: function AppComponent_Template(rf, ctx) {
if (rf & 1) {
/* ... */
_angular_core__WEBPACK_IMPORTED_MODULE_1__['ɵɵelementStart'](0, 'div', 0);
_angular_core__WEBPACK_IMPORTED_MODULE_1__['ɵɵlistener'](
'click',
function AppComponent_Template_div_click_0_listener() {
return ctx.onClick();
}
);
/* ... */
}
if (rf & 2) {
_angular_core__WEBPACK_IMPORTED_MODULE_1__['ɵɵproperty']('id', ctx.title);
}
},
/* ... */
});
<div [id]="title" title="my-title" (click)="onClick()">This is inside the div</div>Platform Browser
AppComponent.ɵcmp = /*@__PURE__*/ _angular_core__WEBPACK_IMPORTED_MODULE_1__['ɵɵdefineComponent']({
/* ... */
consts: [['title', 'my-title', 3, 'id']],
template: function AppComponent_Template(rf, ctx) {
if (rf & 1) {
/* ... */
_angular_core__WEBPACK_IMPORTED_MODULE_1__['ɵɵelementStart'](0, 'div', 0);
_angular_core__WEBPACK_IMPORTED_MODULE_1__['ɵɵlistener'](
'click',
function AppComponent_Template_div_click_0_listener() {
return ctx.onClick();
}
);
/* ... */
}
if (rf & 2) {
_angular_core__WEBPACK_IMPORTED_MODULE_1__['ɵɵproperty']('id', ctx.title);
}
},
/* ... */
});
<div [id]="title" title="my-title" (click)="onClick()">This is inside the div</div>Create a Custom Renderer
Create a Custom Renderer
Create a Custom Renderer
RendererFactory2
Create a Custom Renderer
RendererFactory2
Renderer2
+
Create a Custom Renderer
Demo
Thank you
Angular v17
(upto 17.3)
output()
input()
queries
model()
browserUrl
https://github.com/angular/angular/issues/17004
GuardResult & MaybeAsync
TypeScript 5.4
https://devblogs.microsoft.com/typescript/announcing-typescript-5-4/
https://github.com/angular/angular/releases
Angular Three Renderer
By Chau Tran
Angular Three Renderer
- 1,175