Skip to content

Commit 961b81e

Browse files
docs(component): add documentation for FACE
Form-associated custom elements were added to Stencil in stenciljs/core#4784 for version 4.5.0. This adds documentation explaining the changes to the component authoring DSL and also breaking down some examples of how the functionality could be used to build out a component.
1 parent 6242e46 commit 961b81e

File tree

3 files changed

+167
-0
lines changed

3 files changed

+167
-0
lines changed

cspell-wordlist.txt

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Deno
1010
FOUC
1111
POSIX
1212
Vetur
13+
WHATWG
1314
async
1415
autoprefixing
1516
contentful

docs/components/component.md

+34
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,40 @@ In the example above, the following allows `todo-list` to display the provided a
114114

115115
For more information on configuring assets, please see Stencil's [Assets Guide](../guides/assets.md)
116116

117+
118+
### formAssociated
119+
120+
**Optional**
121+
122+
**Type: `boolean`**
123+
124+
**Default: `false`**
125+
126+
If `true` the component will be
127+
[form-associated](https://html.spec.whatwg.org/dev/custom-elements.html#form-associated-custom-element),
128+
allowing you to take advantage of the
129+
[`ElementInternals`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/attachInternals)
130+
API to enable your Stencil component to participate in forms.
131+
132+
A minimal form-associated Stencil component could look like this:
133+
134+
```tsx
135+
import { Component } from '@stencil/core';
136+
137+
@Component({
138+
tag: 'form-associated',
139+
formAssociated: true
140+
})
141+
export class FormAssociated {
142+
render() {
143+
return <span>form associated!</span>
144+
}
145+
}
146+
```
147+
148+
See the documentation for [form-associated components](./form-associated.md)
149+
for more info and examples.
150+
117151
### scoped
118152

119153
**Optional**

docs/components/form-associated.md

+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
---
2+
title: Form-Associated Components
3+
sidebar_label: Form-Associated Components
4+
description: Form-Associated Stencil Components
5+
slug: /form-associated
6+
---
7+
8+
# Building Form-Associated Components in Stencil
9+
10+
As of v4.5.0, Stencil has support for form-associated custom elements. This
11+
allows Stencil components to participate in a rich way in HTML forms,
12+
integrating with native browser features for validation and accessibility while
13+
maintaining encapsulation and control over their styling and presentation.
14+
15+
:::caution
16+
Browser support for the APIs that this feature depends on is still not
17+
universal[^1] and the Stencil team has no plans at present to support or
18+
incorporate any polyfills for the browser functionality. Before you ship
19+
form-associated Stencil components make sure that the browsers you need to
20+
support have shipped the necessary APIs.
21+
:::
22+
23+
## Creating a Form-Associated Component
24+
25+
A form-associated Stencil component is one which sets the new [`formAssociated`](./component.md#formAssociated)
26+
option in the argument to the `@Component`
27+
decorator to `true`, like so:
28+
29+
```tsx
30+
import { Component } from '@stencil/core';
31+
32+
@Component({
33+
tag: 'my-face',
34+
formAssociated: true,
35+
})
36+
export class MyFACE {
37+
}
38+
```
39+
40+
This element will now be marked as a form-associated custom element via the
41+
[`formAssociated`](https://html.spec.whatwg.org/#custom-elements-face-example)
42+
static property, but by itself this is not terribly useful.
43+
44+
In order to meaningfully interact with a `<form>` element that is an ancestor
45+
of our custom element we'll need to get access to an
46+
[`ElementInternals`](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals)
47+
object corresponding to our element instance. Stencil provides a decorator,
48+
`@AttachInternals`, which does just this, allowing you to decorate a property on
49+
your component and bind an `ElementInternals` object to that property which you
50+
can then use to interact with the surrounding form.
51+
52+
:::info
53+
Under the hood the `AttachInternals` decorator makes use of the very similarly
54+
named
55+
[`attachInternals`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/attachInternals)
56+
method on `HTMLElement` to associate your Stencil component with an ancestor
57+
`<form>` element. During compilation, Stencil will generate code that calls
58+
this method at an appropriate point in the component lifecycle for both
59+
[lazy](../output-targets/dist.md) and [custom
60+
elements](../output-targets/custom-elements.md) builds.
61+
:::
62+
63+
A Stencil component using this API to implement a custom text input could look
64+
like this:
65+
66+
```tsx title="src/components/custom-text-input.tsx"
67+
import { Component, h, AttachInternals, State } from '@stencil/core';
68+
69+
@Component({
70+
tag: 'custom-text-input',
71+
shadow: true,
72+
formAssociated: true
73+
})
74+
export class CustomTextInput {
75+
@State() value: string;
76+
77+
@AttachInternals() internals: ElementInternals;
78+
79+
handleChange(event) {
80+
this.value = event.target.value;
81+
this.internals.setFormValue(event.target.value);
82+
}
83+
84+
componentWillLoad() {
85+
this.internals.setFormValue("a default value");
86+
}
87+
88+
render() {
89+
return (
90+
<input
91+
type="text"
92+
value={this.value}
93+
onInput={(event) => this.handleChange(event)}
94+
/>
95+
)
96+
}
97+
}
98+
```
99+
100+
If this component is rendered within a `<form>` element like so:
101+
102+
103+
```html
104+
<form>
105+
<custom-text-input name="my-custom-input"></custom-text-input>
106+
</form>
107+
```
108+
109+
then it will automatically be linked up to the surrounding form. The
110+
`ElementInternals` object found at `this.internals` will have a bunch of
111+
methods on it for interacting with that form and getting key information out of
112+
it.
113+
114+
In our `<custom-text-input>` example above we use the
115+
[`setFormValue`](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/setFormValue)
116+
method to set a value in the surrounding form. This will read the `name`
117+
attribute off of the element and use it when setting the value, so the value
118+
typed by a user into the `input` will added to the form under the
119+
`"my-custom-input"` name.
120+
121+
This example just scratches the surface, and a great deal more is possible with
122+
the `ElementInternals` API, including [setting the element's
123+
validity](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/setValidity),
124+
reading the validity state of the form, reading other form values, and more.
125+
126+
## Resources
127+
128+
- [WHATWG specification for form-associated custom elements](https://html.spec.whatwg.org/dev/custom-elements.html#form-associated-custom-elements)
129+
- [ElementInternals and Form-Associated Custom Elements](https://webkit.org/blog/13711/elementinternals-and-form-associated-custom-elements/) from the WebKit blog
130+
131+
[^1]: See <https://caniuse.com/?search=attachInternals> for up-to-date adoption
132+
estimates.

0 commit comments

Comments
 (0)