
Quasar QForm 深度实践表单渲染、子组件内部校验与原生提交控制【免费下载链接】quasarQuasar Framework - Build high-performance VueJS user interfaces in record time项目地址: https://gitcode.com/gh_mirrors/qu/quasarQForm 是 Quasar 框架中负责渲染原生form元素并承担“校验编排器”职责的 Vue 组件它能自动收集表单内所有支持 Quasar 校验 API 的子组件QInput、QSelect、QField 包装组件等统一触发其基于rules的内部校验internal validation并接管焦点管理、submit/reset事件流。读完本文你可以掌握 QForm 的完整用法——基本校验表单、编程式调用validate()、关闭浏览器自动补全、原生提交到 URL、让自定义组件接入 QForm 校验体系以及无障碍a11y行为细节并且能看到每个行为背后在源码中的真实实现位置。什么是 QForm为校验而生的表单容器The QForm component 渲染一个formDOM 元素并允许你轻松校验子表单组件如 QInput、QSelect 或你用 QField 包装的组件前提是这些子组件使用的是内部校验通过rules关联而不是外部校验external validation。从源码结构看这套父子通信机制基于 Vue 的provide/inject实现QForm 通过formKey符号向子树注入一个包含bindComponent/unbindComponent方法的对象子组件挂载时注册自身、卸载时注销自身。相关实现见 QForm 的 provide(formKey) 逻辑以及子组件侧的 useFormChild 组合式函数 和 QFormChildMixin。在使用前官方文档明确要求注意以下几点原文档 WARNING 完整继承QForm 只钩接到 QInput、QSelect 或 QField 包装的组件上这些组件必须使用内部校验rules而不是外部校验validate()方法只运行组件的内部校验即它们的rules。原生 HTML 约束如底层原生 input 上的typeemail或required属性是由浏览器在原生表单提交时强制的validate()不会参考它们——因此请把这些约束也表达为 rules例如:rules[email]如果你想利用reset功能务必同时捕获 QForm 的reset事件并在其 handler 中重置所有被包装组件的 model。校验的执行策略greedy与否QForm 提供greedy属性控制校验策略。QForm.json 的接口定义 说明其默认行为是“在发现第一个无效字段同步校验后即停止”。对照 validate() 源码 可以确认两条执行路径props.greedy为真时用Promise.all(registeredComponents.map(validateComponent))并发校验所有子组件最后过滤出全部无效项——也就是说greedy下会收集到所有错误而不只是第一个默认非 greedy时通过reduce构建一个串行 Promise 链一旦某个组件校验失败if (!r.valid) throw r就中断只聚焦第一个无效组件。此外validateComponent 的防御性处理 值得一提如果某个子组件的validate()同步抛出异常会被视为“校验失败”而不是“表单崩溃”如果返回了 nullish违反契约的validate()同样判为失败——单个坏组件不会破坏整个表单的校验流程。基本用法以下是文档内置的 Basic 示例对应 docs/src/examples/QForm/Basic.vue展示了 QForm 内部校验rules Submit/Reset 按钮 reset手动重置 model 的完整套路div classq-pa-md stylemax-width: 400px q-form submitonSubmit resetonReset classq-gutter-md q-input filled v-modelname labelYour name * hintName and surname lazy-rules :rules[val (val val.length 0) || Please type something] / q-input filled typenumber v-model.numberage labelYour age * lazy-rules :rules[ val (val ! null val ! ) || Please type your age, val (val 0 val 100) || Please type a real age ] / q-toggle v-modelaccept labelI accept the license and terms / div q-btn labelSubmit typesubmit colorprimary / q-btn labelReset typereset colorprimary flat classq-ml-sm / /div /q-form /div// script setup import { useQuasar } from quasar import { ref } from vue const $q useQuasar() const name ref(null) const age ref(null) const accept ref(false) function onSubmit() { if (accept.value ! true) { $q.notify({ color: red-5, textColor: white, icon: warning, message: You need to accept the license and terms first }) } else { $q.notify({ color: green-4, textColor: white, icon: cloud_done, message: Submitted }) } } function onReset() { name.value null age.value null accept.value false }几个值得注意的细节两个q-input都使用了lazy-rules——只在校验被触发后而非输入过程中才应用规则避免用户还没填完就被标红注意q-toggle上的accept并没有走 QForm 的rules体系而是靠submithandler 里的手动判断未接受时用$q.notify报警告onReset中手动把三个 model 归零正是前文 WARNING 中“捕获reset并重置所有被包装组件 model”这一要求的落地写法。用 QBtn 激活 submit 与 reset为了让用户能够激活表单上的submit或reset事件创建一个type设为submit或reset的 QBtndiv q-btn labelSubmit typesubmit colorprimary / q-btn labelReset typereset colorprimary flat classq-ml-sm / /div从 submit() 的源码实现 看其内部流程是先stopAndPrevent(evt)阻止默认提交行为然后调用validate()当且仅当本次提交未“过期”index validateIndex即期间没有触发新的校验且校验通过时若组件上绑定了onSubmitprop即submit监听器存在则emit(submit, evt)否则调用evt.target.submit()走原生提交。这解释了为什么submit只在所有校验通过后才会被调用。编程式校验validate() 与 resetValidation()除了按钮触发还可以给 QForm 一个 Vue ref直接调用validate和resetValidation函数。Composition API 写法// q-form refmyFormRef setup () { const myFormRef useTemplateRef(myFormRef) function validate () { myFormRef.value.validate().then(success { if (success) { // yay, models are correct } else { // oh no, user has filled in // at least one invalid value } }) } // to reset validations: function reset () { myFormRef.value.resetValidation() } return { // ... } }Options API 写法// q-form refmyForm this.$refs.myForm.validate().then(success { if (success) { // yay, models are correct } else { // oh no, user has filled in // at least one value is invalid } }) // to reset validations: this.$refs.myForm.resetValidation()根据 接口定义validate(shouldFocus)触发对表单内所有适用 Quasar 组件的校验返回一个总是被 fulfill的Promisebooleantrue表示校验成功false表示检测到无效 model。可选参数shouldFocusBoolean指定是否聚焦到出错组件指定时会覆盖no-error-focuspropresetValidation()重置表单内所有适用组件的校验状态。从 resetValidation 源码 可见它先自增validateIndex使所有进行中的旧校验结果“过期”避免旧结果覆盖新结果然后遍历已注册组件逐一调用其resetValidation()若存在。校验触发时还会发出两个事件见 events 定义validation-success校验触发后所有内部 Quasar 组件的 model 均有效validation-error校验触发后至少一个内部组件的 model 无效事件携带ref参数——第一个触发校验错误的组件实例引用。校验失败后的焦点处理同样有源码依据在 validate() 的错误分支 中若未设置no-error-focusQForm 会从错误列表中找到第一个已挂载且未销毁、实现了focus方法的组件并调用focus()把键盘焦点移到第一个无效字段上。关闭浏览器自动补全如果你希望关闭部分浏览器对表单内所有 input 元素使用的自动纠错或拼写检查行为可以给 QForm 组件添加这些纯 HTML 属性autocorrectoff autocapitalizeoff autocompleteoff spellcheckfalse由于 QForm 直接渲染为form元素这些属性会原样落到 DOM 上由浏览器自身解释。原生提交到 URLNative Form Submit如果你在 QForm 上使用原生的action和method属性请记住必须给每个 Quasar 表单组件使用nameprop这样实际发送的 formData 才会包含用户填写的内容q-form actionhttps://some-url.com methodpost q-input namefirstname ... !-- ... -- /q-form行为规则如下这些规则与 submit() 源码 的分支逻辑一一对应通过设置 QForm 的action、method、enctype和target属性来控制表单的提交方式如果 QForm 上没有submit监听器则校验成功后表单会自动进行原生提交源码中即evt.target.submit()分支如果 QForm 上存在submit监听器则校验成功后会调用该监听器。此时若要继续执行原生提交需要手动触发q-form actionhttps://some-url.com methodpost submit.preventonSubmit q-input namefirstname ... !-- ... -- /q-formmethods: { onSubmit (evt) { console.log(submit - do something here, evt) evt.target.submit() } }自定义子组件接入 QFormChild communication默认情况下所有 Quasar 表单组件都会与父 QForm 实例通信。如果出于某种原因你在创建自己的表单组件且不包装 Quasar 表单组件可以通过以下方式让 QForm 感知到它。Composition APIimport { useFormChild } from quasar setup () { // function validate () { ... } useFormChild({ validate, // Function; Can be async; // Should return a Boolean (or a Promise resolving to a Boolean) resetValidation, // Optional function which resets validation requiresQForm: true // should it error out if no parent QForm is found? }) }Options APIimport { QFormChildMixin } from quasar // some component export default { mixins: [ QFormChildMixin ], methods: { // required! should return a Boolean // or a Promise resolving to a Boolean validate () { console.log(called my-comp.validate()) return true }, // optional function resetValidation () { // ... } }, // ... }从 useFormChild 源码 可以看到其内部契约通过inject(formKey, false)获取父级 QForm若拿不到且requiresQForm为真则console.error(Parent QForm not found on useFormChild()!)通过Object.assign(proxy, { validate, resetValidation })把你的validate/resetValidation暴露为组件实例上的公开方法——这正是 QForm 遍历时能够调用的入口QFormChildMixin 中也定义了同名的空占位方法onMounted时若disable不为真调用$form.bindComponent(proxy)注册自己onBeforeUnmount时注销还 watch 了props.disable一旦组件被禁用就调用resetValidation()并从 QForm 解绑恢复启用时重新绑定——被禁用的字段不会参与表单校验。Options API 的 QFormChildMixin 以相同的方式工作mounted钩子里向this.$.provides[formKey]注册beforeUnmount注销并 watchdisable做相同的解绑/绑定处理。另外QForm 实例还提供了getValidationComponents()方法见 方法定义 与 暴露逻辑返回一个支持 Quasar 校验 API 的子组件实例数组来自 QField 派生或使用了useFormChild()/QFormChildMixin的组件便于你自行遍历处理校验组件。无障碍Accessibilityv2.25QForm 渲染的是原生form元素因此浏览器的内置表单语义包括按Enter隐式提交原样生效。围绕无障碍的关键行为校验失败时聚焦QForm 会把键盘焦点移动到第一个无效字段可用no-error-focusprop 关闭对应 validate() 中的 focus 分支错误播报屏幕阅读器通过该字段自身的rolealert消息获取其错误——参见 QField 文档的 Accessibility 一节autofocus prop表单挂载时聚焦第一个[autofocus]元素找不到则回退到第一个可聚焦元素。从 focus() 的源码 可见其选择器策略是逐级降级先找[autofocus][tabindex]/[data-autofocus][tabindex]再找[autofocus] [tabindex]再找不带 tabindex 的[autofocus]/[data-autofocus]最后find第一个tabIndex ! -1的[tabindex]元素没有聚合错误摘要屏幕阅读器用户听到的是“获得焦点的那个字段”的 alert而不是“总共多少字段失败”。对于长表单官方建议在校验失败时自行渲染一个 live region例如 “3 fields need attention”。另外两个相关 propno-reset-focusreset 表单时不聚焦第一个组件与greedy见前文。reset 流程从 reset() 源码 看先emit(reset)给业务代码重置 model 的机会再在nextTick中调用resetValidation()并在autofocus且未设置no-reset-focus时重新聚焦——顺序保证了用户态先于校验态重置。关键文件索引内容路径QForm 组件实现validate/submit/reset/focus/子组件注册ui/src/components/form/QForm.jsQForm props / events / methods 接口定义ui/src/components/form/QForm.jsonOptions API 混入实现ui/src/components/form/QFormChildMixin.jsComposition API 组合式函数实现ui/src/composables/use-form/use-form-child.js官方 Basic 示例docs/src/examples/QForm/Basic.vue相关文档QField / QInput / QSelectfield.md、input.md、select.md【免费下载链接】quasarQuasar Framework - Build high-performance VueJS user interfaces in record time项目地址: https://gitcode.com/gh_mirrors/qu/quasar创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考