验证表单关联的自定义元素

问题描述 投票:0回答:1

我已经构建了一个简单的 2 个文本字段自定义元素,但无法进行验证以正常工作。没有涵盖这方面的教程。

这是我的自定义元素:


/* name.js */

class MyName extends HTMLElement {

    static formAssociated = true

    constructor() {
        super()
        this.internals = this.attachInternals()
        this.internals.formAssociated = true;
        var shadowRoot = this.attachShadow({mode: 'open', delegatesFocus: true})
        this.data = new FormData()
        this.data.set('firstname', '')
        this.data.set('lastname', '')
        shadowRoot.innerHTML = this.html
    }

    connectedCallback() {
        // any DOM manipulation goes here, run only if not initialised
        if (!this.initialized) {
            console.log('initialised');
            this.onUpdateValue();
            this.shadowRoot.querySelectorAll('input').forEach( el => el.addEventListener('input', event => {
                console.log('content changed')
                event.target.classList.add('show_error');
                this.data.set(event.target.name, event.target.value)
                this.internals.setFormValue(this.data)
                this.onUpdateValue(); // trigger the form validation
                this.internals.setValidity({ customError: true }, 'data not valid');
            }));
            // waits before displaying the element to prevent flickering
            setTimeout(() => this.shadowRoot.querySelector('div').classList.remove('hidden'), 100);
        }
    }

    onUpdateValue() {
        // The Constraint Validation API
        let valid = true;
        const p = this.shadowRoot.querySelector('p.error');
        this.shadowRoot.querySelectorAll('input').forEach(element => {
            if (element.validity.valid === false) valid = false;
        })
        if (valid) {
            this.internals.setValidity({});
        } else {
            console.log('invalid');
            this.internals.setValidity({ customError: true }, 'data not valid');
        }
    }

    get willValidate() { return this.internals.willValidate }
    checkValidity() { return this.internals.checkValidity() }
    reportValidity() {return this.internals.reportValidity() }

    get value() {
        return this.data
    }

    set value(v) {
        if (v instanceof FormData) {
            this.shadowRoot.querySelector('input[name="firstname"]').value = v.get('firstname')
            this.shadowRoot.querySelector('input[name="lastname"]').value = v.get('lastname')
            this.data = v
            this.internals.setFormValue(v)
        }
    }

    formResetCallback() {
        this.data.set('firstname', '')
        this.data.set('lastname', '')
        this.shadowRoot.querySelector('input[name="firstname"]').value = this.data.get('firstname')
        this.shadowRoot.querySelector('input[name="lastname"]').value = this.data.get('lastname')
    }

    formDisabledCallback(isDisabled) {
        this.shadowRoot.querySelector('input[name="firstname"]').disabled = isDisabled
        this.shadowRoot.querySelector('input[name="lastname"]').disabled = isDisabled
    }

    reportValidity() { // expose reportValidity on the CE's surface
        return this._internals.reportValidity();
    }

    static get observedAttributes() {
        return ['value'];
    }

    html = `
<link rel="stylesheet" href="common.css" />
<style>
// removes any inherited css
#container {
    all: initial;
}
// prevents the flicker as the style is applied
.hidden {
    visibility: hidden;
    // display: none;
}
input:invalid {
    border-color: red;
}
</style>
<div class="hidden">
<h2 part="title">Address Form</h2>
<p><input type="text" name="firstname" placeholder="firstname" minlength="5" /></p>
<p><input type="text" name="lastname" placeholder="lastname"  minlength="5" /></p>
<p class="error"></p>
</div>
`

}

customElements.define("my-name", MyName)

我有一个简单的页面,其中包含此自定义元素(name =“foobar”)以及一个提交按钮,其中以下事件触发表单提交:

document.querySelector('form').addEventListener('submit', event => {
    event.preventDefault();
    console.log(document.querySelector('my-name').checkValidity());
    // getting the value of the custom element
    const name = document.querySelector('my-name').value;
    document.querySelector('pre#formdata').textContent = JSON.stringify(Object.fromEntries(name.entries()), null, 2);
})

当我加载页面并单击按钮时,控制台中记录了以下错误:

名称='foobar'的无效表单控件不可聚焦。

我想我明白发生了什么,浏览器正在尝试突出显示自定义元素(但失败了)。我假设这是每个构建这些 FACE 元素的人都面临的问题,但找不到发布的任何解决方案。

任何帮助将不胜感激...

javascript forms validation custom-element
1个回答
0
投票

我在同一条船上。我不知道所有的答案,但我已经差不多了。我还没有让错误消息消失,但已经让元素聚焦并显示错误消息弹出窗口。

您缺少的主要内容是将内部输入元素作为第三个参数包含到

this.internals.setValidity
。这准确地告诉它焦点在哪里。

在组件上暴露

validity
getter 也很好。如果您在表单元素上调用
checkValidity
reportValidity
,浏览器将使用它。如果您有其他方式显示它们,组件的使用者可能希望在表单上设置
novalidate
并在提交时手动调用
checkValidity
以跳过这些本机浏览器弹出错误消息。

您也许能够从 Material 3 的 Web 组件逆向工程出更完整的解决方案。特别要注意的是它们的 mixins 中的表单关联元素和验证,以及各个组件如何在这些 mixins 上实现抽象方法。 复选框几乎没有其他考虑因素。

我从Material comment中发现了 setValidity 调用,内容如下:

当表单尝试提交或调用

delegatesFocus: true

 时,
<input>
form.reportValidity()
 的元素将在 Chrome 和 Safari 中引发错误:“名称 ='' 的无效表单控件不可聚焦”。有效性锚点必须在 
internals.setValidity()
 中提供,并且必须是渲染的 
<input>
 元素。

© www.soinside.com 2019 - 2024. All rights reserved.