├── .gitignore
├── LICENSE
├── README.md
├── index.html
├── package.json
├── src
├── custom_browser_platform.ts
└── main.ts
├── tsconfig.json
└── webpack.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules/
2 | /dist/
3 | .idea
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2010-2016 Google, Inc. http://angularjs.org
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Experiments with Angular Renderers: Example App
2 |
3 | ## How to Run
4 | ```
5 | clone this repo
6 | npm install
7 | npm run start
8 | open localhost:8080
9 | ```
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Document
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "CustomPlatform",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "webpack-dev-server",
9 | "build": "webpack -p"
10 | },
11 | "author": "",
12 | "license": "ISC",
13 | "dependencies": {
14 | "@angular/common": "2.4.0",
15 | "@angular/compiler": "2.4.0",
16 | "@angular/core": "2.4.0",
17 | "@angular/platform-browser": "2.4.0",
18 | "core-js": "^2.4.1",
19 | "rxjs": "5.0.0-rc.1",
20 | "ts-loader": "^1.3.3",
21 | "zone.js": "^0.6.26"
22 | },
23 | "devDependencies": {
24 | "clang-format": "^1.0.32",
25 | "typescript": "2.0.7",
26 | "webpack": "2.1.0-beta.25",
27 | "webpack-dev-server": "2.1.0-beta.10",
28 | "@angular/platform-browser-dynamic": "2.4.0",
29 | "@angular/platform-server": "2.4.0",
30 | "@angular/compiler-cli": "2.4.0"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/custom_browser_platform.ts:
--------------------------------------------------------------------------------
1 | import {Renderer, RootRenderer, RenderComponentType, NgModule, APP_INITIALIZER, NgZone} from "@angular/core";
2 | import {BrowserModule} from "@angular/platform-browser";
3 |
4 |
5 | // We render everything into a forest of Nodes.
6 | // View, Element, and Text are different types of nodes.
7 | class View {
8 | children: Node[] = [];
9 | }
10 |
11 | class Element {
12 | constructor(public name: string) {}
13 | attributes: {[n:string]:string} = {};
14 | properties: {[n:string]:any} = {};
15 | children: Node[] = [];
16 | view: View = null;
17 | }
18 |
19 | class Text {
20 | constructor(public value: string) {}
21 | }
22 |
23 | type Node = Element | View | Text;
24 |
25 |
26 | // We define a custom in-memory renderer.
27 | // To make thing simpler we only support a subset of all the operations.
28 | // We support element creation, text creation, and setting attributes and properties.
29 | class InMemoryRootRenderer implements RootRenderer {
30 | public roots: any[] = [];
31 |
32 | renderComponent(componentProto: RenderComponentType): Renderer {
33 | return new InMemoryRenderer(this.roots);
34 | }
35 | }
36 |
37 | class InMemoryRenderer implements Renderer {
38 | constructor(private roots: any[]) {}
39 |
40 | selectRootElement(selectorOrNode: string|any, debugInfo?: any): Element {
41 | const root = new Element(selectorOrNode);
42 | this.roots.push(root);
43 | return root;
44 | }
45 |
46 | createElement(parentElement: any, name: string, debugInfo?: any): Element {
47 | const element = new Element(name);
48 | parentElement.children.push(element);
49 | return element;
50 | }
51 |
52 | createViewRoot(hostElement: Element): View {
53 | const view = new View();
54 | hostElement.view = view;
55 | return view;
56 | }
57 |
58 | createText(parentElement: Element, value: string, debugInfo?: any): Text {
59 | const text = new Text(value);
60 | parentElement.children.push(text);
61 | return text;
62 | }
63 |
64 | setElementProperty(renderElement: Element, propertyName: string, propertyValue: any): void {
65 | renderElement.properties[propertyName] = propertyValue;
66 | }
67 |
68 | setElementAttribute(renderElement: Element, attributeName: string, attributeValue: string): void {
69 | renderElement.attributes[attributeName] = attributeValue;
70 | }
71 |
72 | setText(renderNode: Text, text: string): void {
73 | renderNode.value = text;
74 | }
75 |
76 | setBindingDebugInfo(renderElement: any, propertyName: string, propertyValue: string): void {
77 | }
78 |
79 | createTemplateAnchor(parentElement: any): any {
80 | throw new Error("not implemented");
81 | }
82 |
83 | projectNodes(parentElement: any, nodes: any[]): void {
84 | throw new Error("not implemented");
85 | }
86 |
87 | attachViewAfter(node: any, viewRootNodes: any[]): void {
88 | throw new Error("not implemented");
89 | }
90 |
91 | detachView(viewRootNodes: any[]): void {
92 | throw new Error("not implemented");
93 | }
94 |
95 | destroyView(hostElement: any, viewAllNodes: any[]): void {
96 | throw new Error("not implemented");
97 | }
98 |
99 | listen(renderElement: any, name: string, callback: Function): Function {
100 | throw new Error("not implemented");
101 | }
102 |
103 | listenGlobal(target: string, name: string, callback: Function): Function {
104 | throw new Error("not implemented");
105 | }
106 |
107 | setElementClass(renderElement: any, className: string, isAdd: boolean): void {
108 | throw new Error("not implemented");
109 | }
110 |
111 | setElementStyle(renderElement: any, styleName: string, styleValue: string): void {
112 | throw new Error("not implemented");
113 | }
114 |
115 | invokeElementMethod(renderElement: any, methodName: string, args?: any[]): void {
116 | throw new Error("not implemented");
117 | }
118 |
119 | animate(element: any, startingStyles: any, keyframes: any[], duration: number, delay: number, easing: string): any {
120 | throw new Error("not implemented");
121 | }
122 | }
123 |
124 | // We print a new snapshot every time the zone gets stable.
125 | // That would be the moment when the browse would modify the DOM.
126 | function setUpRenderFlushing(zone: NgZone, renderer: InMemoryRootRenderer) {
127 | return () => {
128 | zone.onStable.subscribe(() => {
129 | console.group("--");
130 | console.log(renderer.roots);
131 | console.log(JSON.stringify(renderer.roots, null, 2));
132 | console.groupEnd();
133 | });
134 | };
135 | }
136 |
137 | // Instead of defining the whole platform from scratch, we essentially extend
138 | // the browser platform (hence the import and the export).
139 | @NgModule({
140 | imports: [BrowserModule],
141 | exports: [BrowserModule],
142 | providers: [
143 | {provide: RootRenderer, useClass: InMemoryRootRenderer},
144 | {provide: APP_INITIALIZER, multi: true, useFactory: setUpRenderFlushing, deps: [NgZone, RootRenderer]}
145 | ]
146 | })
147 | export class CustomBrowserPlatform {}
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | // import polyfills
2 | import 'core-js/es7/reflect'
3 | import 'zone.js/dist/zone'
4 |
5 | // import Angular
6 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
7 | import {NgModule, Component, Renderer, ElementRef} from "@angular/core";
8 | import {CustomBrowserPlatform} from "./custom_browser_platform";
9 |
10 | @Component({
11 | selector: 'child',
12 | template: `
13 | ChildCmp
14 | `
15 | })
16 | class ChildCmp {
17 | constructor(renderer: Renderer, ref: ElementRef) {
18 | renderer.setElementProperty(ref.nativeElement, "someProp", 123);
19 | }
20 | }
21 |
22 | @Component({
23 | selector: 'app',
24 | template: `
25 | AppCmp [
26 |
27 | ]
28 | `
29 | })
30 | class AppCmp {}
31 |
32 | @NgModule({
33 | imports: [CustomBrowserPlatform],
34 | declarations: [ChildCmp, AppCmp],
35 | bootstrap: [AppCmp]
36 | })
37 | class AppModule {}
38 |
39 | platformBrowserDynamic().bootstrapModule(AppModule);
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "es2015",
4 | "moduleResolution": "node",
5 | "target": "es5",
6 | "noImplicitAny": false,
7 | "sourceMap": true,
8 | "mapRoot": "",
9 | "emitDecoratorMetadata": true,
10 | "experimentalDecorators": true,
11 | "lib": [
12 | "es2015",
13 | "dom"
14 | ],
15 | "outDir": "lib",
16 | "skipLibCheck": true,
17 | "rootDir": "."
18 | }
19 | }
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | resolve: {
5 | extensions: ['.ts', '.js'],
6 | },
7 | entry: './src/main.ts',
8 | output: {
9 | path: path.join(process.cwd(), 'dist'),
10 | publicPath: 'dist/',
11 | filename: "bundle.js"
12 | },
13 | module: {
14 | loaders: [
15 | {
16 | test: /\.ts$/,
17 | loader: 'ts-loader'
18 | }
19 | ]
20 | },
21 |
22 | devServer: {
23 | historyApiFallback: true
24 | }
25 | };
26 |
--------------------------------------------------------------------------------