Skip to content

Instantly share code, notes, and snippets.

@antischematic
Last active February 21, 2022 15:12
Show Gist options
  • Save antischematic/04a3150f719954b73e34923206932a28 to your computer and use it in GitHub Desktop.
Save antischematic/04a3150f719954b73e34923206932a28 to your computer and use it in GitHub Desktop.
Angular Single File Module
// noinspection BadExpressionStatementJS
import {
use,
inject,
listen,
subscribe, provide, ValueToken, ViewDef,
} from "@mmuscat/angular-composition-api"
import { EMPTY } from "rxjs"
import { Component, ViewChild } from "@angular/core"
import MyComponent from "./counter.component"
export const Child = new ValueToken("Child")
export const API = new ValueToken<any>("API", {
factory() {
return {
save() {},
}
},
})
@Component({
selector: "my-counter",
standalone: true,
providers: [
{
provide: Theme,
useValue: "default"
},
{
provide: Child,
useExisting: MyCounter
}
],
inputs: ["count"],
outputs: ["countChange"],
host: ({
class: "count",
}),
queries: ({
viewChild: new ViewChild("viewChild"),
}),
styles: [`
.button {
background-color: blue
}`
],
template: `
<div>{{ count }}</div>
<button class="button" (click)="increment()">
Increment
</button>
`
})
export default class MyCounter extends ViewDef(setup) {
static ngTemplateContextGuard(dir: MyCounter) {
// etc
}
}
function setup() {
const api = inject(API)
const count = use(0)
const countChange = listen(count)
const result = use(EMPTY)
function increment() {
countChange(count() + 1)
}
function save() {
api.save(count()).subscribe(result)
}
subscribe(count, console.log)
provide(Child, {
count
})
return {
count,
countChange,
result,
increment,
save
}
}
import { TestBed } from "@angular/core/testing"
import MyCounter, { Child, API } from "./counter.component"
describe("MyCounter", {
it("should create", () => {
TestBed.configureTestingModule({
imports: [MyCounter],
providers: [{
provide: API
useValue: {
save: createSpy()
}
}]
}).compileComponents()
expect(TestBed.createComponent(MyCounter)).toBeTruthy()
})
})
import {
use,
inject,
listen,
subscribe,
ValueToken,
} from "@mmuscat/angular-composition-api"
import { EMPTY } from "rxjs"
import { Component, ViewChild } from "@angular/core"
// self-reference generated class via default import
import MyCounter from "./counter.component"
// top level exports can be imported from other files
export const Child = new ValueToken("Child")
export const API = new ValueToken<any>("API", {
factory() {
return {
save() {},
}
},
})
// special handling for known static properties
export function ngTemplateContextGuard(dir: MyCounter) {
// etc
}
// compile label marks the beginning of the component
// For directives use
// compile: Directive
// For services use
// compile: Injectable
// For pipes use
// compile: Pipe
// metadata is provided using labels
compile: Component
selector: "my-counter"
standalone: true
providers: [
{
provide: Theme,
useValue: "default"
},
{
provide: Child,
// self-reference in provider
useExisting: MyCounter
}
]
inputs: ["count"]
outputs: ["countChange"]
host: ({
class: "count",
})
queries: ({
viewChild: new ViewChild("viewChild"),
})
// now in component instance context
const api = inject(API)
// exported vars are exposed to the template and public class instance
export const count = use(0)
export const countChange = listen(count)
export const result = use(EMPTY)
export function increment() {
countChange(count() + 1)
}
export function save() {
api.save(count()).subscribe(result)
}
subscribe(count, console.log)
// metadata labels can be placed in anywhere after `compile` label for convenience
style: `
.button {
background-color: blue
}
`
template: `
<div>{{ count }}</div>
<button class="button" (click)="increment()">
Increment
</button>
`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment