[{"data":1,"prerenderedAt":1306},["ShallowReactive",2],{"content-\u002Fdocs\u002Fcross-cutting-state\u002Finject-form":3},{"id":4,"title":5,"body":6,"description":1285,"extension":1286,"meta":1287,"metaRows":1288,"navigation":128,"path":1301,"seo":1302,"source":1303,"stem":1304,"__hash__":1305},"docs\u002Fdocs\u002Fcross-cutting-state\u002Finject-form.md","injectForm",{"type":7,"value":8,"toc":1272},"minimark",[9,16,26,29,44,49,54,61,318,321,461,475,479,482,644,671,681,684,726,732,740,757,809,823,829,908,915,919,928,963,971,975,978,996,999,1013,1040,1053,1158,1164,1214,1221,1230,1234,1268],[10,11,13],"h1",{"id":12},"injectform",[14,15,5],"code",{},[17,18,19],"blockquote",{},[20,21,22,23,25],"p",{},"Reach any registered form from any descendant. Call ",[14,24,5],{}," and get the same handle back. Ambient resolution for the parent's own form, keyed resolution for distant ones.",[27,28],"docs-meta-table",{},[20,30,31,32,35,36,39,40,43],{},"The parent owns the form and renders the email field. ",[14,33,34],{},"ProfileFieldset.vue"," and ",[14,37,38],{},"StatusPill.vue"," are sibling SFCs that each call ",[14,41,42],{},"injectForm('docs-demo-inject-form')"," to reach the same form: no props passed, same reactive surface. Both children would run unchanged regardless of how deep they sat in the tree.",[45,46],"docs-demo",{"label":47,"slug":48},"Form Injection Demo","inject-form",[50,51,53],"h2",{"id":52},"the-common-case-ambient-resolution","The common case: ambient resolution",[20,55,56,57,60],{},"Parent owns the form (no ",[14,58,59],{},"key","):",[62,63,68],"pre",{"className":64,"code":65,"language":66,"meta":67,"style":67},"language-vue shiki shiki-themes github-light github-dark","\u003C!-- SignupForm.vue -->\n\u003Cscript setup lang=\"ts\">\n  import { useForm } from 'attaform\u002Fzod'\n\n  const form = useForm\u003CForm>({ schema })\n  const onSubmit = form.handleSubmit(async (values) => {\n    await api.post('\u002Fsignup', values)\n  })\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cform @submit.prevent=\"onSubmit\">\n    \u003CEmailRow \u002F>\n    \u003CProfileGroup \u002F>\n    \u003Cbutton>Sign up\u003C\u002Fbutton>\n  \u003C\u002Fform>\n\u003C\u002Ftemplate>\n","vue","",[14,69,70,79,107,123,130,154,192,212,218,228,233,243,262,274,284,299,309],{"__ignoreMap":67},[71,72,75],"span",{"class":73,"line":74},"line",1,[71,76,78],{"class":77},"sJ8bj","\u003C!-- SignupForm.vue -->\n",[71,80,82,86,90,94,97,100,104],{"class":73,"line":81},2,[71,83,85],{"class":84},"sVt8B","\u003C",[71,87,89],{"class":88},"s9eBZ","script",[71,91,93],{"class":92},"sScJk"," setup",[71,95,96],{"class":92}," lang",[71,98,99],{"class":84},"=",[71,101,103],{"class":102},"sZZnC","\"ts\"",[71,105,106],{"class":84},">\n",[71,108,110,114,117,120],{"class":73,"line":109},3,[71,111,113],{"class":112},"szBVR","  import",[71,115,116],{"class":84}," { useForm } ",[71,118,119],{"class":112},"from",[71,121,122],{"class":102}," 'attaform\u002Fzod'\n",[71,124,126],{"class":73,"line":125},4,[71,127,129],{"emptyLinePlaceholder":128},true,"\n",[71,131,133,136,140,143,146,148,151],{"class":73,"line":132},5,[71,134,135],{"class":112},"  const",[71,137,139],{"class":138},"sj4cs"," form",[71,141,142],{"class":112}," =",[71,144,145],{"class":92}," useForm",[71,147,85],{"class":84},[71,149,150],{"class":92},"Form",[71,152,153],{"class":84},">({ schema })\n",[71,155,157,159,162,164,167,170,173,176,179,183,186,189],{"class":73,"line":156},6,[71,158,135],{"class":112},[71,160,161],{"class":138}," onSubmit",[71,163,142],{"class":112},[71,165,166],{"class":84}," form.",[71,168,169],{"class":92},"handleSubmit",[71,171,172],{"class":84},"(",[71,174,175],{"class":112},"async",[71,177,178],{"class":84}," (",[71,180,182],{"class":181},"s4XuR","values",[71,184,185],{"class":84},") ",[71,187,188],{"class":112},"=>",[71,190,191],{"class":84}," {\n",[71,193,195,198,201,204,206,209],{"class":73,"line":194},7,[71,196,197],{"class":112},"    await",[71,199,200],{"class":84}," api.",[71,202,203],{"class":92},"post",[71,205,172],{"class":84},[71,207,208],{"class":102},"'\u002Fsignup'",[71,210,211],{"class":84},", values)\n",[71,213,215],{"class":73,"line":214},8,[71,216,217],{"class":84},"  })\n",[71,219,221,224,226],{"class":73,"line":220},9,[71,222,223],{"class":84},"\u003C\u002F",[71,225,89],{"class":88},[71,227,106],{"class":84},[71,229,231],{"class":73,"line":230},10,[71,232,129],{"emptyLinePlaceholder":128},[71,234,236,238,241],{"class":73,"line":235},11,[71,237,85],{"class":84},[71,239,240],{"class":88},"template",[71,242,106],{"class":84},[71,244,246,249,252,255,257,260],{"class":73,"line":245},12,[71,247,248],{"class":84},"  \u003C",[71,250,251],{"class":88},"form",[71,253,254],{"class":92}," @submit.prevent",[71,256,99],{"class":84},[71,258,259],{"class":102},"\"onSubmit\"",[71,261,106],{"class":84},[71,263,265,268,271],{"class":73,"line":264},13,[71,266,267],{"class":84},"    \u003C",[71,269,270],{"class":88},"EmailRow",[71,272,273],{"class":84}," \u002F>\n",[71,275,277,279,282],{"class":73,"line":276},14,[71,278,267],{"class":84},[71,280,281],{"class":88},"ProfileGroup",[71,283,273],{"class":84},[71,285,287,289,292,295,297],{"class":73,"line":286},15,[71,288,267],{"class":84},[71,290,291],{"class":88},"button",[71,293,294],{"class":84},">Sign up\u003C\u002F",[71,296,291],{"class":88},[71,298,106],{"class":84},[71,300,302,305,307],{"class":73,"line":301},16,[71,303,304],{"class":84},"  \u003C\u002F",[71,306,251],{"class":88},[71,308,106],{"class":84},[71,310,312,314,316],{"class":73,"line":311},17,[71,313,223],{"class":84},[71,315,240],{"class":88},[71,317,106],{"class":84},[20,319,320],{},"Any descendant grabs the same form:",[62,322,324],{"className":64,"code":323,"language":66,"meta":67,"style":67},"\u003C!-- EmailRow.vue -->\n\u003Cscript setup lang=\"ts\">\n  import { injectForm } from 'attaform\u002Fzod'\n\n  const form = injectForm\u003CForm>()\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Clabel>Email\u003C\u002Flabel>\n  \u003Cinput v-register=\"form?.register('email')\" \u002F>\n  \u003Cem v-if=\"form?.fields.email.showErrors\">{{ form?.fields.email.firstError?.message }}\u003C\u002Fem>\n\u003C\u002Ftemplate>\n",[14,325,326,331,347,358,362,380,388,392,400,414,431,453],{"__ignoreMap":67},[71,327,328],{"class":73,"line":74},[71,329,330],{"class":77},"\u003C!-- EmailRow.vue -->\n",[71,332,333,335,337,339,341,343,345],{"class":73,"line":81},[71,334,85],{"class":84},[71,336,89],{"class":88},[71,338,93],{"class":92},[71,340,96],{"class":92},[71,342,99],{"class":84},[71,344,103],{"class":102},[71,346,106],{"class":84},[71,348,349,351,354,356],{"class":73,"line":109},[71,350,113],{"class":112},[71,352,353],{"class":84}," { injectForm } ",[71,355,119],{"class":112},[71,357,122],{"class":102},[71,359,360],{"class":73,"line":125},[71,361,129],{"emptyLinePlaceholder":128},[71,363,364,366,368,370,373,375,377],{"class":73,"line":132},[71,365,135],{"class":112},[71,367,139],{"class":138},[71,369,142],{"class":112},[71,371,372],{"class":92}," injectForm",[71,374,85],{"class":84},[71,376,150],{"class":92},[71,378,379],{"class":84},">()\n",[71,381,382,384,386],{"class":73,"line":156},[71,383,223],{"class":84},[71,385,89],{"class":88},[71,387,106],{"class":84},[71,389,390],{"class":73,"line":194},[71,391,129],{"emptyLinePlaceholder":128},[71,393,394,396,398],{"class":73,"line":214},[71,395,85],{"class":84},[71,397,240],{"class":88},[71,399,106],{"class":84},[71,401,402,404,407,410,412],{"class":73,"line":220},[71,403,248],{"class":84},[71,405,406],{"class":88},"label",[71,408,409],{"class":84},">Email\u003C\u002F",[71,411,406],{"class":88},[71,413,106],{"class":84},[71,415,416,418,421,424,426,429],{"class":73,"line":230},[71,417,248],{"class":84},[71,419,420],{"class":88},"input",[71,422,423],{"class":92}," v-register",[71,425,99],{"class":84},[71,427,428],{"class":102},"\"form?.register('email')\"",[71,430,273],{"class":84},[71,432,433,435,438,441,443,446,449,451],{"class":73,"line":235},[71,434,248],{"class":84},[71,436,437],{"class":88},"em",[71,439,440],{"class":92}," v-if",[71,442,99],{"class":84},[71,444,445],{"class":102},"\"form?.fields.email.showErrors\"",[71,447,448],{"class":84},">{{ form?.fields.email.firstError?.message }}\u003C\u002F",[71,450,437],{"class":88},[71,452,106],{"class":84},[71,454,455,457,459],{"class":73,"line":245},[71,456,223],{"class":84},[71,458,240],{"class":88},[71,460,106],{"class":84},[20,462,463,464,466,467,470,471,474],{},"You supply the ",[14,465,150],{}," generic; Vue's injection system erases it, so Attaform can't recover the shape on your behalf. Other than that, ",[14,468,469],{},"injectForm\u003CForm>()"," returns a form type-identical to ",[14,472,473],{},"useForm","'s return.",[50,476,478],{"id":477},"reaching-a-form-that-isnt-an-ancestor","Reaching a form that isn't an ancestor",[20,480,481],{},"Floating save buttons, sidebar status widgets, anything in a different branch of the component tree:",[62,483,485],{"className":64,"code":484,"language":66,"meta":67,"style":67},"\u003C!-- FloatingSaveButton.vue (anywhere in the app) -->\n\u003Cscript setup lang=\"ts\">\n  import { injectForm } from 'attaform\u002Fzod'\n\n  const form = injectForm\u003CForm>('signup')\n  const onSave = () => form?.handleSubmit(async (values) => api.post('\u002Fsignup', values))()\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cbutton :disabled=\"!form?.meta.dirty || form?.meta.submitting\" @click=\"onSave\">Save\u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n",[14,486,487,492,508,518,522,545,587,595,599,607,636],{"__ignoreMap":67},[71,488,489],{"class":73,"line":74},[71,490,491],{"class":77},"\u003C!-- FloatingSaveButton.vue (anywhere in the app) -->\n",[71,493,494,496,498,500,502,504,506],{"class":73,"line":81},[71,495,85],{"class":84},[71,497,89],{"class":88},[71,499,93],{"class":92},[71,501,96],{"class":92},[71,503,99],{"class":84},[71,505,103],{"class":102},[71,507,106],{"class":84},[71,509,510,512,514,516],{"class":73,"line":109},[71,511,113],{"class":112},[71,513,353],{"class":84},[71,515,119],{"class":112},[71,517,122],{"class":102},[71,519,520],{"class":73,"line":125},[71,521,129],{"emptyLinePlaceholder":128},[71,523,524,526,528,530,532,534,536,539,542],{"class":73,"line":132},[71,525,135],{"class":112},[71,527,139],{"class":138},[71,529,142],{"class":112},[71,531,372],{"class":92},[71,533,85],{"class":84},[71,535,150],{"class":92},[71,537,538],{"class":84},">(",[71,540,541],{"class":102},"'signup'",[71,543,544],{"class":84},")\n",[71,546,547,549,552,554,557,559,562,564,566,568,570,572,574,576,578,580,582,584],{"class":73,"line":156},[71,548,135],{"class":112},[71,550,551],{"class":92}," onSave",[71,553,142],{"class":112},[71,555,556],{"class":84}," () ",[71,558,188],{"class":112},[71,560,561],{"class":84}," form?.",[71,563,169],{"class":92},[71,565,172],{"class":84},[71,567,175],{"class":112},[71,569,178],{"class":84},[71,571,182],{"class":181},[71,573,185],{"class":84},[71,575,188],{"class":112},[71,577,200],{"class":84},[71,579,203],{"class":92},[71,581,172],{"class":84},[71,583,208],{"class":102},[71,585,586],{"class":84},", values))()\n",[71,588,589,591,593],{"class":73,"line":194},[71,590,223],{"class":84},[71,592,89],{"class":88},[71,594,106],{"class":84},[71,596,597],{"class":73,"line":214},[71,598,129],{"emptyLinePlaceholder":128},[71,600,601,603,605],{"class":73,"line":220},[71,602,85],{"class":84},[71,604,240],{"class":88},[71,606,106],{"class":84},[71,608,609,611,613,616,618,621,624,626,629,632,634],{"class":73,"line":230},[71,610,248],{"class":84},[71,612,291],{"class":88},[71,614,615],{"class":92}," :disabled",[71,617,99],{"class":84},[71,619,620],{"class":102},"\"!form?.meta.dirty || form?.meta.submitting\"",[71,622,623],{"class":92}," @click",[71,625,99],{"class":84},[71,627,628],{"class":102},"\"onSave\"",[71,630,631],{"class":84},">Save\u003C\u002F",[71,633,291],{"class":88},[71,635,106],{"class":84},[71,637,638,640,642],{"class":73,"line":235},[71,639,223],{"class":84},[71,641,240],{"class":88},[71,643,106],{"class":84},[20,645,646,647,649,650,653,654,656,657,660,661,664,665,670],{},"Pass the same ",[14,648,59],{}," you passed to ",[14,651,652],{},"useForm({ key: 'signup' })",". If no form is registered under that key when the component mounts, ",[14,655,5],{}," returns ",[14,658,659],{},"null"," and dev mode logs the unresolved key at the call site. Reach for the form with ",[14,662,663],{},"?."," at every consumption site so a missing form degrades to no-ops instead of crashing. See ",[666,667,669],"a",{"href":668},"#when-resolution-fails","When resolution fails"," below.",[50,672,674,675,677,678,680],{"id":673},"do-i-need-to-pass-a-key-to-useform","Do I need to pass a ",[14,676,59],{}," to ",[14,679,473],{},"?",[20,682,683],{},"The two resolution modes are cleanly split:",[685,686,687,705],"ul",{},[688,689,690,697,698,701,702,704],"li",{},[691,692,693,694,696],"strong",{},"Anonymous (no ",[14,695,59],{},") → ambient access."," ",[14,699,700],{},"useForm({ schema })"," fills the parent's ambient slot. Any descendant's ",[14,703,469],{}," (no key) resolves to it; closest ancestor wins when nested.",[688,706,707,697,714,717,718,721,722,725],{},[691,708,709,710,713],{},"Keyed (",[14,711,712],{},"key: 'x'",") → explicit access only.",[14,715,716],{},"useForm({ schema, key: 'x' })"," registers the form under ",[14,719,720],{},"'x'"," but does NOT fill the ambient slot. Descendants reach it via ",[14,723,724],{},"injectForm\u003CForm>('x')",", not via the no-key form.",[20,727,728,729,731],{},"Skip ",[14,730,59],{}," for single-component one-off forms (login modal, settings panel). Supply one when you want cross-component lookup, multi-call-site shared state, a stable persistence default, or a legible DevTools label.",[733,734,736,737,739],"h3",{"id":735},"gotcha-multiple-anonymous-useform-in-one-component","Gotcha: multiple anonymous ",[14,738,473],{}," in one component",[20,741,742,743,746,747,750,751,753,754,756],{},"Vue's ",[14,744,745],{},"provide"," \u002F ",[14,748,749],{},"inject"," is last-write-wins per component. If a parent calls ",[14,752,473],{}," twice without keys, the second overwrites the first in the ambient slot, and descendants using ",[14,755,469],{}," only see the second.",[62,758,762],{"className":759,"code":760,"language":761,"meta":67,"style":67},"language-ts shiki shiki-themes github-light github-dark","\u002F\u002F Parent component\nconst formA = useForm({ schema: schemaA }) \u002F\u002F provides ambient → A\nconst formB = useForm({ schema: schemaB }) \u002F\u002F provides ambient → B (overwrites A)\n\u002F\u002F Descendants' injectForm\u003CForm>() reads B. A is unreachable via ambient.\n","ts",[14,763,764,769,787,804],{"__ignoreMap":67},[71,765,766],{"class":73,"line":74},[71,767,768],{"class":77},"\u002F\u002F Parent component\n",[71,770,771,774,777,779,781,784],{"class":73,"line":81},[71,772,773],{"class":112},"const",[71,775,776],{"class":138}," formA",[71,778,142],{"class":112},[71,780,145],{"class":92},[71,782,783],{"class":84},"({ schema: schemaA }) ",[71,785,786],{"class":77},"\u002F\u002F provides ambient → A\n",[71,788,789,791,794,796,798,801],{"class":73,"line":109},[71,790,773],{"class":112},[71,792,793],{"class":138}," formB",[71,795,142],{"class":112},[71,797,145],{"class":92},[71,799,800],{"class":84},"({ schema: schemaB }) ",[71,802,803],{"class":77},"\u002F\u002F provides ambient → B (overwrites A)\n",[71,805,806],{"class":73,"line":125},[71,807,808],{"class":77},"\u002F\u002F Descendants' injectForm\u003CForm>() reads B. A is unreachable via ambient.\n",[20,810,811,812,815,816,818,819,822],{},"The runtime emits a dev-mode ",[14,813,814],{},"console.warn"," lazily, when (and only when) a descendant actually consumes the ambient slot via ",[14,817,469],{}," with no key. The warning lists each anonymous ",[14,820,821],{},"useForm()"," call by source frame so you can navigate to the offending sites.",[20,824,825,828],{},[691,826,827],{},"Fix:"," give each form a key (which removes them from the ambient slot entirely) and look them up explicitly:",[62,830,832],{"className":759,"code":831,"language":761,"meta":67,"style":67},"useForm({ schema: schemaA, key: 'a' })\nuseForm({ schema: schemaB, key: 'b' })\n\u002F\u002F Descendants:\nconst a = injectForm\u003CFormA>('a')\nconst b = injectForm\u003CFormB>('b')\n",[14,833,834,847,859,864,886],{"__ignoreMap":67},[71,835,836,838,841,844],{"class":73,"line":74},[71,837,473],{"class":92},[71,839,840],{"class":84},"({ schema: schemaA, key: ",[71,842,843],{"class":102},"'a'",[71,845,846],{"class":84}," })\n",[71,848,849,851,854,857],{"class":73,"line":81},[71,850,473],{"class":92},[71,852,853],{"class":84},"({ schema: schemaB, key: ",[71,855,856],{"class":102},"'b'",[71,858,846],{"class":84},[71,860,861],{"class":73,"line":109},[71,862,863],{"class":77},"\u002F\u002F Descendants:\n",[71,865,866,868,871,873,875,877,880,882,884],{"class":73,"line":125},[71,867,773],{"class":112},[71,869,870],{"class":138}," a",[71,872,142],{"class":112},[71,874,372],{"class":92},[71,876,85],{"class":84},[71,878,879],{"class":92},"FormA",[71,881,538],{"class":84},[71,883,843],{"class":102},[71,885,544],{"class":84},[71,887,888,890,893,895,897,899,902,904,906],{"class":73,"line":132},[71,889,773],{"class":112},[71,891,892],{"class":138}," b",[71,894,142],{"class":112},[71,896,372],{"class":92},[71,898,85],{"class":84},[71,900,901],{"class":92},"FormB",[71,903,538],{"class":84},[71,905,856],{"class":102},[71,907,544],{"class":84},[20,909,910,911,914],{},"Mixing modes is fine; keyed forms don't interfere with an ambient sibling. A parent with three keyed forms plus one anonymous form produces no warning; the descendant's ",[14,912,913],{},"injectForm\u003CF>()"," unambiguously resolves to the (only) anonymous one.",[50,916,918],{"id":917},"compound-vs-single-purpose","Compound vs. single-purpose",[20,920,921,922,35,924,927],{},"For components that touch one field, both ",[14,923,5],{},[14,925,926],{},"useRegister"," work. The choice:",[685,929,930,941],{},[688,931,932,936,937,940],{},[691,933,934],{},[14,935,926],{},": single-field child that takes ",[14,938,939],{},"v-register"," from its consumer. Use when the parent decides which path the child binds to.",[688,942,943,947,948,951,952,955,956,955,959,962],{},[691,944,945],{},[14,946,5],{},": child that touches one or more specific paths the parent doesn't declare. Use when the path is the child's responsibility (an ",[14,949,950],{},"AddressBlock"," always binds ",[14,953,954],{},"address.street",", ",[14,957,958],{},"address.city",[14,960,961],{},"address.zip",").",[20,964,965,967,968,970],{},[14,966,926],{}," doesn't accept a key or path; it's a single-purpose ambient hook. Compound use cases belong on ",[14,969,5],{},".",[50,972,974],{"id":973},"lifetime","Lifetime",[20,976,977],{},"Both resolution modes ref-count on the form's registry entry. In practice:",[685,979,980,983,986],{},[688,981,982],{},"The form survives until every component that reached it unmounts.",[688,984,985],{},"Cleanup is automatic; no explicit dispose call from the consumer.",[688,987,988,989,992,993,995],{},"A form accessed only by ",[14,990,991],{},"injectForm(key)"," stays alive as long as at least one consumer is mounted, even if the original ",[14,994,473],{}," owner unmounted first.",[50,997,669],{"id":998},"when-resolution-fails",[20,1000,1001,656,1003,1006,1007,1009,1010,1012],{},[14,1002,5],{},[14,1004,1005],{},"T | null"," rather than throwing, so descendants are robust to mount-order quirks (children rendered before the parent's ",[14,1008,473],{}," runs, conditional ancestors, dynamic imports). Two cases produce ",[14,1011,659],{},":",[685,1014,1015,1031],{},[688,1016,1017,1020,1021,1024,1025,1027,1028,1030],{},[691,1018,1019],{},"No ambient form",": ",[14,1022,1023],{},"injectForm()"," with no ancestor ",[14,1026,473],{}," and no key. Returns ",[14,1029,659],{}," silently. Ambient lookup is opportunistic, so a downstream component library reading the ambient slot stays quiet in trees that don't have a form rather than spamming consumers' consoles.",[688,1032,1033,1020,1036,1039],{},[691,1034,1035],{},"Key not registered",[14,1037,1038],{},"injectForm('key-name')"," but nothing is registered under that key. Dev mode logs the unresolved key at the call site.",[20,1041,1042,1043,1045,1046,1048,1049,1052],{},"Keep the return as ",[14,1044,1005],{}," and reach for it with ",[14,1047,663],{}," at the consumption sites. The directive accepts ",[14,1050,1051],{},"undefined"," peacefully, optional chains short-circuit cleanly through reactive reads, and the child still works the moment the form becomes available:",[62,1054,1056],{"className":64,"code":1055,"language":66,"meta":67,"style":67},"\u003Cscript setup lang=\"ts\">\n  const ctx = injectForm\u003CForm>('signup')\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cinput v-register=\"ctx?.register('email')\" \u002F>\n  \u003Cem v-if=\"ctx?.fields.email.showErrors\">{{ ctx?.fields.email.firstError?.message }}\u003C\u002Fem>\n\u003C\u002Ftemplate>\n",[14,1057,1058,1074,1095,1103,1107,1115,1130,1150],{"__ignoreMap":67},[71,1059,1060,1062,1064,1066,1068,1070,1072],{"class":73,"line":74},[71,1061,85],{"class":84},[71,1063,89],{"class":88},[71,1065,93],{"class":92},[71,1067,96],{"class":92},[71,1069,99],{"class":84},[71,1071,103],{"class":102},[71,1073,106],{"class":84},[71,1075,1076,1078,1081,1083,1085,1087,1089,1091,1093],{"class":73,"line":81},[71,1077,135],{"class":112},[71,1079,1080],{"class":138}," ctx",[71,1082,142],{"class":112},[71,1084,372],{"class":92},[71,1086,85],{"class":84},[71,1088,150],{"class":92},[71,1090,538],{"class":84},[71,1092,541],{"class":102},[71,1094,544],{"class":84},[71,1096,1097,1099,1101],{"class":73,"line":109},[71,1098,223],{"class":84},[71,1100,89],{"class":88},[71,1102,106],{"class":84},[71,1104,1105],{"class":73,"line":125},[71,1106,129],{"emptyLinePlaceholder":128},[71,1108,1109,1111,1113],{"class":73,"line":132},[71,1110,85],{"class":84},[71,1112,240],{"class":88},[71,1114,106],{"class":84},[71,1116,1117,1119,1121,1123,1125,1128],{"class":73,"line":156},[71,1118,248],{"class":84},[71,1120,420],{"class":88},[71,1122,423],{"class":92},[71,1124,99],{"class":84},[71,1126,1127],{"class":102},"\"ctx?.register('email')\"",[71,1129,273],{"class":84},[71,1131,1132,1134,1136,1138,1140,1143,1146,1148],{"class":73,"line":194},[71,1133,248],{"class":84},[71,1135,437],{"class":88},[71,1137,440],{"class":92},[71,1139,99],{"class":84},[71,1141,1142],{"class":102},"\"ctx?.fields.email.showErrors\"",[71,1144,1145],{"class":84},">{{ ctx?.fields.email.firstError?.message }}\u003C\u002F",[71,1147,437],{"class":88},[71,1149,106],{"class":84},[71,1151,1152,1154,1156],{"class":73,"line":214},[71,1153,223],{"class":84},[71,1155,240],{"class":88},[71,1157,106],{"class":84},[20,1159,1160,1161,1012],{},"For optional consumers (a floating panel that should hide entirely when the form isn't mounted), wrap the whole subtree in ",[14,1162,1163],{},"v-if",[62,1165,1167],{"className":64,"code":1166,"language":66,"meta":67,"style":67},"\u003Ctemplate>\n  \u003Cdiv v-if=\"ctx\" class=\"status\">{{ ctx.meta.dirty ? '●' : '' }}\u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[14,1168,1169,1177,1206],{"__ignoreMap":67},[71,1170,1171,1173,1175],{"class":73,"line":74},[71,1172,85],{"class":84},[71,1174,240],{"class":88},[71,1176,106],{"class":84},[71,1178,1179,1181,1184,1186,1188,1191,1194,1196,1199,1202,1204],{"class":73,"line":81},[71,1180,248],{"class":84},[71,1182,1183],{"class":88},"div",[71,1185,440],{"class":92},[71,1187,99],{"class":84},[71,1189,1190],{"class":102},"\"ctx\"",[71,1192,1193],{"class":92}," class",[71,1195,99],{"class":84},[71,1197,1198],{"class":102},"\"status\"",[71,1200,1201],{"class":84},">{{ ctx.meta.dirty ? '●' : '' }}\u003C\u002F",[71,1203,1183],{"class":88},[71,1205,106],{"class":84},[71,1207,1208,1210,1212],{"class":73,"line":109},[71,1209,223],{"class":84},[71,1211,240],{"class":88},[71,1213,106],{"class":84},[20,1215,1216,1217,1220],{},"Either way, the non-null assertion (",[14,1218,1219],{},"ctx!",") is a pattern to avoid: it teaches the type checker to look the other way, and a single mount-order regression turns into a runtime crash instead of a quiet no-op.",[20,1222,1223,1225,1226,1229],{},[14,1224,5],{}," does throw ",[14,1227,1228],{},"OutsideSetupError"," if called outside a Vue setup function: a structural mistake the runtime can catch unambiguously.",[50,1231,1233],{"id":1232},"where-to-next","Where to next",[685,1235,1236,1245,1254],{},[688,1237,1238,1244],{},[666,1239,1241,1243],{"href":1240},"\u002Fdocs\u002Fbinding-inputs\u002Fuse-register",[14,1242,926],{}," for custom components",": the single-field child binding alternative.",[688,1246,1247,1020,1251,1253],{},[666,1248,1250],{"href":1249},"\u002Fdocs\u002Fcross-cutting-state\u002Fundo-redo","Undo & redo",[14,1252,5],{}," makes \"Undo\" buttons in distant components effortless.",[688,1255,1256,1264,1265,1267],{},[666,1257,1259,1260,1263],{"href":1258},"\u002Fdocs\u002Fmultistep\u002Fuse-wizard","Multistep flows (",[14,1261,1262],{},"useWizard",")",": orchestrates multiple ",[14,1266,473],{}," instances, not single-form access.",[1269,1270,1271],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":67,"searchDepth":81,"depth":81,"links":1273},[1274,1275,1276,1281,1282,1283,1284],{"id":52,"depth":81,"text":53},{"id":477,"depth":81,"text":478},{"id":673,"depth":81,"text":1277,"children":1278},"Do I need to pass a key to useForm?",[1279],{"id":735,"depth":109,"text":1280},"Gotcha: multiple anonymous useForm in one component",{"id":917,"depth":81,"text":918},{"id":973,"depth":81,"text":974},{"id":998,"depth":81,"text":669},{"id":1232,"depth":81,"text":1233},"Reach any registered form from anywhere in the component tree, no props drilling, no ref forwarding. Ambient resolution for nested components, keyed lookup for distant ones.","md",{},[1289,1292,1295,1298],{"label":1290,"value":1291},"Category","Composable",{"label":1293,"value":1294,"kind":14},"Signature","injectForm\u003CForm>(key?) => ReturnType\u003Ctypeof useForm\u003CForm>>",{"label":1296,"value":1297,"kind":14},"Ambient mode","useForm({ schema }) without a key",{"label":1299,"value":1300,"kind":14},"Explicit mode","useForm({ schema, key })","\u002Fdocs\u002Fcross-cutting-state\u002Finject-form",{"title":5,"description":1285},null,"docs\u002Fcross-cutting-state\u002Finject-form","OKnTR-9UL1MATQF-hDy5ibHUekaq-SGPxJ5FPGNIlh0",1780949761358]