Controles de formulario
Controles de formulario para componentes web de Gstock.Todos los componentes web utilizan un shadow DOM para encapsular el marcado, los estilos y el comportamiento. Una salvedad de este enfoque es que los elementos nativos <form> no reconocen los controles de formulario ubicados dentro de un shadow root.
Este problema se resuelve utilizando el evento formdata, que está disponible en todos los navegadores modernos. Esto significa que, cuando se envía un formulario, los controles de formulario de Gstock Web Components agregarán automáticamente sus valores al objeto FormData que se utiliza para enviar el formulario.
En la mayoría de los casos, las cosas “simplemente funcionarán”. Sin embargo, si está utilizando una biblioteca de serialización de formularios, es posible que deba adaptarla para reconocer los controles de formulario de Gstock.
Gstock Web Components utiliza detectores de eventos para interceptar los eventos formdata y submit del formulario. Esto le permite inyectar datos y activar la validación según sea necesario.
Si también está adjuntando un detector de eventos al formulario, debe adjuntarlo después de que los controles de formulario de Gstock Web Components se conecten al DOM, de lo contrario, su lógica se ejecutará antes de que Gstock Web Components tenga la oportunidad de inyectar datos del formulario y validar los controles de formulario.
Serialización de datos
La serialización es solo una palabra elegante para recopilar datos de formulario. Si depende de envíos de formularios estándar, por ejemplo, <form action="...">, probablemente pueda omitir esta sección. Sin embargo, la mayoría de las aplicaciones modernas utilizan la Fetch API o alguna biblioteca para enviar formularios mediante JavaScript.
La interfaz FormData ofrece una forma estándar de serializar formularios en el navegador. Puedes crear un objeto FormData a partir de cualquier elemento <form> como este.
const form = document.querySelector('form');
const data = new FormData(form);
// All form control data is available in a FormData object
Sin embargo, hay a quién le resulta complicado trabajar con FormData o necesita pasar un payload JSON al servidor. Para solucionar esto, Gstock Web Components ofrece una utilidad de serialización que recopila datos de formularios y devuelve un objeto JavaScript simple.
import { serialize } from '@gstock/webcomponents/dist/utils/form.js';
const form = document.querySelector('form');
const data = serialize(form);
// All form control data is available in a plain object
Esto genera un objeto con pares de nombre/valor que se asignan a cada control de formulario. Si más de un control de formulario comparte el mismo nombre, los valores se pasarán como una matriz, por ejemplo, { name: ['value1', 'value2'] }.
Validación de restricciones
La validación del lado del cliente se puede habilitar a través de la API de validación de restricciones del navegador para los controles de formulario de Gstock Web Components. Puede activarla utilizando atributos como required, pattern, minlength, maxlength, etc. Gstock Web Components implementa muchos de los mismos atributos que los controles de formulario nativos, pero consulte la documentación para obtener una lista de propiedades compatibles para cada componente.
Si no desea utilizar la validación del lado del cliente, puede suprimir este comportamiento agregando novalidate al elemento <form> circundante.
Si esta sintaxis le resulta desconocida, ¡no se preocupe! La mayor parte de lo que está aprendiendo en esta página es conocimiento de la plataforma que también se aplica a los controles de formulario regulares.
La validación del lado del cliente se puede utilizar para mejorar la experiencia de usuario de los formularios, pero no reemplaza la validación del lado del servidor. ¡Siempre debes validar y depurar la entrada del usuario en el servidor!
Campos obligatorios
Para que un campo sea obligatorio, utilice el atributo required. Los campos obligatorios recibirán automáticamente un * después de sus etiquetas. Esto se puede configurar mediante la propiedad personalizada --gstock-input-required-content.
El formulario no se enviará si un campo obligatorio está incompleto.
<form>
<gstock-input name="name" label="Name" required></gstock-input>
<br>
<gstock-select label="Favorite Animal" clearable="clearable" required>
<gstock-option value="birds">Birds</gstock-option>
<gstock-option value="cats">Cats</gstock-option>
<gstock-option value="dogs">Dogs</gstock-option>
<gstock-option value="other">Other</gstock-option>
</gstock-select>
<br>
<gstock-textarea name="comment" label="Comment" required></gstock-textarea>
<br>
<gstock-checkbox required>Check me before submitting</gstock-checkbox>
<br>
<br>
<gstock-button type="submit">Submit</gstock-button>
</form>
<script type="module">
const form = document.querySelector('form');
await Promise.all([
customElements.whenDefined('gstock-button'),
customElements.whenDefined('gstock-checkbox'),
customElements.whenDefined('gstock-input'),
customElements.whenDefined('gstock-select'),
customElements.whenDefined('gstock-option'),
customElements.whenDefined('gstock-textarea'),
]).then(() => {
form.addEventListener('submit', event => {
event.preventDefault();
alert('All fields are valid!');
});
});
</script>
Patrones de entrada
Para restringir un valor a un patrón específico, use el atributo pattern. Este ejemplo solo permite las letras de la A a la Z, por lo que el formulario no se enviará si se ingresa un número o un símbolo. Esto solo funciona con elementos <gstock-input>.
<form>
<gstock-input name="letters" required label="Letters" pattern="[A-Za-z]+"></gstock-input>
<br>
<br>
<gstock-button type="submit">Submit</gstock-button>
<gstock-button type="reset" variant="outlined">Reset</gstock-button>
</form>
<script type="module">
const form = document.querySelector('form');
// Wait for controls to be defined before attaching form listeners
await Promise.all([
customElements.whenDefined('gstock-button'),
customElements.whenDefined('gstock-input'),
]).then(() => {
form.addEventListener('submit', event => {
event.preventDefault();
alert('All fields are valid!');
});
});
</script>
Tipos de entrada
Algunos tipos de entrada activarán automáticamente restricciones, como email y url.
<form>
<gstock-input type="email" label="Email" placeholder="you@example.com" required></gstock-input>
<br>
<gstock-input type="url" label="URL" placeholder="https://example.com/" required></gstock-input>
<br>
<br>
<gstock-button type="submit">Submit</gstock-button>
<gstock-button type="reset" variant="outlined">Reset</gstock-button>
</form>
<script type="module">
const form = document.querySelector('form');
// Wait for controls to be defined before attaching form listeners
await Promise.all([
customElements.whenDefined('gstock-button'),
customElements.whenDefined('gstock-input'),
]).then(() => {
form.addEventListener('submit', event => {
event.preventDefault();
alert('All fields are valid!');
});
});
</script>
Errores de validación personalizados
Para crear un error de validación personalizado, pase un string que no esté vacío al método setCustomValidity().
Esto anulará cualquier restricción de validación existente. El formulario no se enviará cuando se establezca una validez personalizada y el navegador mostrará un error de validación cuando se envíe el formulario que lo contiene. Para que el componente vuelva a ser válido, vuelva a llamar a setCustomValidity() con un string vacío.
<form>
<gstock-input label="Type “gstock”" required></gstock-input>
<br>
<br>
<gstock-button type="submit">Submit</gstock-button>
<gstock-button type="reset" variant="outlined">Reset</gstock-button>
</form>
<script type="module">
const form = document.querySelector('form');
const input = form.querySelector('gstock-input');
// Wait for controls to be defined before attaching form listeners
await Promise.all([
customElements.whenDefined('gstock-button'),
customElements.whenDefined('gstock-input'),
]).then(() => {
form.addEventListener('submit', event => {
event.preventDefault();
alert('All fields are valid!');
});
input.addEventListener('gstock-input-event', () => {
if (input.value === 'gstock') {
input.setCustomValidity('');
} else {
input.setCustomValidity("Hey, you're supposed to type 'gstock' before submitting this!");
}
});
});
</script>
La validación personalizada se puede aplicar a cualquier control de formulario que admita el método setCustomValidity(). No se limita a entradas y áreas de texto.
Estilos de validación personalizados
Debido a las muchas formas en que se utilizan los controles de formulario, Gstock no proporciona estilos de validación listos para usar para los controles de formulario como parte de su tema predeterminado. En su lugar, se aplicarán los siguientes atributos para reflejar la validez de un control a medida que los usuarios interactúan con él. Puede usarlos para crear estilos personalizados para cualquiera de los estados de validación que le interesen.
data-required- el control de formulario es obligatoriodata-optional- el control de formulario es opcionaldata-invalid- el control de formulario actualmente no es válidodata-valid- el control de formulario actualmente es válidodata-user-invalid- el control de formulario actualmente no es válido y el usuario ha interactuado con éldata-user-valid- el control de formulario actualmente es válido y el usuario ha interactuado con él
Estos atributos se asignan a las pseudoclases integradas del navegador para la validación: :required, :optional, :invalid, :valid y las propuestas :user-invalid y :user-valid.
En el futuro, los atributos de datos se reemplazarán con pseudoclases personalizadas como :--valid y :--invalid. Gstock está utilizando atributos de datos como solución alternativa hasta que los navegadores admitan estados personalizados a través de ElementInternals.states.
Estilizar controles de formulario no válidos
Puedes utilizar cualquiera de los atributos de datos mencionados anteriormente para determinar la validez, pero normalmente es preferible utilizar data-user-invalid y data-user-valid, ya que se aplican solo después de una interacción del usuario, como escribir o enviar el formulario. Esto evita que los controles de formulario vacíos parezcan no válidos inmediatamente, lo que suele dar como resultado una mala experiencia del usuario.
Este ejemplo muestra estilos de validación personalizados utilizando data-user-invalid y data-user-valid. Prueba a escribir en los campos para ver cómo cambia la validez con la entrada del usuario.
<form>
<gstock-input name="name" label="Name" help-text="What would you like people to call you?" autocomplete="off" required validity-styles></gstock-input>
<br>
<gstock-select name="animal" label="Favorite Animal" help-text="Select the best option." clearable required validity-styles>
<gstock-option value="birds">Birds</gstock-option>
<gstock-option value="cats">Cats</gstock-option>
<gstock-option value="dogs">Dogs</gstock-option>
<gstock-option value="other">Other</gstock-option>
</gstock-select>
<br>
<gstock-checkbox value="accept" required validity-styles>
Accept terms and conditions
</gstock-checkbox>
<br>
<br>
<gstock-button type="submit">Submit</gstock-button>
<gstock-button type="reset" variant="outlined">Reset</gstock-button>
</form>
<script type="module">
const form = document.querySelector('form');
// Wait for controls to be defined before attaching form listeners
await Promise.all([
customElements.whenDefined('gstock-button'),
customElements.whenDefined('gstock-checkbox'),
customElements.whenDefined('gstock-input'),
customElements.whenDefined('gstock-option'),
customElements.whenDefined('gstock-select'),
]).then(() => {
form.addEventListener('submit', event => {
event.preventDefault();
alert('All fields are valid!');
});
});
</script>
Validación de formularios en línea
De forma predeterminada, los controles de formulario de Gstock utilizan los mensajes de error del navegador en forma tooltip-style. No se proporciona ningún mecanismo para mostrar errores en línea, ya que hay demasiadas opiniones sobre cómo funcionaría esto cuando se combina con controles de formulario nativos y otros elementos personalizados. Sin embargo, puede implementar su propia solución utilizando la siguiente técnica.
Para desactivar los mensajes de error del navegador, debe cancelar el evento gstock-invalid-event. Luego, puede aplicar sus propios errores de validación en línea. Este ejemplo demuestra una forma primitiva de hacerlo.
<form>
<gstock-input name="name" label="Name" help-text="What would you like people to call you?" autocomplete="off" required></gstock-input>
<div id="name-error" aria-live="polite" hidden="hidden"></div>
<br>
<br>
<gstock-button type="submit">Submit</gstock-button>
<gstock-button type="reset" variant="outlined">Reset</gstock-button>
</form>
<script type="module">
const form = document.querySelector('form');
const nameError = document.getElementById('name-error');
// Wait for controls to be defined before attaching form listeners
await Promise.all([
customElements.whenDefined('gstock-button'),
customElements.whenDefined('gstock-input'),
]).then(() => {
// A form control is invalid
form.addEventListener(
'gstock-invalid-event',
event => {
// Suppress the browser's constraint validation message
event.preventDefault();
nameError.textContent = `Error: ${event.target.validationMessage}`;
nameError.hidden = false;
event.target.focus();
},
{ capture: true }, // you must use capture since gstock-invalid-event doesn't bubble!
);
// Handle form submit
form.addEventListener('submit', event => {
event.preventDefault();
nameError.hidden = true;
nameError.textContent = '';
setTimeout(() => alert('All fields are valid'), 50);
});
// Handle form reset
form.addEventListener('reset', event => {
nameError.hidden = true;
nameError.textContent = '';
});
});
</script>
<style>
#name-error {
font-size: var(--gstock-input-help-text-font-size-medium);
color: var(--gstock-color-danger-700);
}
#name-error ~ gstock-button {
margin-top: var(--gstock-legacy-spacing-medium);
}
gstock-input {
display: block;
}
/* user invalid styles */
gstock-input[data-user-invalid]::part(base) {
border-color: var(--gstock-color-danger-600);
}
[data-user-invalid]::part(form-control-help-text),
[data-user-invalid]::part(form-control-label) {
color: var(--gstock-color-danger-700);
}
gstock-input:focus-within[data-user-invalid]::part(base) {
border-color: var(--gstock-color-danger-600);
box-shadow: 0 0 0 var(--gstock-focus-ring-width) var(--gstock-color-danger-300);
}
/* User valid styles */
gstock-input[data-user-valid]::part(base) {
border-color: var(--gstock-color-success-600);
}
[data-user-valid]::part(form-control-help-text),
[data-user-valid]::part(form-control-label) {
color: var(--gstock-color-success-700);
}
gstock-input:focus-within[data-user-valid]::part(base) {
border-color: var(--gstock-color-success-600);
box-shadow: 0 0 0 var(--gstock-focus-ring-width) var(--gstock-color-success-300);
}
</style>
Este ejemplo tiene como objetivo demostrar el concepto de proporcionar sus propios mensajes de error en línea. No está pensado para adaptarse a formularios más complejos. Se recomienda a los usuarios que deseen esta funcionalidad que creen una solución de validación más adecuada utilizando las técnicas que se muestran a continuación. Según cómo implemente esta función, los mensajes de error personalizados pueden afectar la accesibilidad de los controles de formulario.
Obtener controles de formulario asociados
En este momento, el uso de HTMLFormElement.elements no devolverá los controles de formulario de Gstock Web Components porque el navegador no es consciente de su estado como controles de formulario de elementos personalizados.
Afortunadamente, Gstock Web Components proporciona una función elements() que hace algo muy similar. Sin embargo, en lugar de devolver una colección HTMLFormControlsCollection, devuelve una matriz de controles de formulario, tanto de HTML como de Gstock Web Components, en el orden en que aparecen en el DOM.
import { getFormControls } from '@gstock/webcomponents/dist/utils/form.js';
const form = document.querySelector('#my-form');
const formControls = getFormControls(form);
console.log(formControls); // e.g. [input, gstock-input, ...]
Probablemente no necesites esta función. Si estás recopilando datos de formularios para enviarlos, probablemente quieras usar Serialización de datos en su lugar.