Skip to content

Instantly share code, notes, and snippets.

@reosablo
Last active December 14, 2024 05:21
Show Gist options
  • Select an option

  • Save reosablo/85282bc54d4d8affde259b28f54d3df9 to your computer and use it in GitHub Desktop.

Select an option

Save reosablo/85282bc54d4d8affde259b28f54d3df9 to your computer and use it in GitHub Desktop.
Buildless Angular 17 app
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Buildless Angular 17 app</title>
<script type="importmap">
{
"imports": {
"@angular/common": "https://esm.sh/v135/@angular/[email protected]?dev",
"@angular/common/": "https://esm.sh/v135/@angular/[email protected]&dev/",
"@angular/compiler": "https://esm.sh/v135/@angular/[email protected]?dev",
"@angular/core": "https://esm.sh/v135/@angular/[email protected]?dev",
"@angular/core/": "https://esm.sh/v135/@angular/[email protected]&dev/",
"@angular/platform-browser": "https://esm.sh/v135/@angular/[email protected]?dev",
"zone.js": "https://esm.sh/v135/[email protected]?dev"
}
}
</script>
<script type="module" src="./main.js"></script>
</head>
<body>
<app-root></app-root>
</body>
</html>
// @ts-check
/**
* @file Buildless Angular 17 app
* @license CC0-1.0
*/
import "@angular/compiler";
import "zone.js";
import { TitleCasePipe } from "@angular/common";
import { HttpClient, provideHttpClient } from "@angular/common/http";
import {
Component,
Injectable,
effect,
inject,
signal,
} from "@angular/core";
import { toSignal } from "@angular/core/rxjs-interop";
import { bootstrapApplication } from "@angular/platform-browser";
class AppService {
static {
Injectable({ providedIn: "root" })(this);
}
/** @readonly */
static #urlBase = "https://jsonplaceholder.typicode.com";
/** @readonly */
#httpClient = inject(HttpClient);
getUsers() {
return this.#httpClient.get(`${AppService.#urlBase}/users`);
}
/** @param {*} userId */
getPosts(userId) {
return this.#httpClient.get(`${AppService.#urlBase}/posts`, {
params: { userId },
});
}
}
class AppComponent {
static {
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>
`,
})(this);
}
/** @readonly */
#appService = inject(AppService);
/** @protected @readonly */
users = toSignal(this.#appService.getUsers());
/** @protected @readonly */
selectedUserId = signal(/** @type {*} */(undefined));
/** @protected @readonly */
posts = signal(/** @type {*} */(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