Skip to content

Commit

Permalink
feat: Angular adapter (TanStack#627)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
3 people authored Mar 23, 2024
1 parent 3e1530b commit d88bde9
Show file tree
Hide file tree
Showing 82 changed files with 8,359 additions and 532 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

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

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

<a href="https://twitter.com/intent/tweet?button_hashtag=TanStack" target="\_parent">
<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">
Expand Down
43 changes: 43 additions & 0 deletions docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@
}
]
},
{
"label": "angular",
"children": [
{
"label": "Quick Start",
"to": "framework/angular/quick-start"
}
]
},
{
"label": "solid",
"children": [
Expand Down Expand Up @@ -112,6 +121,15 @@
}
]
},
{
"label": "angular",
"children": [
{
"label": "Arrays",
"to": "framework/angular/guides/arrays"
}
]
},
{
"label": "solid",
"children": [
Expand Down Expand Up @@ -263,6 +281,31 @@
}
]
},
{
"label": "angular",
"children": [
{
"label": "Simple",
"to": "framework/angular/examples/simple"
},
{
"label": "Arrays",
"to": "framework/angular/examples/array"
},
{
"label": "Yup",
"to": "framework/angular/examples/yup"
},
{
"label": "Zod",
"to": "framework/angular/examples/zod"
},
{
"label": "Valibot",
"to": "framework/angular/examples/valibot"
}
]
},
{
"label": "solid",
"children": [
Expand Down
133 changes: 133 additions & 0 deletions docs/framework/angular/guides/arrays.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
---
id: arrays
title: Arrays
---

TanStack Form supports arrays as values in a form, including sub-object values inside of an array.

# Basic Usage

To use an array, you can use `field.api.state.value` on an array value:

```typescript
@Component({
selector: 'app-root',
standalone: true,
imports: [TanStackField],
template: `
<ng-container [tanstackField]="form" name="people" #people="field">
<div>
@for (_ of people.api.state.value; track $index) {
<!-- ... -->
}
</div>
</ng-container>
`,
})
export class AppComponent {
form = injectForm({
defaultValues: {
people: [] as Array<{ name: string; age: number }>,
},
onSubmit({ value }) {
alert(JSON.stringify(value))
},
})
}
```

This will generate the mapped JSX every time you run `pushValue` on `field`:

```html
<button (click)="people.api.pushValue(defaultPerson)" type="button">
Add person
</button>
```

Finally, you can use a subfield like so:

```html
<ng-container
[tanstackField]="form"
[name]="'people[' + $index + '].name'"
#person="field"
>
<div>
<label>
<div>Name for person {{ $index }}</div>
<input
[value]="person.api.state.value"
(input)="
person.api.handleChange($any($event).target.value)
"
/>
</label>
</div>
</ng-container>
```

## Full Example

```typescript
@Component({
selector: 'app-root',
standalone: true,
imports: [TanStackField],
template: `
<form (submit)="handleSubmit($event)">
<div>
<ng-container [tanstackField]="form" name="people" #people="field">
<div>
@for (_ of people.api.state.value; track $index) {
<ng-container
[tanstackField]="form"
[name]="'people[' + $index + '].name'"
#person="field"
>
<div>
<label>
<div>Name for person {{ $index }}</div>
<input
[value]="person.api.state.value"
(input)="
person.api.handleChange($any($event).target.value)
"
/>
</label>
</div>
</ng-container>
}
</div>
<button (click)="people.api.pushValue(defaultPerson)" type="button">
Add person
</button>
</ng-container>
</div>
<button type="submit" [disabled]="!canSubmit()">
{{ isSubmitting() ? '...' : 'Submit' }}
</button>
</form>
`,
})
export class AppComponent {
defaultPerson = { name: '', age: 0 }

form = injectForm({
defaultValues: {
people: [] as Array<{ name: string; age: number }>,
},
onSubmit({ value }) {
alert(JSON.stringify(value))
},
})

canSubmit = injectStore(this.form, (state) => state.canSubmit)
isSubmitting = injectStore(this.form, (state) => state.isSubmitting)

handleSubmit(event: SubmitEvent) {
event.preventDefault()
event.stopPropagation()
void this.form.handleSubmit()
}
}
```
59 changes: 59 additions & 0 deletions docs/framework/angular/quick-start.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
id: quick-start
title: Quick Start
---

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.

```typescript
import { Component } from '@angular/core'
import { bootstrapApplication } from '@angular/platform-browser'
import { TanStackField, injectForm } from '@tanstack/angular-form'

@Component({
selector: 'app-root',
standalone: true,
imports: [TanStackField],
template: `
<form (submit)="handleSubmit($event)">
<div>
<ng-container
[tanstackField]="form"
name="fullName"
#fullName="field"
>
<label [for]="fullName.api.name">First Name:</label>
<input
[name]="fullName.api.name"
[value]="fullName.api.state.value"
(blur)="fullName.api.handleBlur()"
(input)="fullName.api.handleChange($any($event).target.value)"
/>
</ng-container>
</div>
<button type="submit">Submit</button>
</form>
`,
})
export class AppComponent {
form = injectForm({
defaultValues: {
fullName: '',
},
onSubmit({ value }) {
// Do something with form data
console.log(value)
},
})

handleSubmit(event: SubmitEvent) {
event.preventDefault()
event.stopPropagation()
void this.form.handleSubmit()
}
}

bootstrapApplication(AppComponent).catch((err) => console.error(err))
```

From here, you'll be ready to explore all of the other features of TanStack Form!
4 changes: 4 additions & 0 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ $ npm i @tanstack/react-form
# or
$ pnpm add @tanstack/vue-form
# or
$ yarn add @tanstack/angular-form
# or
$ yarn add @tanstack/solid-form
# or
$ pnpm add @tanstack/lit-form
```

> 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.
Expand Down
16 changes: 16 additions & 0 deletions examples/angular/array/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Editor configuration, see https://editorconfig.org
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

[*.ts]
quote_type = single

[*.md]
max_line_length = off
trim_trailing_whitespace = false
42 changes: 42 additions & 0 deletions examples/angular/array/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.

# Compiled output
/dist
/tmp
/out-tsc
/bazel-out

# Node
/node_modules
npm-debug.log
yarn-error.log

# IDEs and editors
.idea/
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace

# Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*

# Miscellaneous
/.angular/cache
.sass-cache/
/connect.lock
/coverage
/libpeerconnection.log
testem.log
/typings

# System files
.DS_Store
Thumbs.db
27 changes: 27 additions & 0 deletions examples/angular/array/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Simple

This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.0.1.

## Development server

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.

## Code scaffolding

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`.

## Build

Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.

## Running unit tests

Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).

## Running end-to-end tests

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.

## Further help

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.
Loading

0 comments on commit d88bde9

Please sign in to comment.