Skip to content

Instantly share code, notes, and snippets.

@reosablo
Last active March 25, 2024 08:42
Show Gist options
  • Save reosablo/bbe57cbe1389f8c7525daf8a681bab0f to your computer and use it in GitHub Desktop.
Save reosablo/bbe57cbe1389f8c7525daf8a681bab0f to your computer and use it in GitHub Desktop.
Build-free Angular 17 app with `@babel/standalone`
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Build-free Angular 17 app with @babel/standalone</title>
<script type="importmap">
{
"imports": {
"@angular/common": "https://esm.sh/v135/@angular/common@17.0.3?dev",
"@angular/common/": "https://esm.sh/v135/@angular/common@17.0.3&dev/",
"@angular/compiler": "https://esm.sh/v135/@angular/compiler@17.0.3?dev",
"@angular/core": "https://esm.sh/v135/@angular/core@17.0.3?dev",
"@angular/core/": "https://esm.sh/v135/@angular/core@17.0.3&dev/",
"@angular/platform-browser": "https://esm.sh/v135/@angular/platform-browser@17.0.3?dev",
"zone.js": "https://esm.sh/v135/zone.js@0.14.2?dev"
}
}
</script>
<script type="module">
import {
availablePlugins,
availablePresets,
registerPreset,
} from "https://esm.sh/@babel/standalone@7.23.6";
registerPreset("angular", {
presets: [[availablePresets["typescript"]]],
plugins: [
[availablePlugins["proposal-decorators"], { version: "legacy" }],
[availablePlugins["transform-class-properties"]],
],
});
</script>
<!-- `data-plugins=""` attribute is required for @babel/standalone@7.23.6 -->
<script type="text/babel" data-presets="angular" data-plugins="" data-type="module" src="./main.ts"></script>
</head>
<body>
<app-root></app-root>
</body>
</html>
/**
* @file Build-free Angular 17 app with `@babel/standalone`
* @license CC0-1.0
*/
import "@angular/compiler";
import "zone.js";
import { TitleCasePipe } from "@angular/common";
import { HttpClient, provideHttpClient } from "@angular/common/http";
import { Component, effect, inject, Injectable, signal } from "@angular/core";
import { toSignal } from "@angular/core/rxjs-interop";
import { bootstrapApplication } from "@angular/platform-browser";
@Injectable({ providedIn: "root" })
class AppService {
static readonly #urlBase = "https://jsonplaceholder.typicode.com";
readonly #httpClient = inject(HttpClient);
getUsers() {
return this.#httpClient.get(`${AppService.#urlBase}/users`);
}
getPosts(userId: any) {
return this.#httpClient.get(`${AppService.#urlBase}/posts`, {
params: { userId },
});
}
}
@Component({
standalone: true,
selector: "app-root",
imports: [TitleCasePipe],
template: `
<h1>Show Posts</h1>
<label>
Select User:
<select (change)="selectedUserId.set($event.target.value)">
<option hidden selected></option>
@for (user of users(); track user.id) {
<option value="{{user.id}}">
&#64;{{user.username}}: {{user.name}}
</option>
}
</select>
</label>
<ul>
@for (post of posts(); track post.id) {
<li>{{post.title | titlecase}}</li>
}
</ul>
<p>
Powered by
<a href="https://jsonplaceholder.typicode.com/" target="_blank">
JSONPlaceholder
</a>
</p>
`,
})
class AppComponent {
readonly #appService = inject(AppService);
protected readonly users = toSignal(this.#appService.getUsers());
protected readonly selectedUserId = signal<any>(undefined);
protected readonly posts = signal<any>(undefined);
constructor() {
effect((onCleanup) => {
const selectedUserId = this.selectedUserId();
if (selectedUserId !== undefined) {
const subscription = this.#appService.getPosts(selectedUserId)
.subscribe((posts) => this.posts.set(posts));
onCleanup(() => subscription.unsubscribe());
}
});
}
}
await bootstrapApplication(AppComponent, {
providers: [provideHttpClient()],
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment