Skip to content

Commit 399eae8

Browse files
authored
Merge pull request #5 from ara-framework/refactor/support-aot
refactor: remove bootstrapping responsability to library
2 parents 49d74c3 + 3beb35c commit 399eae8

4 files changed

Lines changed: 179 additions & 62 deletions

File tree

README.md

Lines changed: 144 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,162 @@
44

55
On the server, wraps the component in a function to render it to a HTML string given its props.
66

7-
On the client, calling this function with your component scans the DOM for any server-side rendered instances of it. It then resumes those components using the server-specified props.
7+
On the client, uses the `HypernovaModuleFactory` to provide the metadata necessary to bootstrap the components.
8+
9+
**Note**: `renderAngular` and `mountComponent` are not supported anymore. They were removed to move the boostrapping responsability to the library consumer in order to support AOT and JIT compiling.
810

911
## Install
1012

1113
```sh
1214
npm install hypernova-angular
1315
```
1416

15-
## Usage
17+
## Server Usage
1618

17-
Here's how to use it in your browser module:
19+
Uses `renderAngular` to return hypernova bindings.
1820

1921
```ts
20-
import { renderAngular } from 'hypernova-angular'
22+
import { renderAngular } from 'hypernova-angular/server'
2123

2224
import { ExampleModule } from './components/example/example.module'
2325
import { ExampleComponent } from './components/example/example.component'
2426

25-
renderAngular('Example', ExampleComponent, ExampleModule)
27+
hypernova({
28+
getComponent (name) {
29+
if (name === 'Example') {
30+
return renderAngular(name, ExampleComponent, ExampleModule)
31+
}
32+
}
33+
}
34+
```
35+
36+
## Browser Usage
37+
38+
You can use [Ara CLI](https://github.com/ara-framework/ara-cli) to create a service (Nova) with everything ready to start.
39+
40+
```bash
41+
ara new:nova -t angular
42+
```
43+
44+
Or, you can use the following configurations.
45+
46+
### App Module
47+
48+
The following code define a `AppModule` responsible of bootstrapping the Angular component in Hypernova placeholder.
49+
50+
- `Hypernova.Name`: Name of the component to bootstrap.
51+
- `Hypernova.Node`: The placeholder where the component will be rendered.
52+
53+
```ts
54+
import { NgModule, Inject } from '@angular/core';
55+
import { BrowserModule } from '@angular/platform-browser';
56+
57+
import { ExampleModule } from './components/example/example.module'
58+
import { ExampleComponent } from './components/example/example.component';
59+
60+
const APP_ID = 'hypernova';
61+
62+
const components = {
63+
'Example': ExampleComponent
64+
}
65+
66+
@NgModule({
67+
imports: [
68+
ExampleModule,
69+
BrowserModule.withServerTransition({ appId: APP_ID }),
70+
],
71+
entryComponents: [ExampleComponent]
72+
})
73+
export class AppModule {
74+
constructor (
75+
@Inject('Hypernova.Name') private name: string,
76+
@Inject('Hypernova.Node') private node: HTMLElement
77+
){}
78+
79+
ngDoBootstrap(app) {
80+
const Component = components[this.name]
81+
if (Component) {
82+
return app.bootstrap(Component, this.node)
83+
}
84+
};
85+
}
86+
```
87+
88+
Use the `components` dictionary to support more components.
89+
90+
```ts
91+
const components = {
92+
[Hypernova.Name]: AngularComponent
93+
}
94+
```
95+
96+
### Browser JIT
97+
98+
`browser.main.ts`
99+
```ts
100+
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
101+
102+
import { AppModule } from './app.module';
103+
import { load, loadById, HypernovaModuleFactory } from 'hypernova-angular';
104+
105+
import { CompilerFactory, Compiler } from '@angular/core';
106+
107+
const platform = platformBrowserDynamic();
108+
109+
// Compile module (JIT)
110+
const compilerFactory: CompilerFactory = platform.injector.get(CompilerFactory);
111+
112+
const compiler: Compiler = compilerFactory.createCompiler([])
113+
114+
const moduleFactory = compiler.compileModuleSync(AppModule);
115+
116+
const render = (name: string, placeholder: any) => {
117+
118+
// Wrap module factory to provide necessary metadata to boostrap it.
119+
const hypernovaModuleFactory = new HypernovaModuleFactory(moduleFactory, name, placeholder);
120+
121+
platform.bootstrapModuleFactory(hypernovaModuleFactory);
122+
}
123+
124+
// Nova Bridge support
125+
document.addEventListener('NovaMount', (event) => {
126+
const { name, id } = (<CustomEvent>event).detail;
127+
128+
const placeholder = loadById(name, id);
129+
130+
if (placeholder) {
131+
render(name, placeholder);
132+
}
133+
})
134+
135+
// Render in placeholders rendered by Hypernova Server
136+
load('Example').forEach(render.bind(this, 'Example'));
137+
```
138+
139+
### Browser AOT
140+
141+
`browser.aot.main.ts`
142+
```ts
143+
import { platformBrowser } from '@angular/platform-browser';
144+
import { AppModuleNgFactory } from './app.module.ngfactory';
145+
import { load, loadById, HypernovaModuleFactory } from 'hypernova-angular';
146+
147+
enableProdMode();
148+
149+
const render = (name: string, placeholder: any) => {
150+
const hypernovaModuleFactory = new HypernovaModuleFactory(AppModuleNgFactory, name, placeholder);
151+
platformBrowser().bootstrapModuleFactory(hypernovaModuleFactory);
152+
}
153+
154+
document.addEventListener('NovaMount', (event) => {
155+
const { name, id } = (<CustomEvent>event).detail;
156+
157+
const placeholder = loadById(name, id);
158+
159+
if (placeholder) {
160+
render(name, placeholder);
161+
}
162+
})
163+
164+
load('Example').forEach(render.bind(this, 'Example'));
26165
```

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "hypernova-angular",
3-
"version": "1.0.0",
3+
"version": "2.0.0-alpha.0",
44
"description": "Angular Bindings for Hypernova",
55
"main": "lib/index.js",
66
"author": "Felipe Guizar Diaz <felipegaiacharly@gmail.com>",

src/hypernova.module.factory.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { NgModuleFactory, ReflectiveInjector } from '@angular/core';
2+
3+
export const HYPERNOVA_DATA = 'Hypernova.Data';
4+
5+
export class HypernovaModuleFactory extends NgModuleFactory {
6+
constructor(moduleFactory, name, placeholder) {
7+
super();
8+
this.name = name;
9+
this.placeholder = placeholder;
10+
this.moduleFactory = moduleFactory;
11+
this.moduleType = moduleFactory.moduleType;
12+
13+
this.create = (parentInjector) => {
14+
const newInjector = ReflectiveInjector.resolveAndCreate([
15+
{
16+
provide: 'Hypernova.Data',
17+
useValue: this.placeholder.data,
18+
},
19+
{
20+
provide: 'Hypernova.Name',
21+
useValue: this.name,
22+
},
23+
{
24+
provide: 'Hypernova.Node',
25+
useValue: this.placeholder.node,
26+
},
27+
], parentInjector);
28+
29+
return this.moduleFactory.create(newInjector);
30+
};
31+
}
32+
}

src/index.js

Lines changed: 2 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,9 @@
1-
import hypernova, { load } from 'hypernova';
2-
import { BrowserModule } from '@angular/platform-browser';
3-
4-
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
5-
6-
import { NgModule } from '@angular/core';
7-
81
import { findNode, getData } from 'nova-helpers';
92

103
export { load } from 'hypernova';
114

5+
export { HypernovaModuleFactory, HYPERNOVA_DATA } from './hypernova.module.factory';
6+
127
export const loadById = (name, id) => {
138
const node = findNode(name, id);
149
const data = getData(name, id);
@@ -22,52 +17,3 @@ export const loadById = (name, id) => {
2217

2318
return null;
2419
};
25-
26-
const APP_ID = 'hypernova';
27-
28-
export const HYPERNOVA_DATA = 'Hypernova.Data';
29-
30-
const getBrowserAppModule = (Component, Module, node, propsData) => {
31-
function AppModule() {
32-
this.ngDoBootstrap = (app) => {
33-
app.bootstrap(Component, node);
34-
};
35-
}
36-
return NgModule({
37-
imports: [
38-
Module,
39-
BrowserModule.withServerTransition({ appId: APP_ID }),
40-
],
41-
entryComponents: [Component],
42-
providers: [
43-
{
44-
provide: HYPERNOVA_DATA,
45-
useValue: propsData,
46-
},
47-
],
48-
})(AppModule);
49-
};
50-
51-
export const mountComponent = (Component, Module, node, propsData) => {
52-
const BrowserAppModule = getBrowserAppModule(Component, Module, node, propsData);
53-
54-
platformBrowserDynamic().bootstrapModule(BrowserAppModule);
55-
};
56-
57-
export const renderAngular = (name, Component, Module) => hypernova({
58-
server() {
59-
throw new Error('Use hypernova-angular/server instead');
60-
},
61-
62-
client() {
63-
const payloads = load(name);
64-
if (payloads) {
65-
payloads.forEach((payload) => {
66-
const { node, data: propsData } = payload;
67-
68-
mountComponent(Component, Module, node, propsData);
69-
});
70-
}
71-
return Component;
72-
},
73-
});

0 commit comments

Comments
 (0)