[{"data":1,"prerenderedAt":720},["ShallowReactive",2],{"content-\u002Fdocs\u002Freading-the-form\u002Flist":3},{"id":4,"title":5,"body":6,"description":702,"extension":703,"meta":704,"metaRows":705,"navigation":153,"path":715,"seo":716,"source":717,"stem":718,"__hash__":719},"docs\u002Fdocs\u002Freading-the-form\u002Flist.md","list",{"type":7,"value":8,"toc":690},"minimark",[9,15,30,33,56,61,66,80,335,340,346,372,385,389,412,528,538,544,570,574,577,610,616,623,634,638,686],[10,11,12],"h1",{"id":5},[13,14,5],"code",{},[16,17,18],"blockquote",{},[19,20,21,22,25,26,29],"p",{},"One array, one FieldState per element, in index order. Each entry carries a stable ",[13,23,24],{},"key",", so a keyed ",[13,27,28],{},"v-for"," keeps every row attached to its element through any reorder.",[31,32],"docs-meta-table",{},[19,34,35,38,39,46,47,49,50,52,53,55],{},[13,36,37],{},"form.list(path)"," is the iteration view over an array. It hands back one ",[40,41,43],"a",{"href":42},"\u002Fdocs\u002Freading-the-form\u002Ffields",[13,44,45],{},"FieldState"," per element, in index order, and each entry carries a ",[13,48,24],{}," that follows its element across every shape change. Bind that ",[13,51,24],{}," to your ",[13,54,28],{}," and Vue keeps each row's component instance, input focus, and cursor attached to the element the user is working on, even after a drag-reorder.",[57,58],"docs-demo",{"label":59,"slug":60},"form.list Demo","form-list",[62,63,65],"h2",{"id":64},"iterating-an-array","Iterating an array",[19,67,68,69,71,72,75,76,79],{},"Reach for ",[13,70,5],{}," wherever you render a repeating field. Pair it with the array index for binding and ",[13,73,74],{},"row.key"," for the ",[13,77,78],{},":key",":",[81,82,87],"pre",{"className":83,"code":84,"language":85,"meta":86,"style":86},"language-vue shiki shiki-themes github-light github-dark","\u003Cscript setup lang=\"ts\">\n  import { useForm } from 'attaform\u002Fzod'\n  import { z } from 'zod'\n\n  const schema = z.object({\n    roster: z.array(z.string()),\n  })\n\n  const form = useForm({ schema })\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv v-for=\"(row, i) in form.list('roster')\" :key=\"row.key\">\n    \u003Cinput v-register=\"form.register(`roster.${i}`)\" \u002F>\n    \u003Cp v-if=\"row.showErrors\">{{ row.firstError?.message }}\u003C\u002Fp>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n","vue","",[13,88,89,119,135,148,155,177,195,201,206,222,232,237,247,274,294,316,326],{"__ignoreMap":86},[90,91,94,98,102,106,109,112,116],"span",{"class":92,"line":93},"line",1,[90,95,97],{"class":96},"sVt8B","\u003C",[90,99,101],{"class":100},"s9eBZ","script",[90,103,105],{"class":104},"sScJk"," setup",[90,107,108],{"class":104}," lang",[90,110,111],{"class":96},"=",[90,113,115],{"class":114},"sZZnC","\"ts\"",[90,117,118],{"class":96},">\n",[90,120,122,126,129,132],{"class":92,"line":121},2,[90,123,125],{"class":124},"szBVR","  import",[90,127,128],{"class":96}," { useForm } ",[90,130,131],{"class":124},"from",[90,133,134],{"class":114}," 'attaform\u002Fzod'\n",[90,136,138,140,143,145],{"class":92,"line":137},3,[90,139,125],{"class":124},[90,141,142],{"class":96}," { z } ",[90,144,131],{"class":124},[90,146,147],{"class":114}," 'zod'\n",[90,149,151],{"class":92,"line":150},4,[90,152,154],{"emptyLinePlaceholder":153},true,"\n",[90,156,158,161,165,168,171,174],{"class":92,"line":157},5,[90,159,160],{"class":124},"  const",[90,162,164],{"class":163},"sj4cs"," schema",[90,166,167],{"class":124}," =",[90,169,170],{"class":96}," z.",[90,172,173],{"class":104},"object",[90,175,176],{"class":96},"({\n",[90,178,180,183,186,189,192],{"class":92,"line":179},6,[90,181,182],{"class":96},"    roster: z.",[90,184,185],{"class":104},"array",[90,187,188],{"class":96},"(z.",[90,190,191],{"class":104},"string",[90,193,194],{"class":96},"()),\n",[90,196,198],{"class":92,"line":197},7,[90,199,200],{"class":96},"  })\n",[90,202,204],{"class":92,"line":203},8,[90,205,154],{"emptyLinePlaceholder":153},[90,207,209,211,214,216,219],{"class":92,"line":208},9,[90,210,160],{"class":124},[90,212,213],{"class":163}," form",[90,215,167],{"class":124},[90,217,218],{"class":104}," useForm",[90,220,221],{"class":96},"({ schema })\n",[90,223,225,228,230],{"class":92,"line":224},10,[90,226,227],{"class":96},"\u003C\u002F",[90,229,101],{"class":100},[90,231,118],{"class":96},[90,233,235],{"class":92,"line":234},11,[90,236,154],{"emptyLinePlaceholder":153},[90,238,240,242,245],{"class":92,"line":239},12,[90,241,97],{"class":96},[90,243,244],{"class":100},"template",[90,246,118],{"class":96},[90,248,250,253,256,259,261,264,267,269,272],{"class":92,"line":249},13,[90,251,252],{"class":96},"  \u003C",[90,254,255],{"class":100},"div",[90,257,258],{"class":104}," v-for",[90,260,111],{"class":96},[90,262,263],{"class":114},"\"(row, i) in form.list('roster')\"",[90,265,266],{"class":104}," :key",[90,268,111],{"class":96},[90,270,271],{"class":114},"\"row.key\"",[90,273,118],{"class":96},[90,275,277,280,283,286,288,291],{"class":92,"line":276},14,[90,278,279],{"class":96},"    \u003C",[90,281,282],{"class":100},"input",[90,284,285],{"class":104}," v-register",[90,287,111],{"class":96},[90,289,290],{"class":114},"\"form.register(`roster.${i}`)\"",[90,292,293],{"class":96}," \u002F>\n",[90,295,297,299,301,304,306,309,312,314],{"class":92,"line":296},15,[90,298,279],{"class":96},[90,300,19],{"class":100},[90,302,303],{"class":104}," v-if",[90,305,111],{"class":96},[90,307,308],{"class":114},"\"row.showErrors\"",[90,310,311],{"class":96},">{{ row.firstError?.message }}\u003C\u002F",[90,313,19],{"class":100},[90,315,118],{"class":96},[90,317,319,322,324],{"class":92,"line":318},16,[90,320,321],{"class":96},"  \u003C\u002F",[90,323,255],{"class":100},[90,325,118],{"class":96},[90,327,329,331,333],{"class":92,"line":328},17,[90,330,227],{"class":96},[90,332,244],{"class":100},[90,334,118],{"class":96},[19,336,337,339],{},[13,338,5],{}," is typed against every array path in the schema, so the path autocompletes to arrays only, and each entry's type narrows to the element shape.",[62,341,343,344],{"id":342},"why-key-by-rowkey","Why key by ",[13,345,74],{},[19,347,348,350,351,354,355,354,358,361,362,365,366,368,369,371],{},[13,349,74],{}," is an allocated identity token, not the index. It is minted once for an element and travels with it through ",[13,352,353],{},"insert",", ",[13,356,357],{},"remove",[13,359,360],{},"move",", and ",[13,363,364],{},"swap",", staying distinct even when two elements hold identical values. Keying a ",[13,367,28],{}," by the index instead ties each row to a slot, so a reorder reshuffles which DOM node and component instance render which element; a half-typed input can jump to the wrong row. Keying by ",[13,370,74],{}," ties each row to its element, so the row a user is editing stays put when the list around it moves.",[19,373,374,375,380,381,384],{},"The same token is on every FieldState as ",[40,376,377],{"href":42},[13,378,379],{},"field.key",", reachable through ",[13,382,383],{},"form.fields('roster.0').key"," when you need it outside an iteration.",[62,386,388],{"id":387},"each-entry-is-a-live-fieldstate","Each entry is a live FieldState",[19,390,391,392,395,396,354,399,354,402,354,405,354,408,411],{},"The entries are the same field states ",[13,393,394],{},"form.fields"," exposes, so every read stays live as the user interacts. A row carries the full surface: ",[13,397,398],{},"row.value",[13,400,401],{},"row.errors",[13,403,404],{},"row.showErrors",[13,406,407],{},"row.firstError",[13,409,410],{},"row.touched",", and the rest.",[81,413,415],{"className":83,"code":414,"language":85,"meta":86,"style":86},"\u003Ctemplate>\n  \u003Cul>\n    \u003Cli v-for=\"(row, i) in form.list('roster')\" :key=\"row.key\">\n      \u003Cinput v-register=\"form.register(`roster.${i}`)\" :aria-invalid=\"row.showErrors\" \u002F>\n      \u003Cspan v-if=\"row.showErrors\" :id=\"row.aria.errorId\">{{ row.firstError?.message }}\u003C\u002Fspan>\n    \u003C\u002Fli>\n  \u003C\u002Ful>\n\u003C\u002Ftemplate>\n",[13,416,417,425,434,455,477,503,512,520],{"__ignoreMap":86},[90,418,419,421,423],{"class":92,"line":93},[90,420,97],{"class":96},[90,422,244],{"class":100},[90,424,118],{"class":96},[90,426,427,429,432],{"class":92,"line":121},[90,428,252],{"class":96},[90,430,431],{"class":100},"ul",[90,433,118],{"class":96},[90,435,436,438,441,443,445,447,449,451,453],{"class":92,"line":137},[90,437,279],{"class":96},[90,439,440],{"class":100},"li",[90,442,258],{"class":104},[90,444,111],{"class":96},[90,446,263],{"class":114},[90,448,266],{"class":104},[90,450,111],{"class":96},[90,452,271],{"class":114},[90,454,118],{"class":96},[90,456,457,460,462,464,466,468,471,473,475],{"class":92,"line":150},[90,458,459],{"class":96},"      \u003C",[90,461,282],{"class":100},[90,463,285],{"class":104},[90,465,111],{"class":96},[90,467,290],{"class":114},[90,469,470],{"class":104}," :aria-invalid",[90,472,111],{"class":96},[90,474,308],{"class":114},[90,476,293],{"class":96},[90,478,479,481,483,485,487,489,492,494,497,499,501],{"class":92,"line":157},[90,480,459],{"class":96},[90,482,90],{"class":100},[90,484,303],{"class":104},[90,486,111],{"class":96},[90,488,308],{"class":114},[90,490,491],{"class":104}," :id",[90,493,111],{"class":96},[90,495,496],{"class":114},"\"row.aria.errorId\"",[90,498,311],{"class":96},[90,500,90],{"class":100},[90,502,118],{"class":96},[90,504,505,508,510],{"class":92,"line":179},[90,506,507],{"class":96},"    \u003C\u002F",[90,509,440],{"class":100},[90,511,118],{"class":96},[90,513,514,516,518],{"class":92,"line":197},[90,515,321],{"class":96},[90,517,431],{"class":100},[90,519,118],{"class":96},[90,521,522,524,526],{"class":92,"line":203},[90,523,227],{"class":96},[90,525,244],{"class":100},[90,527,118],{"class":96},[19,529,530,531,534,535,537],{},"Binding still flows through ",[13,532,533],{},"form.register"," with the element path; ",[13,536,5],{}," supplies the key and the per-row reads, and the array index supplies the register path.",[62,539,541,543],{"id":540},"formfields-stays-the-aggregate",[13,542,394],{}," stays the aggregate",[19,545,546,549,550,354,553,361,556,559,560,563,564,566,567,569],{},[13,547,548],{},"form.fields('roster')"," remains the single aggregated container for the whole array: one rolled-up FieldState whose ",[13,551,552],{},"errors",[13,554,555],{},"valid",[13,557,558],{},"touched"," summarize every element at once. That is the read for an array-level message (",[13,561,562],{},"z.array(...).min(1)"," lands there). ",[13,565,5],{}," is the complementary per-element view. Reach for the aggregate when you want one verdict for the array, and for ",[13,568,5],{}," when you want a row each.",[62,571,573],{"id":572},"read-only-by-design","Read-only by design",[19,575,576],{},"The returned array is frozen. Identity is bookkept by the mutation helpers, so shape changes go through them rather than the view:",[431,578,579,594,604],{},[440,580,581,587,588,587,591,593],{},[40,582,584],{"href":583},"\u002Fdocs\u002Fwriting-and-mutating\u002Ffield-arrays",[13,585,586],{},"append"," \u002F ",[13,589,590],{},"prepend",[13,592,353],{}," to add a row.",[440,595,596,598,599,587,601,603],{},[13,597,357],{}," to drop one, ",[13,600,360],{},[13,602,364],{}," to reorder.",[440,605,606,609],{},[13,607,608],{},"replace"," to overwrite a slot with a fresh element.",[19,611,612,613,615],{},"Each helper replays its exact change onto the identity tokens, which is what lets ",[13,614,74],{}," stay true across the mutation.",[62,617,619,622],{"id":618},"record-is-the-record-counterpart",[13,620,621],{},"record"," is the record counterpart",[19,624,625,627,628,633],{},[13,626,5],{}," is for arrays. For a record, whose entries are keyed rather than ordered, reach for ",[40,629,631],{"href":630},"\u002Fdocs\u002Freading-the-form\u002Frecord",[13,632,621],{},": it hands back a keyed object, one FieldState per entry under the entry's own key. The two split cleanly by path type, and each rejects the other at compile time.",[62,635,637],{"id":636},"where-to-next","Where to next",[431,639,640,647,653,664,675],{},[440,641,642,646],{},[40,643,644],{"href":630},[13,645,621],{},": the record counterpart, one FieldState per entry keyed by the record's own key.",[440,648,649,652],{},[40,650,651],{"href":583},"Field-array mutations",": the seven helpers that add, remove, and reorder elements.",[440,654,655,660,661,663],{},[40,656,657],{"href":42},[13,658,659],{},"fields",": the per-leaf FieldState and the ",[13,662,24],{}," every entry carries.",[440,665,666,674],{},[40,667,669,670,673],{"href":668},"\u002Fdocs\u002Fbinding-inputs\u002Fv-register","The ",[13,671,672],{},"v-register"," directive",": the binding each row's input flows through.",[440,676,677,682,683,685],{},[40,678,680],{"href":679},"\u002Fdocs\u002Freading-the-form\u002Ferrors",[13,681,552],{},": the per-path errors behind ",[13,684,407],{},".",[687,688,689],"style",{},"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 .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":86,"searchDepth":121,"depth":121,"links":691},[692,693,695,696,698,699,701],{"id":64,"depth":121,"text":65},{"id":342,"depth":121,"text":694},"Why key by row.key",{"id":387,"depth":121,"text":388},{"id":540,"depth":121,"text":697},"form.fields stays the aggregate",{"id":572,"depth":121,"text":573},{"id":618,"depth":121,"text":700},"record is the record counterpart",{"id":636,"depth":121,"text":637},"form.list reads an array as one FieldState per element, in index order, each carrying a stable key so a keyed v-for survives inserts, removals, moves, and swaps.","md",{},[706,709,712],{"label":707,"value":708},"Category","Return method",{"label":710,"value":711,"kind":13},"Type","(path) => readonly FieldState[]",{"label":713,"value":714},"Keyed","Yes, by element identity","\u002Fdocs\u002Freading-the-form\u002Flist",{"title":5,"description":702},null,"docs\u002Freading-the-form\u002Flist","tNPkjPK0sLwH4CY4xbg7hK_m8QyQf5Ldmr8Weg79I9Q",1780949758328]