Last active
February 21, 2022 15:12
-
-
Save antischematic/04a3150f719954b73e34923206932a28 to your computer and use it in GitHub Desktop.
Angular Single File Module
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | |
}) | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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