Skip to content

Commit

Permalink
feat(astro-angular): add support for render and client component prov…
Browse files Browse the repository at this point in the history
…iders (#376)
  • Loading branch information
brandonroberts authored May 7, 2023
1 parent 3af5ab3 commit bbfcb40
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 14 deletions.
8 changes: 8 additions & 0 deletions apps/astro-app/src/components/card.component.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import {
ChangeDetectionStrategy,
Component,
inject,
Input,
ViewEncapsulation,
} from '@angular/core';
import { provideAnimations } from '@angular/platform-browser/animations';
import { HttpClient, provideHttpClient } from '@angular/common/http';

@Component({
selector: 'astro-card',
Expand Down Expand Up @@ -90,4 +93,9 @@ export class CardComponent {
@Input() href = '';
@Input() title = '';
@Input() body = '';

static renderProviders = [provideHttpClient()];
static clientProviders = [CardComponent.renderProviders, provideAnimations()];

private _http = inject(HttpClient);
}
36 changes: 36 additions & 0 deletions apps/astro-app/src/components/todos.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Component, OnInit, inject } from '@angular/core';
import { NgFor } from '@angular/common';
import { provideHttpClient, HttpClient } from '@angular/common/http';

interface Todo {
id: number;
title: string;
completed: boolean;
}

@Component({
selector: 'app-todos',
standalone: true,
imports: [NgFor],
template: `
<h2>Todos</h2>
<ul>
<li *ngFor="let todo of todos">
{{ todo.title }}
</li>
</ul>
`,
})
export class TodosComponent implements OnInit {
static clientProviders = [provideHttpClient()];

http = inject(HttpClient);
todos: Todo[] = [];

ngOnInit() {
this.http
.get<Todo[]>('https://jsonplaceholder.typicode.com/todos')
.subscribe((todos) => (this.todos = todos));
}
}
3 changes: 3 additions & 0 deletions apps/astro-app/src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Layout from '../layouts/Layout.astro';
import Card from '../components/Card.astro';
import { CardComponent } from '../components/card.component.ts';
import { Instructions } from '../components/Instructions';
import { TodosComponent } from '../components/todos.component.ts';
const serverSideTitle = 'Angular (server side binding)';
---
Expand Down Expand Up @@ -45,6 +46,8 @@ const serverSideTitle = 'Angular (server side binding)';
/>
</ul>
</main>

<TodosComponent />
</Layout>

<style>
Expand Down
64 changes: 55 additions & 9 deletions packages/astro-angular/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export default defineConfig({
vite: {
tsconfig: 'path/to/tsconfig.app.json',
workspaceRoot: 'rootDir',
inlineStylesExtension: 'scss|sass|less'
inlineStylesExtension: 'scss|sass|less',
},
}),
],
Expand Down Expand Up @@ -174,6 +174,52 @@ import { HelloComponent } from '../components/hello.component';

Find more information about [Client Directives](https://docs.astro.build/en/reference/directives-reference/#client-directives) in the Astro documentation.

## Adding Component Providers

Additional providers can be added to a component for static rendering and client hydration.

These are `renderProviders` and `clientProviders` respectively. These providers are defined as static arrays on the Component class, and are registered when the component is rendered, and hydrated on the client.

```ts
import { Component, OnInit, inject } from '@angular/core';
import { NgFor } from '@angular/common';
import { provideHttpClient, HttpClient } from '@angular/common/http';

interface Todo {
id: number;
title: string;
completed: boolean;
}

@Component({
selector: 'app-todos',
standalone: true,
imports: [NgFor],
template: `
<h2>Todos</h2>
<ul>
<li *ngFor="let todo of todos">
{{ todo.title }}
</li>
</ul>
`,
})
export class TodosComponent implements OnInit {
static clientProviders = [provideHttpClient()];
static renderProviders = [];

http = inject(HttpClient);
todos: Todo[] = [];

ngOnInit() {
this.http
.get<Todo[]>('https://jsonplaceholder.typicode.com/todos')
.subscribe((todos) => (this.todos = todos));
}
}
```

## Using Components in MDX pages

To use components with MDX pages, you must install and configure MDX support by following the Astro integration of [@astrojs/mdx](https://docs.astro.build/en/guides/integrations-guide/mdx/). Your `astro.config.mjs` should now include the `@astrojs/mdx` integration.
Expand All @@ -192,10 +238,10 @@ Create an `.mdx` file inside the `src/pages` directory and add the Angular compo

```md
---
layout: "../../layouts/BlogPost.astro"
title: "Using Angular in MDX"
description: "Lorem ipsum dolor sit amet"
pubDate: "Sep 22 2022"
layout: '../../layouts/BlogPost.astro'
title: 'Using Angular in MDX'
description: 'Lorem ipsum dolor sit amet'
pubDate: 'Sep 22 2022'
---

import { HelloComponent } from "../../components/hello.component.ts";
Expand All @@ -208,10 +254,10 @@ To hydrate the component on the client, use one of the Astro [client directives]

```md
---
layout: "../../layouts/BlogPost.astro"
title: "Using Angular in MDX"
description: "Lorem ipsum dolor sit amet"
pubDate: "Sep 22 2022"
layout: '../../layouts/BlogPost.astro'
title: 'Using Angular in MDX'
description: 'Lorem ipsum dolor sit amet'
pubDate: 'Sep 22 2022'
---

import { HelloComponent } from "../../components/hello.component.ts";
Expand Down
10 changes: 8 additions & 2 deletions packages/astro-angular/src/client.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'zone.js/dist/zone.js';
import {
EnvironmentProviders,
Provider,
reflectComponentType,
ɵComponentType as ComponentType,
} from '@angular/core';
Expand All @@ -8,11 +10,15 @@ import { createApplication } from '@angular/platform-browser';

export default (element: HTMLElement) => {
return (
Component: ComponentType<unknown>,
Component: ComponentType<unknown> & {
clientProviders?: (Provider | EnvironmentProviders)[];
},
props?: Record<string, unknown>,
_childHTML?: unknown
) => {
createApplication().then((appRef: ApplicationRef) => {
createApplication({
providers: [...(Component.clientProviders || [])],
}).then((appRef: ApplicationRef) => {
const zone = appRef.injector.get(NgZone);
zone.run(() => {
const componentRef = createComponent(Component, {
Expand Down
24 changes: 23 additions & 1 deletion packages/astro-angular/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,29 @@ function getViteConfiguration(vite?: PluginOptions) {
* this workaround for now.
*
*/
plugins: [(viteAngular as any).default(vite)],
plugins: [
(viteAngular as any).default(vite),
{
name: '@analogjs/astro-angular-platform-server',
transform(code: string, id: string) {
if (id.includes('platform-server')) {
code = code.replace(/global\./g, 'globalThis.');

return {
code: code.replace(
'new xhr2.XMLHttpRequest',
'new (xhr2.default.XMLHttpRequest || xhr2.default)'
),
};
}

return;
},
},
],
ssr: {
noExternal: ['@angular/**', '@analogjs/**'],
},
};
}

Expand Down
8 changes: 7 additions & 1 deletion packages/astro-angular/src/server.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'zone.js/bundles/zone-node.umd.js';
import type {
ComponentMirror,
EnvironmentProviders,
Provider,
ɵComponentType as ComponentType,
} from '@angular/core';
Expand All @@ -13,6 +14,7 @@ import {
BEFORE_APP_SERIALIZED,
provideServerRendering,
renderApplication,
ɵSERVER_CONTEXT,
} from '@angular/platform-server';
import { bootstrapApplication } from '@angular/platform-browser';

Expand Down Expand Up @@ -72,7 +74,9 @@ const STATIC_PROPS_HOOK_PROVIDER: Provider = {
};

async function renderToStaticMarkup(
Component: ComponentType<unknown>,
Component: ComponentType<unknown> & {
renderProviders: (Provider | EnvironmentProviders)[];
},
props: Record<string, unknown>,
_children: unknown
) {
Expand All @@ -88,6 +92,8 @@ async function renderToStaticMarkup(
},
STATIC_PROPS_HOOK_PROVIDER,
provideServerRendering(),
{ provide: ɵSERVER_CONTEXT, useValue: 'analog' },
...(Component.renderProviders || []),
],
});

Expand Down
2 changes: 1 addition & 1 deletion packages/astro-angular/tsconfig.lib.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"declaration": true,
"declaration": false,
"inlineSourceMap": true,
"sourceMap": false,
"types": []
Expand Down

0 comments on commit bbfcb40

Please sign in to comment.