# Form

Source code is hosted at [GitHub](https://github.com/corets/form)

This is an opinionated form library for any JavaScript environment, with focus on React and React Native. The main difference between this project and other form libraries out there, is the ability to define a form *outside* of your presentation layer and just map it inside a component later on. A form built this way will encapsulate all the necessary logic to handle validation, submission, error handling and so on. This leads to clean separation of concerns, easier testing, composability and reusability.

One of the coolest features of this library is the fully static form fields access, anything that can be caught at compile time does not have to be tested explicitly.

* **Works everywhere**, optimised for **React and React Native**
* Strong typings from tail to toe, for **joyful developer experience**
* Can be **tested separately from the UI**
* Treat **forms as a service** and reduce complexity inside components
* John Wick kind of crazy **powerful validation** options
* Fully static field access, **forget your string based keys**
* Many goodies like, status indicators, dirty fields, etc.
* Tested by an army of killer coding ninja monkeys with a **test coverage of 100%**
* Tons of other features and very easy customisation options

{% tabs %}
{% tab title="yarn" %}

```bash
yarn add @corets/form
```

{% endtab %}

{% tab title="npm" %}

```bash
npm install --save @corets/form
```

{% endtab %}
{% endtabs %}

Seamless React integration is shipped in this package:

{% content-ref url="../hooks/use-form" %}
[use-form](https://docs.corets.io/hooks/use-form)
{% endcontent-ref %}

Ready to use form bindings for React, to get you started, can be found here:

{% content-ref url="../hooks/use-form-binder" %}
[use-form-binder](https://docs.corets.io/hooks/use-form-binder)
{% endcontent-ref %}

Comes with built in support for the schema package for delightful validation logic:

{% content-ref url="schema" %}
[schema](https://docs.corets.io/services/schema)
{% endcontent-ref %}

Static fields access is powered by this library:

{% content-ref url="accessor" %}
[accessor](https://docs.corets.io/services/accessor)
{% endcontent-ref %}

## Quick start <a href="#quick-start" id="quick-start"></a>

Use this example as a high level overview of what this library has to offer and whether it suits your personal preference. Here we are going to create a very basic form that has some validation logic and dispatches an HTTP request to a remote endpoint, for processing.

First, let's define our types:

```typescript
export type User = {  
    uuid: string  
    firstName: string  
    lastName: string
}

export type CreateUserForm = {  
    firstName: string  
    lastName: string
}

export type CreateUserResult = {  
    success?: string  
    error?: string  
    user?: User
}
```

Next, define a method that is going to call the API:

```typescript
export const createUser = async (data: CreateUserForm): Promise<User> => 
    ({ id: 1, ...data })
```

Now we can build the form logic:

```typescript
import { createFormFromSchema } from "@corets/form"
import { object, value } from "@corets/schema"

export const createUserForm = () => {  
    return createFormFromSchema<CreateUserForm, CreateUserResult>(object({      
            firstName: value("").string().min(2).max(20).toTrimmed(),      
            lastName: value("").string().min(2).max(20).toTrimmed()    
        }))    
        .handler(async (form) => {      
            try {        
                const user = await createUser(form.get())        
                
                return { success: "User created", user }      
            } catch (error) {        
                return { error: "Could not create user" }      
            }    
        })
}
```

Now let's build the actual form:

```typescript
import React from "react"
import { useForm } from "@corets/use-form"
import { useFormBinder } from "@corets/use-form-binder"

const CreateUserForm = () => {  
    const form = useForm(createUserForm)  
    const bind = useFormBinder(form)  
    const errors = form.getErrors()  
    const result = form.getResult()
    const isSubmitting = form.isSubmitting()

    return (    
        <form {...bind.form()}>
            <div>{isSubmitting && "Loading..."}</div>
            
            <div>{result?.success || result?.error }</div>  
                
            <div>        
                <input {...bind.input("firstName")} placeholder="First name"/>        
                <div>{form.getErrorsAt("firstName")}</div>       
            </div>
            
            <div>        
                <input {...bind.input("lastName")} placeholder="Last name"/>
                <div>{errors.getErrorsAt("lastName")}</div>      
            </div>      
            
            <button {...bind.button()}>Create</button>    
        </form>  
    )
}
```

{% hint style="info" %}
In this example we are using the vanilla form binder shipped through the [@corets/use-form-binder](https://docs.corets.io/hooks/use-form-binder) package, read more about form binders in the [next section](#binders).
{% endhint %}

## Static fields

One of the unique features of this library is it's static field access. Normally you bind / get / set your form data using dynamic, string based keys, which breaks your compile time safety. This significantly increases the maintenance cost of forms in the future, since you always have to be extremely careful when changing any of the form fields or the form object structure.

There is a better way to do this! Below is a side by side comparison of a form using dynamic and static field access. Keep in mind that you have full IDE / autocomplete support when accessing form fields through the static facade.

{% tabs %}
{% tab title="static" %}

```typescript
import { createForm, ObservableFormField } from "@corets/form"

const form = createForm({ some: { nested: "field" } })
const fields = form.getFields()

fields.some.nested.get().getValue()
fields.some.nested.get().setValue("new value")
```

{% endtab %}

{% tab title="dynamic" %}

```typescript
import { createForm, ObservableForm } from "@corets/form"

const form = createForm({ some: { nested: "field" } })

form.getAt("some.nested")
form.setAt("some.nested", "new value")
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Have a look at the [Form.getFields()](#formfields) documentation for more details.
{% endhint %}

You can read more about static accessors in the package docs:

{% content-ref url="accessor" %}
[accessor](https://docs.corets.io/services/accessor)
{% endcontent-ref %}

## Component binders <a href="#binders" id="binders"></a>

This library was not designed for any specific framework. You should be able to use it everywhere where you can run JavaScript. This is why it does not ship any logic on how to connect to the UI out of the box. The [@corets/use-form-binder](https://docs.corets.io/hooks/use-form-binder) package provides bindings for vanilla HTML elements, to get you started.

Given the nature of modern frontend development, projects use various component libraries and more often than not you have to write a custom input component. Obviously it is not possible to write one binder to *rule them all*. Therefore the most pragmatic approach is to create dedicated binders for various use cases. The good thing is that **it is super easy to write your own binder!**

Take a look at [how binders are implemented](https://github.com/corets/use-form-binder/blob/master/src/FormBinder.ts) in the @corets/use-form-binder package to get an idea.

Here is an example of a very basic, vanilla text field binder:

```typescript
import { ObservableForm } from "@corets/form"

const createBinder = (form: ObservableForm) => ({  
    input: createInputBinder(form)
})

const createInputBinder = (form: ObservableForm) => (path: string) => {  
    return {    
        name: path,    
        value: form.getAt(path),    
        onChange: (e) => form.setAt(path, e.target.value),  
    }
}
```

That's all there is about it! The best thing is, no matter what component you are going to use, **you can always make it work! ™ 😎**

Now let's use that binder in our form:

```typescript
import React from "react"
import { createForm } from "@corets/form"
import { useForm } from "@corets/use-form"

const Example = () => {  
    const form = useForm(() => createForm({ field: "value" }))  
    const bind = createBinder(form)  
    
    return <input {...bind.input("field")} />
}
```

Another way to create a binder is using the [`Form.getFields()`](#formfields) method powered by the [@corets/accessor](https://docs.corets.io/services/accessor) library. You no longer need to use dynamic, string based keys to map the form fields, you can use a statically typed facade instead:

```typescript
import { ObservableFormField } from "@corets/form"

const createBinder = () => ({  input: inputBinder })

const inputBinder = (field: ObservableFormField) => {  
    return {    
        name: field.getKey(),    
        value: field.getValue(),    
        onChange: (e) => field.setValue(e.target.value),  
    }
}
```

Now let's use that binder in our form:

```typescript
import React from "react"
import { createForm } from "@corets/form"
import { useForm } from "@corets/use-form"

const Example = () => {  
    const form = useForm(() => createForm({ 
        some: { nested: { field: "value" } } 
    }))  
    
    const bind = createBinder(form)  
    const fields = form.getFields()  
    
    return <input {...bind.input(fields.some.nested.field.get())} />
}
```

{% hint style="info" %}
Read more about static fields [here](#static-fields).
{% endhint %}

## Optimisation <a href="#optimisation" id="optimisation"></a>

You can reduce the number of re-renders in big forms, by wrapping your UI blocks into the `<Memo/>` component from the [@corets/memo](https://docs.corets.io/components/memo) package. Most likely you will never need this, but if you do, you are covered.

```typescript
import React, { useState } from "react"
import { createForm } from "@corets/form"
import { useForm } from "@corets/use-form"
import { Memo } from "@corets/memo"

const Example = () => {  
    const form = useForm(() => createForm({ field1: "foo", field2: "bar" }))  
    const [someValue, setSomeValue] = useState("some state")  
    
    return (    
        <form>      
            <Memo deps={form.getDeps("field1")}>        
                This section will only ever re-render when some of shared form properties change,        
                like: `submitting`, `submitted` or `result`, or when one of the field 
                related properties receives a change specific to this field, 
                like: `errors`, `values`, `changedFields` or `dirtyFields`.
            </Memo>      
            
            <Memo deps={form.getDeps(["field1", "field2"])}>        
                This section will change when one of the two fields receives a relevant change.      
            </Memo>      
            
            <Memo deps={form.getDeps(["field1", "field2"], { errors: false })}>        
                This block will NOT re-render if there is an error for one of the two fields.
            </Memo>      
            
            <Memo deps={[...form.getDeps(["field1", "field2"]), someValue]}>        
                Include an aditional, custom, value to the list of dependencies for a re-render.      
            </Memo>    
        </form>  
    )
}
```

You can read more about \<Memo/> in the package docs:

{% content-ref url="../components/memo" %}
[memo](https://docs.corets.io/components/memo)
{% endcontent-ref %}

## Testing <a href="#testing" id="testing"></a>

When writing unit tests, make sure that you set the `debounce` config property to `0`. This disables throttling of the state changes and allows you to write tests in a synchronous manner.

```typescript
import { createForm } from "@corets/form"

const form = createForm().configure({ debounce: 0 })
```

## createForm() <a href="#createform" id="createform"></a>

Create a new form instance:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ data: "foo" })
```

Create a new form without the factory function:

```typescript
import { Form } from "@corets/form"

const form = new Form({ data: "foo" })
```

Create a form instance with a specific type:

```typescript
import { createForm } from "@corets/form"

type MyForm = { data: string }

const form = createForm<MyForm>({ data: "foo" })
```

Specify form result type:

```typescript
import { createForm } from "@corets/form"

type MyForm = { data: string }
type MyFormResult = { result: string }

const form = createForm<MyForm, MyFormResult>({ data: "foo" })
```

## createFormFromSchema() <a href="#createformfromschema" id="createformfromschema"></a>

Create a new form instance based on a schema definition from the [@corets/schema](https://docs.corets.io/services/schema) package. This is a convenient helper that allows you to avoid unnecessary boilerplate code when defining initial values for a form. Instead of creating an object, that adheres to the specific form type, with the initial values, you can define those initial values inside the schema definition itself.

Here is a side by side comparison of the two different approaches:

{% tabs %}
{% tab title="createFormFromSchema" %}

```typescript
import { createFormFromSchema } from "@corets/form"
import { object, schema } from "@corets/schema"

type MyForm = { data: string }
type MyFormResult = { result: string }

const formSchema = object<MyForm>({  
    data: schema("foo").string().min(2)
})

const form = createFormFromSchema<MyForm, MyFormResult>(formSchema)
```

{% endtab %}

{% tab title="createForm" %}

```typescript
import { createForm } from "@corets/form"
import { object, string } from "@corets/schema"

type MyForm = { data: string }
type MyFormResult = { result: string }

const formSchema = object<MyForm>({ data: string().min(2) })

const initialValues: MyForm = { data: "foo" }

const form = createForm<MyForm, MyFormResult>(initialValues).schema(formSchema)
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
You should always use **createFormFromSchema** instead of of **createForm** where possible.
{% endhint %}

You can read more about schemas in the package docs:

{% content-ref url="schema" %}
[schema](https://docs.corets.io/services/schema)
{% endcontent-ref %}

## Form.configure() <a href="#formconfig" id="formconfig"></a>

Alter form behaviour by providing configuration overrides:

```typescript
import { createForm } from "@corets/form"

const form = createForm().configure({    
    // run schema sanitizers on form submit() and validate()? (default: true)
    sanitize: true,    
    // validate on form submit()? (default: true)
    validate: true,    
    // validate fields immediately after a change? (default: true)
    reactive: true,    
    // delay invocation of listeners after a change (default: 10ms)
    debounce: 10,  
})
```

## Form.handler() <a href="#formhandler" id="formhandler"></a>

Specify a handler that is called whenever the form is submitted:

```typescript
import { createForm } from "@corets/form"

type MyForm = { data: string }
type MyFormResult = { result: string }

const form = createForm<MyForm, MyFormResult>({ data: "foo" })
    .handler(async (form) => {    
        const result: MyFormResult = { result: "some value" }    
        
        return result  
    })
```

Result returned from a handler is also returned from the [`Form.submit()`](#formsubmit) method and is available through the [`Form.getResult()`](#formgetresult) method later on.

## Form.validator() <a href="#formvalidator" id="formvalidator"></a>

Specify a validation function that is called before the form is submitted or after a form field has been changed, depends on your form config:

```typescript
import { createForm } from "@corets/form"

type MyForm = { data: string, nested: { field: string } }

const form = createForm<MyForm>({ data: "foo", nested: { field: "bar" } })  
    .validator(async (form) => {    
        const values = form.get()    
        
        // run some validation logic ...
        
        return {      
            "foo": ["Invalid value"],      
            "nested.field": ["Invalid value"]    
        }  
    })
```

If you happen to return validation errors for the whole form, but the validator has been invoked after a field change, errors relating to fields that are not yet changed, will be omitted. Validator will be called in addition to the schema that you might have configured, possible errors will be merged.

## Form.schema() <a href="#formschema" id="formschema"></a>

Configure a validation schema for your form:

```typescript
import { createForm } from "@corets/form"
import { object, string } from "@corets/schema"

type MyForm = { data: string, nested: { field: string } }

const schema = object({  
    data: string().min(2),  
    nested: object({    
        field: string().max(2).toTrimmed()  
    }
)})

const form = createForm<MyForm>({ 
    data: "foo", 
    nested: { field: "bar" } 
}).schema(schema)
```

Schemas are a delightful and powerful way to write your validation logic. They are feature loaded, very flexible and customisable. If you happen to provide a schema and a validator function, both will be invoked and the resulting errors will be merged.

{% hint style="info" %}
Sanitizers that have been added to the schema will be called before any kind of validation happens and will alter the form values if necessary. This applies to the complete validation cycle and not to the reactive one (where validation is triggered only for the changed field).
{% endhint %}

You can read more about schemas in the package docs:

{% content-ref url="schema" %}
[schema](https://docs.corets.io/services/schema)
{% endcontent-ref %}

## Form.validate() <a href="#formvalidate" id="formvalidate"></a>

Invoking the validate method will trigger the validation process where values get sanitized, schema and validator get called, resulting errors get merged, and so on:

```typescript
import { createForm } from "@corets/form"
import { object, string } from "@corets/schema"

type MyForm = { data: string, nested: { field: string } }

const schema = object<MyForm>({  
    data: string().min(2),  
    nested: object({    
        field: string().max(2)  
    }
)})
const form = createForm<MyForm>({ 
    data: "foo", 
    nested: { field: "" } 
}).schema(schema)

const errors = await form.validate()

if ( ! errors) {
    // continue ...
}
```

Errors object will look something like this:

```typescript
{  
    "data": ["Must be at least \"2\" characters long"],  
    "nested.field": ["Required", "Must be less than \"2\" characters long"],
}
```

You can access errors anytime later:

```typescript
const errors = form.getErrors()
```

You can choose to validate and sanitize only fields that have been changed:

```typescript
const errors = await form.validate({ changed: true })
```

You can choose to disable sanitization of values:

```typescript
const errors = await form.validate({ sanitize: false })
```

You can choose not to persist any possible errors and just do a dry run:

```typescript
const errors = await form.validate({ persist: false })
```

## Form.submit() <a href="#formsubmit" id="formsubmit"></a>

Submitting a form will invoke the validator, schema and handler, depending on the configuration. Whatever is returned from the handler will be returned from the submit method itself and is also stored on the form object for later access.

```typescript
import { createForm } from "@corets/form"

type MyForm = { data: string }
type MyFormResult = { result: string }

const form = createForm<MyForm, MyFormResult>()  
    .handler(async () => {    
        return { result: "foo" }  
    })
const result = await form.submit()
```

You can access result anytime later:

```typescript
const result = form.getResult()
```

Access validation errors:

```typescript
const errors = form.getErrors()
```

Submit without validation:

```typescript
const result = form.submit({ validate: false })
```

Submit without sanitization:

```typescript
const result = form.submit({ sanitize: false })
```

Validate and sanitize only those form fields that have been changed:

```typescript
const result = form.submit({ changed: true })
```

## Form.listen() <a href="#formlisten" id="formlisten"></a>

Subscribe to any changes on the form object:

```typescript
import { createForm } from "@corets/form"

const form = createForm({})
const unsubscribe = form.listen(form => 
    console.log("something has changed")
)

unsubscribe()
```

Set a custom debounce interval for your listener:

```typescript
import { createForm } from "@corets/form"

const form = createForm({})
const unsubscribe = form.listen(form => 
    console.log("something has changed")
, { debounce: 100 })
```

Disable debounce for your listener:

```typescript
import { createForm } from "@corets/form"

const form = createForm({})
const unsubscribe = form.listen(form => 
    console.log("something has changed")
, { debounce: 0 })
```

## Form.get() <a href="#formget" id="formget"></a>

Get all form values:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ data: "foo" })
const values = form.get()
```

## Form.getAt() <a href="#formgetat" id="formgetat"></a>

Get form values at a specific path:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ nested: { data: "foo"} })
const value = form.getAt("nested.data")
```

## Form.set() <a href="#formset" id="formset"></a>

Replace all form values with the new ones:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ data: "foo" })

form.set({ data: "bar" })
```

{% hint style="info" %}
This method overrides all form values and does not track any dirty or changed fields. Use the [Form.setAt()](#formsetat) method instead, if you want to track dirty and changed fields.
{% endhint %}

## Form.setAt() <a href="#formsetat" id="formsetat"></a>

Replace form values at a specific path:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ nested: { data: "foo" } })

form.setAt("nested.data", "bar")
```

## Form.put() <a href="#formput" id="formput"></a>

Add some data to the form by doing a merge:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ data1: "foo" })

form.put({ data2: "bar" })
```

{% hint style="info" %}
Unlike [Form.set()](#formset), this method does not override all the form data, but will rather merge it instead. This method does not track any dirty or changed fields. Use [Form.setAt()](#formsetat) method, if you want to track dirty and changed fields.
{% endhint %}

## Form.clear() <a href="#formclear" id="formclear"></a>

Reset form values, errors, status indicators, etc. This will reset form values to the initial values (the one that you've passed into the [`createForm()`](#createform) function):

```typescript
import { createForm } from "@corets/form"

const form = createForm({ data: "foo" })

form.clear()
```

{% hint style="info" %}
You might want to call this after a successful form submission.
{% endhint %}

You can replace initial values while clearing the form:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ data: "foo" })

form.clear({ data: "bar" })
```

## Form.getErrors() <a href="#formgeterrors" id="formgeterrors"></a>

Get all form errors:

```typescript
import { createForm } from "@corets/form"

const form = createForm()
const errors = form.getErrors()
```

## Form.getErrorsAt() <a href="#formgeterrorsat" id="formgeterrorsat"></a>

Get all errors at a specific path:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ nested: { data: "foo" } })
const errors = form.getErrors("nested.data")
```

## Form.setErrors() <a href="#formseterrors" id="formseterrors"></a>

Replace all errors:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.setErorrs({ field: ["first error", "second error"] })
```

## Form.setErrorsAt() <a href="#formseterrorsat" id="formseterrorsat"></a>

Replace all errors at a specific path:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.setErrorsAt("nested.field", ["first error", "second error"])
```

## Form.addErrors() <a href="#formadderrors" id="formadderrors"></a>

Add some new errors:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.addErrors({ field: ["first error", "second error"] })
```

## Form.addErrorsAt() <a href="#formadderrorsat" id="formadderrorsat"></a>

Add some new errors at a specific path:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.addErrorsAt("nested.field", ["first error", "second error"])
```

## Form.hasErrors() <a href="#formhaserrors" id="formhaserrors"></a>

Check if there are any errors:

```typescript
import { createForm } from "@corets/form"

const form = createForm()
const hasErrors = form.hasErrors()
```

## Form.hasErrorsAt() <a href="#formhaserrorsat" id="formhaserrorsat"></a>

Check if there are any errors at a specific path:

```typescript
import { createForm } from "@corets/form"

const form = createForm()
const hasErrors = form.hasErrorsAt("nested.field")
```

## Form.clearErrors() <a href="#formclearerrors" id="formclearerrors"></a>

Clear all errors:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.clearErrors()
```

## Form.clearErrorsAt() <a href="#formclearerrorsat" id="formclearerrorsat"></a>

Clear all errors at a specific path:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.clearErrorsAt("nested.field")
```

## Form.isDirty() <a href="#formisdirty" id="formisdirty"></a>

Check if any of the form fields are dirty (have been written to):

```typescript
import { createForm } from "@corets/form"

const form = createForm()
const isDirty = form.isDirty()
```

{% hint style="info" %}
Works only for fields that have been set using the [Form.setAt()](#formsetat) method. Methods like [Form.set()](#formset) or [Form.put()](#formput) do not track this kind of changes.
{% endhint %}

## Form.isDirtyAt() <a href="#formisdirtyat" id="formisdirtyat"></a>

Check if a specific field is dirty:

```typescript
import { createForm } from "@corets/form"

const form = createForm()
const isDirty = form.isDirtyAt("nested.field")
```

## Form.getDirty() <a href="#formgetdirty" id="formgetdirty"></a>

Get a list of all dirty fields:

```typescript
import { createForm } from "@corets/form"

const form = createForm()
const dirtyFields = form.getDirty()
```

## Form.setDirtyAt() <a href="#formsetdirtyat" id="formsetdirtyat"></a>

Tag some fields as dirty:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.setDirtyAt(["field", "nested.field"])
```

## Form.clearDirty() <a href="#formcleardirty" id="formcleardirty"></a>

Clear all dirty fields:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.clearDirty()
```

## Form.clearDirtyAt() <a href="#formcleardirtyat" id="formcleardirtyat"></a>

Clear a specific dirty field:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.clearDirtyAt("nested.field")
```

## Form.isChanged() <a href="#formischanged" id="formischanged"></a>

Check if any of the form fields have been changed:

```typescript
import { createForm } from "@corets/form"

const form = createForm()
const isChanged = form.isChanged()
```

{% hint style="info" %}
Contrary to the [Form.isDirty()](#formisdirty) method, for a field to be tracked as changed, its new value has to be different from its initial value provided during the form creation. This only works for fields that have been changed using the [Form.setAt()](#formsetat) method. Methods like [Form.set()](#formset) and [Form.put()](#formput) do not track this kind of changes.
{% endhint %}

## Form.isChangedAt() <a href="#formischangedat" id="formischangedat"></a>

Check if a specific field has been changed:

```typescript
import { createForm } from "@corets/form"

const form = createForm()
const isChanged = form.isChangedAt("nested.field")
```

## Form.getChanged() <a href="#formgetchanged" id="formgetchanged"></a>

Get all fields that have been changed:

```typescript
import { createForm } from "@corets/form"

const form = createForm()
const changedFields = form.getChanged()
```

## Form.setChangedAt() <a href="#formsetchangedat" id="formsetchangedat"></a>

Track some fields as changed:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.setChangedAt(["field", "nested.field"])
```

## Form.clearChanged() <a href="#formclearchanged" id="formclearchanged"></a>

Clear all changed fields:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.clearChanged()
```

## Form.clearChangedAt() <a href="#formclearchangedat" id="formclearchangedat"></a>

Clear a specific changed field:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.clearChangedAt("nested.field")
```

## Form.getResult() <a href="#formgetresult" id="formgetresult"></a>

Get the result that has been returned from the handler function during the last form submission:

```typescript
import { createForm } from "@corets/form"

const form = createForm()
const result = form.getResult()
```

{% hint style="info" %}
Form handler result is also returned directly from the [Form.submit()](#formsubmit) method after invocation.
{% endhint %}

## Form.setResult() <a href="#formsetresult" id="formsetresult"></a>

Manually replace form result:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.setResult({ some: "data" })
```

## Form.clearResult() <a href="#formclearresult" id="formclearresult"></a>

Clear form result:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.clearResult()
```

## Form.isSubmitting() <a href="#formissubmitting" id="formissubmitting"></a>

Check if form is currently being submitted:

```typescript
import { createForm } from "@corets/form"

const form = createForm()
const isSubmitting = form.isSubmitting()
```

{% hint style="info" %}
Form enters this state whenever you invoke the [Form.submit()](#formsubmit) method.
{% endhint %}

## Form.setIsSubmitting() <a href="#formsetsubmitting" id="formsetsubmitting"></a>

Manually change form status:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.setIsSubmitting(true)
```

{% hint style="info" %}
Usually you don't have to do this manually.
{% endhint %}

## Form.isSubmitted() <a href="#formissubmitted" id="formissubmitted"></a>

Check if form has been successfully submitted at least once:

```typescript
import { createForm } from "@corets/form"

const form = createForm()
const isSubmitted = form.isSubmitted()
```

{% hint style="info" %}
Form enters this state after a successful form submission (one that did not cause any validation errors nor did throw an exception from the form handler).
{% endhint %}

## Form.setIsSubmitted() <a href="#formsetsubmitted" id="formsetsubmitted"></a>

Manually change form status:

```typescript
import { createForm } from "@corets/form"

const form = createForm()

form.setIsSubmitted(true)
```

{% hint style="info" %}
Usually you don't have to do this manually.
{% endhint %}

## Form.getDeps() <a href="#formdeps" id="formdeps"></a>

Returns a list of **dependencies** for any specific field. This is useful whenever you need to calculate whether an input field should be re-rendered or not, for example inside [`useMemo()`](https://reactjs.org/docs/hooks-reference.html#usememo), [`useCallback()`](https://reactjs.org/docs/hooks-reference.html#usecallback) or even a [`useEffect()`](https://reactjs.org/docs/hooks-reference.html#useeffect).

```typescript
import { createForm } from "@corets/form"

const form = createForm()
const dependenciesForOneField = form.getDeps("some.field")
const dependenciesForMultipleFields = form.getDeps(["some.field", "another.field"])
```

You can customise what kind of form state should be considered a dependency:

```typescript
form.getDeps("some.field", {  
    // form config (default: true)
    config: true,  
    // value of that specific field (default: true)
    value: true,  
    // dirty fields (default: true)
    isDirty: true,  
    // changed fields config (default: true)
    isChanged: true,  
    // isSubmitting form status (default: true)
    isSubmitting: true,  
    // isSubmitted form status (default: true)
    isSubmitted: true,  
    // errors of that specific field (default: true)
    errors: true,  
    // form result (default: true)
    result: true,
})
```

## Form.getFields() <a href="#formfields" id="formfields"></a>

Returns an accessor object that can be used to get a handle for an individual form field. Each form field is represented by an instance of `FormField`. The main purpose of this approach is to be able to statically access fields, without having to rely on dynamic string based keys.

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { nested: { field: "value" } } })
const fields = form.getFields()

fields.some.nested.field.get().setValue("new value")
// same as
form.setAt("some.nested.field", "new value")
// or even
form.setAt(fields.some.nested.field.key(), "new value")
```

Check out the package docs to learn more about the library used behind the scenes:

{% content-ref url="accessor" %}
[accessor](https://docs.corets.io/services/accessor)
{% endcontent-ref %}

## FormField.getValue() <a href="#formfieldgetvalue" id="formfieldgetvalue"></a>

Get field value:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { field: "value" } })
const field = form.getFields().some.field.get()

field.getValue()
// same as
form.getAt("some.field")
```

## FormField.setValue() <a href="#formfieldsetvalue" id="formfieldsetvalue"></a>

Set field value:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { field: "value" } })
const field = form.getFields().some.field.get()

field.setValue("new value")
// same as
form.getAt("some.field", "new value")
```

## FormField.getKey() <a href="#formfieldgetkey" id="formfieldgetkey"></a>

Get absolute field key:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { field: "value" } })
const field = form.getFields().some.field.get()

field.getKey()
```

## FormField.getErrors() <a href="#formfieldgeterrors" id="formfieldgeterrors"></a>

Get field errors:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { field: "value" } })
const field = form.getFields().some.field.get()

field.getErrors()
// same as
form.getErrorsAt("some.field")
```

## FormField.setErrors() <a href="#formfieldseterrors" id="formfieldseterrors"></a>

Override field errors:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { field: "value" } })
const field = form.getFields().some.field.get()

field.setErrors("new error")
// same as
form.setErrorsAt("some.field", "new error")
```

## FormField.hasErrors() <a href="#formfieldhaserrors" id="formfieldhaserrors"></a>

Check if field has any errors:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { field: "value" } })
const field = form.getFields().some.field.get()

field.hasErrors()
// same as
form.hasErrorsAt("some.field")
```

## FormField.addErrors() <a href="#formfieldadderrors" id="formfieldadderrors"></a>

Add some field errors:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { field: "value" } })
const field = form.getFields().some.field.get()

