Skip to content

Commit

Permalink
Merge pull request #2 from withastro-utils/feat/view-state
Browse files Browse the repository at this point in the history
feat: form@3.3
  • Loading branch information
ido-pluto authored Dec 30, 2023
2 parents 263e625 + 03b9d4b commit e3b49ce
Show file tree
Hide file tree
Showing 13 changed files with 294 additions and 121 deletions.
6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

76 changes: 47 additions & 29 deletions astro.config.mjs
Original file line number Diff line number Diff line change
@@ -1,36 +1,54 @@
import { defineConfig } from 'astro/config';
import {defineConfig} from 'astro/config';
import starlight from '@astrojs/starlight';

import expressiveCode from "astro-expressive-code";

// https://astro.build/config
export default defineConfig({
site: 'https://withastro-utils.github.io',
base: '/docs',
integrations: [expressiveCode(), starlight({
editLink: {
baseUrl: 'https://github.com/withastro-utils/docs/tree/main/',
},
lastUpdated: true,
favicon: '/favicon.png',
title: 'Astro Utils',
logo: {
src: '/src/assets/logo.png'
},
social: {
github: 'https://github.com/withastro-utils/utils'
},
sidebar: [{
label: 'Guides',
autogenerate: {
directory: 'guides'
}
}, {
label: 'Reference',
autogenerate: {
directory: 'reference'
}
}],
customCss: ['./src/styles/home.css', './src/styles/code-margin.css']
})]
site: 'https://withastro-utils.github.io',
base: '/docs',
integrations: [expressiveCode(), starlight({
editLink: {
baseUrl: 'https://github.com/withastro-utils/docs/tree/main/',
},
lastUpdated: true,
favicon: '/favicon.png',
title: 'Astro Utils',
logo: {
src: '/src/assets/logo.png'
},
social: {
github: 'https://github.com/withastro-utils/utils'
},
sidebar: [{
label: 'Guides',
items: [{
label: 'Forms',
items: [
{
label: 'Getting Started',
link: '/guides/forms/getting-started'
}, {
label: 'Data Binding',
link: '/guides/forms/data-binding'
}, {
label: 'JS Helpers',
link: '/guides/forms/js-helpers'
}
]
}, {
label: 'Context',
link: '/guides/context'
}, {
label: 'Express Endpoints',
link: '/guides/express-endpoints'
}]
}, {
label: 'Reference',
autogenerate: {
directory: 'reference/forms'
}
}],
customCss: ['./src/styles/home.css', './src/styles/code-margin.css']
})]
});
Binary file added src/assets/change-color/after-clicks.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/change-color/initial-state.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes.
145 changes: 145 additions & 0 deletions src/content/docs/guides/forms/data-binding.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
---
title: Binding & Validation
description: Using data binding with Astro Forms
---

# Introduction

With Astro Forms you can easily create forms with data binding and validation. It also provides a simple way to handle form submissions and preserve state between postbacks.

## Data Binding

Astro Forms supports two-way data binding. This means that you can bind a form field to a property on `Bind` instance and the value of the property will be updated when the field changes and vice versa.

### Example

```astro
---
import { Bind, BindForm, BButton, BInput } from "@astro-utils/forms/forms.js";
import Layout from "../layouts/Layout.astro";
type Form = {
name: string;
age: number;
}
const form = Bind<Form>();
let showSubmitText: string;
function formSubmit(){
showSubmitText = `You name is ${form.name}, you are ${form.age} years old. `;
form.age++;
}
---
<Layout>
<BindForm bind={form}>
{showSubmitText}
<h4>What you name*</h4>
<BInput type="text" name="name" maxlength={20} required/>
<h4>Enter age*</h4>
<BInput type="int" name="age" min={10} required/>
<BButton onClick={formSubmit} whenFormOK>Submit</BButton>
</BindForm>
</Layout>
```

In this example, the `Bind` instance is bound to the form fields.
- When the user changes the value of the `name` or `age` fields and submit, the `Bind` instance will be updated.
- The `formSubmit` function will be called when the user clicks the `Submit` button and the form is valid.
- After `formSubmit` the `age` property will be incremented and the `showSubmitText` will be updated.

## View State

Astro Forms also supports view state. This means that the values of the form fields will be preserved between postbacks.

### Example

```astro
---
type Form = {
counter: number;
}
const form = Bind<Form>({counter: 0});
function incCounter(){
form.counter++;
}
---
<Layout>
<BindForm bind={form}>
Counter: {form.counter}
<BButton onClick={formSubmit}>Increase counter</BButton>
</BindForm>
</Layout>
```

What happens here is that the `counter` property of the `Bind` instance will be incremented every time the user clicks the `Increase counter` button.

The `Bind` state will persist between postbacks, by storing the data on the client side compressed and __encrypted__.

### Valid values

The state of the `Bind` instance must be serlizable, but you can use more then just JSON.

