<form>
<rad-address>Shipping address</rad-address>
<button type="submit">Submit</button>
</form>
<pre><output></output></pre>
:root {
--primary-color: hsl(250, 100%, 25%);
--text-color: #141414;
--text-color-inverted: #ffffff;
}
pre {
background: var(--primary-color);
color: var(--text-color-inverted);
font-family: "Operator Mono", "Fira Code", monospace;
padding: 16px;
}
import { LitElement, html, css } from 'https://unpkg.com/lit-element?module';
import 'https://cdn.skypack.dev/element-internals-polyfill';
class RadAddress extends LitElement {
static get formAssociated() {
return true;
}
static get styles() {
return css`
:host {
display: block;
margin-bottom: 16px;
}
fieldset {
border-color: var(--primary-color);
}
legend {
color: var(--primary-color);
font-family: system-ui;
font-size: 24px;
margin-bottom: 12px;
}
`;
}
constructor() {
super();
this.internals = this.attachInternals();
}
async firstUpdated(...args) {
await super.firstUpdated(...args);
this._setValue();
}
render() {
return html`
<form>
<fieldset>
<legend>Address</legend>
<rad-input @input="${this._setValue}" name="line-1">Line one</rad-input>
<rad-input @input="${this._setValue}" name="line-2">Line two</rad-input>
<rad-input @input="${this._setValue}" name="city">City</rad-input>
<rad-input @input="${this._setValue}" name="state">State</rad-input>
</fieldset>
</form>
`;
}
_setValue() {
const form = this.shadowRoot.querySelector('form');
const formData = new FormData(form);
this.internals.setFormValue(formData);
console.log(form, formData.get('line-1'))
}
}
customElements.define('rad-address', RadAddress);
class RadInput extends LitElement {
static get formAssociated() {
return true;
}
static get properties() {
return {
name: { type: String, reflect: true },
required: { type: Boolean, reflect: true },
value: { type: String }
};
}
static get styles() {
return css`
:host {
display: block;
font-family: system-ui;;
margin: 0 0 16px;
}
label {
color: #141414;
display: block;
font-size: 14px;
margin-bottom: 8px;
}
input {
border: 1px solid hsl(250, 100%, 25%);
border-radius: 8px;
color: #141414;
font-size: 16px;
padding: 8px;
}
`;
}
constructor() {
super();
this.internals = this.attachInternals();
this.name = name;
this.required = false;
this.value = '';
this._required = false;
console.log(this.internals);
}
render() {
return html`
<label for="input"><slot></slot></label>
<input type="${this.type}" name="${this.name}" id="input" .value="${this.value}" ?required="${this.required}" @input="${this._onInput}" novalidate>
<span>${this.internals.validationMessage}</span>
`;
}
_onInput(event) {
this.value = event.target.value;
this.internals.setFormValue(this.value);
this._manageRequired();
}
_manageRequired() {
const { value } = this;
const input = this.shadowRoot.querySelector('input');
if (value === '' && this.required) {
this.internals.setValidity({
valueMissing: true
}, 'This field is required', input);
} else {
this.internals.setValidity({});
}
}
/** LitElement lifecycle method */
firstUpdated(...args) {
super.firstUpdated(...args);
/** This ensures our element always participates in the form */
this.internals.setFormValue(this.value);
/** Make sure validations are set up */
this._manageRequired();
}
get required() {
return this._isRequired;
}
set required(isRequired) {
this._required = isRequired;
this.internals.ariaRequired = isRequired;
}
}
customElements.define('rad-input', RadInput);
/** Get a reference to the form */
const form = document.querySelector('form');
/** Get the output element to display form data in */
const output = document.querySelector('output');
/** Prevent default on form submissions */
form.addEventListener('submit', event => {
event.preventDefault();
const form = event.target;
/** Get all of the form data */
const formData = new FormData(form);
const data = {};
formData.forEach((value, key) => data[key] = value);
output.innerHTML = JSON.stringify(data, null, 2);
});
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.