field.addErrors("new error")
// same as
form.addErrorsAt("some.field", "new error")
```

## FormField.clearErrors() <a href="#formfieldclearerrors" id="formfieldclearerrors"></a>

Clear field errors:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { field: "value" } })
const field = form.getFields().some.field.get()

field.clearErrors()
// same as
form.clearErrorsAt("some.field")
```

## FormField.isDirty() <a href="#formfieldisdirty" id="formfieldisdirty"></a>

Check if field is dirty:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { field: "value" } })
const field = form.getFields().some.field.get()

field.isDirty()
// same as
form.isDirtyAt("some.field")
```

## FormField.setDirty() <a href="#formfieldsetdirty" id="formfieldsetdirty"></a>

Tag field as dirty:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { field: "value" } })
const field = form.getFields().some.field.get()

field.setDirty()
// same as
form.setDirtyAt("some.field")
```

## FormField.clearDirty() <a href="#formfieldcleardirty" id="formfieldcleardirty"></a>

Clear dirty status of this field:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { field: "value" } })
const field = form.getFields().some.field.get()

field.clearDirty()
// same as
form.clearDirtyAt("some.field")
```

## FormField.isChanged() <a href="#formfieldischanged" id="formfieldischanged"></a>

Check if field is changed:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { field: "value" } })
const field = form.getFields().some.field.get()

field.isChanged()
// same as
form.isChangedAt("some.field")
```

## FormField.setChanged <a href="#formfieldsetchanged" id="formfieldsetchanged"></a>

Tag field as changed:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { field: "value" } })
const field = form.getFields().some.field.get()

field.setChanged()
form.setChangedAt("some.field")
```

## FormField.clearChanged() <a href="#formfieldclearchanged" id="formfieldclearchanged"></a>

Clear changed status of this field:

```typescript
import { createForm } from "@corets/form"

const form = createForm({ some: { field: "value" } })
const field = form.getFields().some.field.get()

field.clearChanged()
form.clearChangedAt("some.field")
```

## FormField.getForm() <a href="#formfieldgetform" id="formfieldgetform"></a>

Get the `Form` instance attached to this field:

```typescript
const form = field.getForm()
```