You can use any valid [SuperJSON](https://github.com/blitz-js/superjson) value in the `Bind` instance.
Meaning also `Date`, `Map`, `URL`, `Set`, `RegExp`, `BigInt`, `undefined` are supported.


### Button State

You can use state per `BButton` component. You can also change the `BButton` props easily each time the button is clicked.

Special props:
- **state**: any - store state per button
- **extra**: any - store extra data per button (cannot be changed in the `onClick` function)
- **innerText**: string - the text of the button (overides the children)
- **innerHTML**: string - the HTML of the button (overides the children)
- **remove**: boolean - remove the button from the HTML

```astro
---
import { BButton, Bind, BindForm } from '@astro-utils/forms/forms.js';
import Layout from '../layouts/Layout.astro';
const colors = ['red', 'green', 'blue', 'yellow', 'purple', 'orange', 'pink', 'black', 'white', 'gray'];
function changeColor() {
this.state.color++;
if (this.state.color >= colors.length) {
this.state.color = this.extra;
}
this.style = `color: ${colors[this.state.color]}`;
}
---
<Layout>
<BindForm>
{
colors.map((_, i) => (
<BButton onClick={changeColor} extra={i} state={{ color: i }}>
Button {i}
</BButton>
))
}
</BindForm>
</Layout>
```

In this example, the `changeColor` will change the button color each time it is clicked for a different color in the `colors` array.

If the button is clicked more times then the length of the `colors` array, it will start from the beginning.

#### Initial state

![Initial state](../../../../assets/change-color/initial-state.png)


#### After some clicks

![After clicks](../../../../assets/change-color/after-clicks.png)
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: Forms - Getting started
title: Getting started
description: How to use astro forms
---

Expand All @@ -8,15 +8,13 @@ import { Tabs, TabItem } from '@astrojs/starlight/components';
# Introduction

Astro-Utils Forms is a set of tools to help you build forms in Astro.

It provides similar functionality to ASP.NET Web Forms, but with a modern approach.

Some of the features:

- ✅ Client & Server side form validation
- ✅ Forms [CSRF](https://developer.mozilla.org/en-US/docs/Glossary/CSRF) protection
- ✅ Data binding
- ✅ File upload (support for multipart forms)
- ✅ Web controls
- ✅ Web Controls (Data Binding) + View State
- ✅ JWT session

## Installation
Expand All @@ -30,6 +28,13 @@ First install the package
npm install @astro-utils/forms
```

</TabItem>
<TabItem label="bun">

```sh
bun i @astro-utils/forms
```

</TabItem>
<TabItem label="pnpm">

Expand Down Expand Up @@ -60,8 +65,8 @@ export default defineConfig({
integrations: [formDebug]
});
```
This integration will easy you're debugging by avoiding the browser popups every vite reload.

This integration will simplify your debugging process by eliminating the browser pop-ups during every Vite reload

Edit
`src/middleware.ts` to add the middleware
Expand Down
73 changes: 73 additions & 0 deletions src/content/docs/guides/forms/js-helpers.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
---
title: JS Helpers
description: SSR JS for client actions
---

# Introduction

Astro forms ship with some utility functions to handle forms.
Some functionality works by sending the script to the client and running it there.

You can access them via `Astro.locals.forms`.

### Controllers

```ts
class FormsReact {
scriptToRun: string;
overrideResponse: Response | null;

// server side redirect
redirect(location: string, status?: ValidRedirectStatus): void;
updateSearchParams(): {
search: URLSearchParams;
redirect(status?: ValidRedirectStatus): void;
};
updateOneSearchParam(key: string, value?: string, status?: ValidRedirectStatus): void;

// client side redirect
redirectTimeoutSeconds(location: string, timeoutSec = 2): void;

alert(message: string): void;
consoleLog(...messages: any[]): void;
console(type: keyof Console, ...messages: any[]): void;
callFunction(func: string, ...args: any[]): void;
}
```

### Example

```astro
---
import { Bind, BindForm, BButton, BInput } from "@astro-utils/forms/forms.js";
import Layout from "../layouts/Layout.astro";
type Form = {
name: string;
age: number;
}
const form = Bind<Form>();
let showSubmitText: string;
function formSubmit(){
showSubmitText = `You name is ${form.name}, you are ${form.age} years old. `;
// Redirect to home page after 2 seconds
Astro.locals.forms.redirectTimeoutSeconds('/');
}
---
<Layout>
<BindForm bind={form}>
{showSubmitText}
<h4>What you name*</h4>
<BInput type="text" name="name" maxlength={20} required/>
<h4>Enter age*</h4>
<BInput type="int" name="age" min={10} required/>
<BButton onClick={formSubmit} whenFormOK>Submit</BButton>
</BindForm>
</Layout>
```
2 changes: 1 addition & 1 deletion src/content/docs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ hero:
file: ../../assets/logo.png
actions:
- text: Get started
link: guides/forms/
link: /docs/guides/forms/getting-started
icon: right-arrow
variant: primary
- text: View on GitHub
Expand Down
Loading

0 comments on commit e3b49ce

Please sign in to comment.