Skip to content

Commit d88bde9

Browse files
crutchcornrlmestrealxhub
authored
feat: Angular adapter (#627)
* chore: initial scaffolding for Angular adapter * chore: add test component tests * chore: add example api * chore: add example * chore: finish setting up example * chore: move items to correct location * chore: got initial version working * chore: WIP on typings * chore: more work on adapter * chore: fix issues with mounting * chore: WIP fixing CI * chore: fix various packaging issues * chore: regenerate lockfile * test: add tests for Angular adapter default values * test: add tests for validating * fix: add types to implicit context value * chore: fix CI * fix: issues with constant names in Angular Adapter Co-authored-by: Rafael Mestre <[email protected]> * fix: typing issues with Form being inferred incorrectly * feat!: migrate away from ng-template and towards ng-container Co-authored-by: Alex Rickabaugh <[email protected]> * chore: fix CI * chore: fix minor issues with versioning * chore: improve angular example * chore: add store to angular form usage * chore: add more tests from React adapter * chore: add Zod example * chore: add Angular Yup example * chore: add valibot to angular examples * chore: add angular array example * chore: add config to publish * docs: add QuickStart guide * docs: add array docs for angular adapter * docs: add initial install instructions * chore: fix CI * chore: set minimum version of Angular to 17.3.0 --------- Co-authored-by: Rafael Mestre <[email protected]> Co-authored-by: Alex Rickabaugh <[email protected]>
1 parent 3e1530b commit d88bde9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+8359
-532
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
![TanStack Form Header](https://github.com/TanStack/form/raw/main/media/repo-header.png)
44

5-
Powerful and type-safe form state management for the web. TS/JS, React Form, Solid Form, Lit Form and Vue Form.
5+
Powerful and type-safe form state management for the web. TS/JS, React Form, Solid Form, Angular Form, Lit Form and Vue Form.
66

77
<a href="https://twitter.com/intent/tweet?button_hashtag=TanStack" target="\_parent">
88
<img alt="#TanStack" src="https://img.shields.io/twitter/url?color=%2308a0e9&label=%23TanStack&style=social&url=https%3A%2F%2Ftwitter.com%2Fintent%2Ftweet%3Fbutton_hashtag%3DTanStack">

docs/config.json

+43
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@
4545
}
4646
]
4747
},
48+
{
49+
"label": "angular",
50+
"children": [
51+
{
52+
"label": "Quick Start",
53+
"to": "framework/angular/quick-start"
54+
}
55+
]
56+
},
4857
{
4958
"label": "solid",
5059
"children": [
@@ -112,6 +121,15 @@
112121
}
113122
]
114123
},
124+
{
125+
"label": "angular",
126+
"children": [
127+
{
128+
"label": "Arrays",
129+
"to": "framework/angular/guides/arrays"
130+
}
131+
]
132+
},
115133
{
116134
"label": "solid",
117135
"children": [
@@ -263,6 +281,31 @@
263281
}
264282
]
265283
},
284+
{
285+
"label": "angular",
286+
"children": [
287+
{
288+
"label": "Simple",
289+
"to": "framework/angular/examples/simple"
290+
},
291+
{
292+
"label": "Arrays",
293+
"to": "framework/angular/examples/array"
294+
},
295+
{
296+
"label": "Yup",
297+
"to": "framework/angular/examples/yup"
298+
},
299+
{
300+
"label": "Zod",
301+
"to": "framework/angular/examples/zod"
302+
},
303+
{
304+
"label": "Valibot",
305+
"to": "framework/angular/examples/valibot"
306+
}
307+
]
308+
},
266309
{
267310
"label": "solid",
268311
"children": [
+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
---
2+
id: arrays
3+
title: Arrays
4+
---
5+
6+
TanStack Form supports arrays as values in a form, including sub-object values inside of an array.
7+
8+
# Basic Usage
9+
10+
To use an array, you can use `field.api.state.value` on an array value:
11+
12+
```typescript
13+
@Component({
14+
selector: 'app-root',
15+
standalone: true,
16+
imports: [TanStackField],
17+
template: `
18+
<ng-container [tanstackField]="form" name="people" #people="field">
19+
<div>
20+
@for (_ of people.api.state.value; track $index) {
21+
<!-- ... -->
22+
}
23+
</div>
24+
</ng-container>
25+
`,
26+
})
27+
export class AppComponent {
28+
form = injectForm({
29+
defaultValues: {
30+
people: [] as Array<{ name: string; age: number }>,
31+
},
32+
onSubmit({ value }) {
33+
alert(JSON.stringify(value))
34+
},
35+
})
36+
}
37+
```
38+
39+
This will generate the mapped JSX every time you run `pushValue` on `field`:
40+
41+
```html
42+
<button (click)="people.api.pushValue(defaultPerson)" type="button">
43+
Add person
44+
</button>
45+
```
46+
47+
Finally, you can use a subfield like so:
48+
49+
```html
50+
<ng-container
51+
[tanstackField]="form"
52+
[name]="'people[' + $index + '].name'"
53+
#person="field"
54+
>
55+
<div>
56+
<label>
57+
<div>Name for person {{ $index }}</div>
58+
<input
59+
[value]="person.api.state.value"
60+
(input)="
61+
person.api.handleChange($any($event).target.value)
62+
"
63+
/>
64+
</label>
65+
</div>
66+
</ng-container>
67+
```
68+
69+
## Full Example
70+
71+
```typescript
72+
@Component({
73+
selector: 'app-root',
74+
standalone: true,
75+
imports: [TanStackField],
76+
template: `
77+
<form (submit)="handleSubmit($event)">
78+
<div>
79+
<ng-container [tanstackField]="form" name="people" #people="field">
80+
<div>
81+
@for (_ of people.api.state.value; track $index) {
82+
<ng-container
83+
[tanstackField]="form"
84+
[name]="'people[' + $index + '].name'"
85+
#person="field"
86+
>
87+
<div>
88+
<label>
89+
<div>Name for person {{ $index }}</div>
90+
<input
91+
[value]="person.api.state.value"
92+
(input)="
93+
person.api.handleChange($any($event).target.value)
94+
"
95+
/>
96+
</label>
97+
</div>
98+
</ng-container>
99+
}
100+
</div>
101+
<button (click)="people.api.pushValue(defaultPerson)" type="button">
102+
Add person
103+
</button>
104+
</ng-container>
105+
</div>
106+
<button type="submit" [disabled]="!canSubmit()">
107+
{{ isSubmitting() ? '...' : 'Submit' }}
108+
</button>
109+
</form>
110+
`,
111+
})
112+
export class AppComponent {
113+
defaultPerson = { name: '', age: 0 }
114+
115+
form = injectForm({
116+
defaultValues: {
117+
people: [] as Array<{ name: string; age: number }>,
118+
},
119+
onSubmit({ value }) {
120+
alert(JSON.stringify(value))
121+
},
122+
})
123+
124+
canSubmit = injectStore(this.form, (state) => state.canSubmit)
125+
isSubmitting = injectStore(this.form, (state) => state.isSubmitting)
126+
127+
handleSubmit(event: SubmitEvent) {
128+
event.preventDefault()
129+
event.stopPropagation()
130+
void this.form.handleSubmit()
131+
}
132+
}
133+
```

docs/framework/angular/quick-start.md

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
---
2+
id: quick-start
3+
title: Quick Start
4+
---
5+
6+
The bare minimum to get started with TanStack Form is to create a form and add a field. Keep in mind that this example does not include any validation or error handling... yet.
7+
8+
```typescript
9+
import { Component } from '@angular/core'
10+
import { bootstrapApplication } from '@angular/platform-browser'
11+
import { TanStackField, injectForm } from '@tanstack/angular-form'
12+
13+
@Component({
14+
selector: 'app-root',
15+
standalone: true,
16+
imports: [TanStackField],
17+
template: `
18+
<form (submit)="handleSubmit($event)">
19+
<div>
20+
<ng-container
21+
[tanstackField]="form"
22+
name="fullName"
23+
#fullName="field"
24+
>
25+
<label [for]="fullName.api.name">First Name:</label>
26+
<input
27+
[name]="fullName.api.name"
28+
[value]="fullName.api.state.value"
29+
(blur)="fullName.api.handleBlur()"
30+
(input)="fullName.api.handleChange($any($event).target.value)"
31+
/>
32+
</ng-container>
33+
</div>
34+
<button type="submit">Submit</button>
35+
</form>
36+
`,
37+
})
38+
export class AppComponent {
39+
form = injectForm({
40+
defaultValues: {
41+
fullName: '',
42+
},
43+
onSubmit({ value }) {
44+
// Do something with form data
45+
console.log(value)
46+
},
47+
})
48+
49+
handleSubmit(event: SubmitEvent) {
50+
event.preventDefault()
51+
event.stopPropagation()
52+
void this.form.handleSubmit()
53+
}
54+
}
55+
56+
bootstrapApplication(AppComponent).catch((err) => console.error(err))
57+
```
58+
59+
From here, you'll be ready to explore all of the other features of TanStack Form!

docs/installation.md

+4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ $ npm i @tanstack/react-form
1010
# or
1111
$ pnpm add @tanstack/vue-form
1212
# or
13+
$ yarn add @tanstack/angular-form
14+
# or
1315
$ yarn add @tanstack/solid-form
16+
# or
17+
$ pnpm add @tanstack/lit-form
1418
```
1519

1620
> Depending on your environment, you might need to add polyfills. If you want to support older browsers, you need to transpile the library from `node_modules` yourselves.

examples/angular/array/.editorconfig

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Editor configuration, see https://editorconfig.org
2+
root = true
3+
4+
[*]
5+
charset = utf-8
6+
indent_style = space
7+
indent_size = 2
8+
insert_final_newline = true
9+
trim_trailing_whitespace = true
10+
11+
[*.ts]
12+
quote_type = single
13+
14+
[*.md]
15+
max_line_length = off
16+
trim_trailing_whitespace = false

examples/angular/array/.gitignore

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# See http://help.github.com/ignore-files/ for more about ignoring files.
2+
3+
# Compiled output
4+
/dist
5+
/tmp
6+
/out-tsc
7+
/bazel-out
8+
9+
# Node
10+
/node_modules
11+
npm-debug.log
12+
yarn-error.log
13+
14+
# IDEs and editors
15+
.idea/
16+
.project
17+
.classpath
18+
.c9/
19+
*.launch
20+
.settings/
21+
*.sublime-workspace
22+
23+
# Visual Studio Code
24+
.vscode/*
25+
!.vscode/settings.json
26+
!.vscode/tasks.json
27+
!.vscode/launch.json
28+
!.vscode/extensions.json
29+
.history/*
30+
31+
# Miscellaneous
32+
/.angular/cache
33+
.sass-cache/
34+
/connect.lock
35+
/coverage
36+
/libpeerconnection.log
37+
testem.log
38+
/typings
39+
40+
# System files
41+
.DS_Store
42+
Thumbs.db

examples/angular/array/README.md

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Simple
2+
3+
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.0.1.
4+
5+
## Development server
6+
7+
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.
8+
9+
## Code scaffolding
10+
11+
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
12+
13+
## Build
14+
15+
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
16+
17+
## Running unit tests
18+
19+
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
20+
21+
## Running end-to-end tests
22+
23+
Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
24+
25+
## Further help
26+
27+
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.

0 commit comments

Comments
 (0)