[{"data":1,"prerenderedAt":762},["ShallowReactive",2],{"content-\u002Fdocs\u002Fbinding-inputs\u002Fcustom-assigners":3},{"id":4,"title":5,"body":6,"description":743,"extension":744,"meta":745,"metaRows":746,"navigation":152,"path":757,"seo":758,"source":759,"stem":760,"__hash__":761},"docs\u002Fdocs\u002Fbinding-inputs\u002Fcustom-assigners.md","Custom assigners",{"type":7,"value":8,"toc":734},"minimark",[9,13,20,23,56,60,65,91,94,99,401,411,420,424,479,531,548,552,560,565,569,579,675,691,698,702,730],[10,11,5],"h1",{"id":12},"custom-assigners",[14,15,16],"blockquote",{},[17,18,19],"p",{},"The escape hatch for elements whose value surface isn't a DOM property. Install a write function per element and decide what lands in storage when an event fires.",[21,22],"docs-meta-table",{},[17,24,25,26,30,31,34,35,38,39,42,43,46,47,55],{},"Click the color swatches to watch the JSON readout flip. The widget isn't an ",[27,28,29],"code",{},"\u003Cinput>","; it's a plain ",[27,32,33],{},"\u003Cdiv>"," with buttons that dispatches ",[27,36,37],{},"input"," events and stores its picked color on ",[27,40,41],{},"dataset.color",". A custom assigner installed via ",[27,44,45],{},"el[assignKey] = fn"," translates that to a form write. The ",[48,49,51,52],"a",{"href":50},"#install-via-assignkey","Install via ",[27,53,54],{},"assignKey"," section walks through the wire-up.",[57,58],"docs-demo",{"label":59,"slug":12},"Custom Assigner Demo",[61,62,64],"h2",{"id":63},"when-v-register-needs-help","When v-register needs help",[17,66,67,70,71,74,75,78,79,82,83,86,87,90],{},[27,68,69],{},"v-register","'s default extractor reads ",[27,72,73],{},"el.value"," (or ",[27,76,77],{},"el.checked"," for checkboxes, ",[27,80,81],{},"el.files"," for files). For elements whose value lives somewhere else (a Web Component with ",[27,84,85],{},"el.color",", a custom widget with ",[27,88,89],{},"el.dataset.x",", a third-party slider with a method-only surface), the default can't see what to write.",[17,92,93],{},"A custom assigner replaces the write step. The directive still fires on the same DOM events; the assigner decides what value to commit.",[61,95,51,97],{"id":96},"install-via-assignkey",[27,98,54],{},[100,101,106],"pre",{"className":102,"code":103,"language":104,"meta":105,"style":105},"language-ts shiki shiki-themes github-light github-dark","import { onMounted, useTemplateRef } from 'vue'\nimport { assignKey, type CustomDirectiveRegisterAssignerFn } from 'attaform'\n\nconst widgetEl = useTemplateRef\u003CHTMLDivElement>('widget')\n\nconst colorAssigner: CustomDirectiveRegisterAssignerFn = (_value, rv) => {\n  const el = widgetEl.value\n  if (!el || !rv) return false\n  rv.setValueWithInternalPath(el.dataset.color ?? '')\n  return true\n}\n\nonMounted(() => {\n  const el = widgetEl.value\n  if (el) {\n    ;(el as HTMLDivElement & { [k: symbol]: CustomDirectiveRegisterAssignerFn })[assignKey] =\n      colorAssigner\n  }\n})\n","ts","",[27,107,108,128,147,154,186,191,229,243,272,292,301,307,312,325,336,344,383,389,395],{"__ignoreMap":105},[109,110,113,117,121,124],"span",{"class":111,"line":112},"line",1,[109,114,116],{"class":115},"szBVR","import",[109,118,120],{"class":119},"sVt8B"," { onMounted, useTemplateRef } ",[109,122,123],{"class":115},"from",[109,125,127],{"class":126},"sZZnC"," 'vue'\n",[109,129,131,133,136,139,142,144],{"class":111,"line":130},2,[109,132,116],{"class":115},[109,134,135],{"class":119}," { assignKey, ",[109,137,138],{"class":115},"type",[109,140,141],{"class":119}," CustomDirectiveRegisterAssignerFn } ",[109,143,123],{"class":115},[109,145,146],{"class":126}," 'attaform'\n",[109,148,150],{"class":111,"line":149},3,[109,151,153],{"emptyLinePlaceholder":152},true,"\n",[109,155,157,160,164,167,171,174,177,180,183],{"class":111,"line":156},4,[109,158,159],{"class":115},"const",[109,161,163],{"class":162},"sj4cs"," widgetEl",[109,165,166],{"class":115}," =",[109,168,170],{"class":169},"sScJk"," useTemplateRef",[109,172,173],{"class":119},"\u003C",[109,175,176],{"class":169},"HTMLDivElement",[109,178,179],{"class":119},">(",[109,181,182],{"class":126},"'widget'",[109,184,185],{"class":119},")\n",[109,187,189],{"class":111,"line":188},5,[109,190,153],{"emptyLinePlaceholder":152},[109,192,194,196,199,202,205,207,210,214,217,220,223,226],{"class":111,"line":193},6,[109,195,159],{"class":115},[109,197,198],{"class":169}," colorAssigner",[109,200,201],{"class":115},":",[109,203,204],{"class":169}," CustomDirectiveRegisterAssignerFn",[109,206,166],{"class":115},[109,208,209],{"class":119}," (",[109,211,213],{"class":212},"s4XuR","_value",[109,215,216],{"class":119},", ",[109,218,219],{"class":212},"rv",[109,221,222],{"class":119},") ",[109,224,225],{"class":115},"=>",[109,227,228],{"class":119}," {\n",[109,230,232,235,238,240],{"class":111,"line":231},7,[109,233,234],{"class":115},"  const",[109,236,237],{"class":162}," el",[109,239,166],{"class":115},[109,241,242],{"class":119}," widgetEl.value\n",[109,244,246,249,251,254,257,260,263,266,269],{"class":111,"line":245},8,[109,247,248],{"class":115},"  if",[109,250,209],{"class":119},[109,252,253],{"class":115},"!",[109,255,256],{"class":119},"el ",[109,258,259],{"class":115},"||",[109,261,262],{"class":115}," !",[109,264,265],{"class":119},"rv) ",[109,267,268],{"class":115},"return",[109,270,271],{"class":162}," false\n",[109,273,275,278,281,284,287,290],{"class":111,"line":274},9,[109,276,277],{"class":119},"  rv.",[109,279,280],{"class":169},"setValueWithInternalPath",[109,282,283],{"class":119},"(el.dataset.color ",[109,285,286],{"class":115},"??",[109,288,289],{"class":126}," ''",[109,291,185],{"class":119},[109,293,295,298],{"class":111,"line":294},10,[109,296,297],{"class":115},"  return",[109,299,300],{"class":162}," true\n",[109,302,304],{"class":111,"line":303},11,[109,305,306],{"class":119},"}\n",[109,308,310],{"class":111,"line":309},12,[109,311,153],{"emptyLinePlaceholder":152},[109,313,315,318,321,323],{"class":111,"line":314},13,[109,316,317],{"class":169},"onMounted",[109,319,320],{"class":119},"(() ",[109,322,225],{"class":115},[109,324,228],{"class":119},[109,326,328,330,332,334],{"class":111,"line":327},14,[109,329,234],{"class":115},[109,331,237],{"class":162},[109,333,166],{"class":115},[109,335,242],{"class":119},[109,337,339,341],{"class":111,"line":338},15,[109,340,248],{"class":115},[109,342,343],{"class":119}," (el) {\n",[109,345,347,350,353,356,359,362,365,367,370,373,375,377,380],{"class":111,"line":346},16,[109,348,349],{"class":119},"    ;(el ",[109,351,352],{"class":115},"as",[109,354,355],{"class":169}," HTMLDivElement",[109,357,358],{"class":115}," &",[109,360,361],{"class":119}," { [",[109,363,364],{"class":212},"k",[109,366,201],{"class":115},[109,368,369],{"class":162}," symbol",[109,371,372],{"class":119},"]",[109,374,201],{"class":115},[109,376,204],{"class":169},[109,378,379],{"class":119}," })[assignKey] ",[109,381,382],{"class":115},"=\n",[109,384,386],{"class":111,"line":385},17,[109,387,388],{"class":119},"      colorAssigner\n",[109,390,392],{"class":111,"line":391},18,[109,393,394],{"class":119},"  }\n",[109,396,398],{"class":111,"line":397},19,[109,399,400],{"class":119},"})\n",[17,402,403,404,407,408,410],{},"The template anchor: ",[27,405,406],{},"\u003Cdiv ref=\"widget\" v-register=\"form.register('color')\" \u002F>",". The ",[27,409,69],{}," binding still goes on the element; the assigner is what reroutes the write step.",[17,412,413,415,416,419],{},[27,414,54],{}," is ",[27,417,418],{},"Symbol.for('attaform:assign-key')",", a well-known symbol so multiple installers (your code, a third-party wrapper, Attaform's own built-in assigners) can detect and coordinate.",[61,421,423],{"id":422},"the-function-signature","The function signature",[100,425,427],{"className":102,"code":426,"language":104,"meta":105,"style":105},"type CustomDirectiveRegisterAssignerFn = (\n  value: unknown,\n  registerValue?: RegisterValue\n) => boolean | undefined\n",[27,428,429,440,453,464],{"__ignoreMap":105},[109,430,431,433,435,437],{"class":111,"line":112},[109,432,138],{"class":115},[109,434,204],{"class":169},[109,436,166],{"class":115},[109,438,439],{"class":119}," (\n",[109,441,442,445,447,450],{"class":111,"line":130},[109,443,444],{"class":212},"  value",[109,446,201],{"class":115},[109,448,449],{"class":162}," unknown",[109,451,452],{"class":119},",\n",[109,454,455,458,461],{"class":111,"line":149},[109,456,457],{"class":212},"  registerValue",[109,459,460],{"class":115},"?:",[109,462,463],{"class":169}," RegisterValue\n",[109,465,466,468,470,473,476],{"class":111,"line":156},[109,467,222],{"class":119},[109,469,225],{"class":115},[109,471,472],{"class":162}," boolean",[109,474,475],{"class":115}," |",[109,477,478],{"class":162}," undefined\n",[480,481,482,495],"table",{},[483,484,485],"thead",{},[486,487,488,492],"tr",{},[489,490,491],"th",{},"Arg",[489,493,494],{},"Use",[496,497,498,513],"tbody",{},[486,499,500,506],{},[501,502,503],"td",{},[27,504,505],{},"value",[501,507,508,509,512],{},"What the directive extracted from the event, post-transforms and post-coerce. May be ",[27,510,511],{},"undefined"," when the assigner reads its own state.",[486,514,515,520],{},[501,516,517],{},[27,518,519],{},"registerValue",[501,521,522,523,526,527,530],{},"The ",[27,524,525],{},"RegisterValue"," for the current binding. Call ",[27,528,529],{},"rv.setValueWithInternalPath(v)"," to commit a write. Supplied by the directive on every fire regardless of install path, so the assigner doesn't have to capture the RV via closure at install time.",[17,532,533,534,537,538,541,542,544,545,547],{},"Return ",[27,535,536],{},"true"," to signal the write was accepted, ",[27,539,540],{},"false"," to reject. ",[27,543,511],{}," is treated as success, so simple assigners can ",[27,546,268],{}," nothing.",[61,549,551],{"id":550},"reach-for-transforms-first","Reach for transforms first",[17,553,554,555,559],{},"If you just want to reshape the value before write (uppercase, trim, parse), reach for ",[48,556,558],{"href":557},"\u002Fdocs\u002Fbinding-inputs\u002Ftransforms","register transforms"," instead. Transforms compose into a per-field pipeline declared at the call site, no element-level installation, no symbol indirection.",[17,561,562,564],{},[27,563,54],{}," is for cases where the extracted value isn't right at all: different source, different shape, different element type. Web Components, custom widgets, third-party libraries that don't speak the standard DOM event surface.",[61,566,568],{"id":567},"persistence-integrates-automatically","Persistence integrates automatically",[17,570,571,572,216,575,578],{},"If the bound element opted in via ",[27,573,574],{},"register('path', { persist: true })",[27,576,577],{},"rv.setValueWithInternalPath(value)"," auto-attaches the persist meta. The write flows to the configured storage backend without the assigner threading anything explicit.",[100,580,582],{"className":102,"code":581,"language":104,"meta":105,"style":105},"const colorAssigner: CustomDirectiveRegisterAssignerFn = (_value, rv) => {\n  const el = widgetEl.value\n  if (!el || !rv) return false\n  \u002F\u002F No second arg, no meta to assemble: the rv consults the\n  \u002F\u002F per-element opt-in registered against this element.\n  rv.setValueWithInternalPath(el.dataset.color ?? '')\n  return true\n}\n",[27,583,584,610,620,640,646,651,665,671],{"__ignoreMap":105},[109,585,586,588,590,592,594,596,598,600,602,604,606,608],{"class":111,"line":112},[109,587,159],{"class":115},[109,589,198],{"class":169},[109,591,201],{"class":115},[109,593,204],{"class":169},[109,595,166],{"class":115},[109,597,209],{"class":119},[109,599,213],{"class":212},[109,601,216],{"class":119},[109,603,219],{"class":212},[109,605,222],{"class":119},[109,607,225],{"class":115},[109,609,228],{"class":119},[109,611,612,614,616,618],{"class":111,"line":130},[109,613,234],{"class":115},[109,615,237],{"class":162},[109,617,166],{"class":115},[109,619,242],{"class":119},[109,621,622,624,626,628,630,632,634,636,638],{"class":111,"line":149},[109,623,248],{"class":115},[109,625,209],{"class":119},[109,627,253],{"class":115},[109,629,256],{"class":119},[109,631,259],{"class":115},[109,633,262],{"class":115},[109,635,265],{"class":119},[109,637,268],{"class":115},[109,639,271],{"class":162},[109,641,642],{"class":111,"line":156},[109,643,645],{"class":644},"sJ8bj","  \u002F\u002F No second arg, no meta to assemble: the rv consults the\n",[109,647,648],{"class":111,"line":188},[109,649,650],{"class":644},"  \u002F\u002F per-element opt-in registered against this element.\n",[109,652,653,655,657,659,661,663],{"class":111,"line":193},[109,654,277],{"class":119},[109,656,280],{"class":169},[109,658,283],{"class":119},[109,660,286],{"class":115},[109,662,289],{"class":126},[109,664,185],{"class":119},[109,666,667,669],{"class":111,"line":231},[109,668,297],{"class":115},[109,670,300],{"class":162},[109,672,673],{"class":111,"line":245},[109,674,306],{"class":119},[17,676,677,678,681,682,685,686,690],{},"Template pairing: ",[27,679,680],{},"\u003Cdiv ref=\"widget\" v-register=\"form.register('color', { persist: true })\" \u002F>",". The opt-in lives at the ",[27,683,684],{},"register"," call site, per the ",[48,687,689],{"href":688},"\u002Fdocs\u002Fpersistence\u002Fper-field-opt-in","two-gate policy","; the assigner just commits the write.",[17,692,693,694,697],{},"Bypass the auto-attach by passing an explicit second arg, e.g. ",[27,695,696],{},"rv.setValueWithInternalPath(value, { persist: false })"," for a transient write that shouldn't persist even when the element is opted in.",[61,699,701],{"id":700},"where-to-next","Where to next",[703,704,705,712,721],"ul",{},[706,707,708,711],"li",{},[48,709,710],{"href":557},"Register transforms",": the higher-level option for value reshaping.",[706,713,714,720],{},[48,715,717],{"href":716},"\u002Fdocs\u002Fbinding-inputs\u002Fuse-register",[27,718,719],{},"useRegister",": the composable for Vue component wrappers that need to re-bind v-register internally.",[706,722,723,729],{},[48,724,522,726,728],{"href":725},"\u002Fdocs\u002Fbinding-inputs\u002Fv-register",[27,727,69],{}," directive",": the directive layer custom assigners plug into.",[731,732,733],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}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);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":105,"searchDepth":130,"depth":130,"links":735},[736,737,739,740,741,742],{"id":63,"depth":130,"text":64},{"id":96,"depth":130,"text":738},"Install via assignKey",{"id":422,"depth":130,"text":423},{"id":550,"depth":130,"text":551},{"id":567,"depth":130,"text":568},{"id":700,"depth":130,"text":701},"For elements whose value surface isn't a DOM property, assignKey installs a per-element function that decides what to write to form state when the directive sees an event.","md",{},[747,750,752,755],{"label":748,"value":749},"Category","Directive binding",{"label":751,"value":45,"kind":27},"Slot",{"label":753,"value":754,"kind":27},"Signature","(value, registerValue?) => boolean | undefined",{"label":756,"value":418,"kind":27},"Symbol","\u002Fdocs\u002Fbinding-inputs\u002Fcustom-assigners",{"title":5,"description":743},null,"docs\u002Fbinding-inputs\u002Fcustom-assigners","5b1LQsnIRge9NZ1Z-ODMFIDThaz-1CP2_EpwpjIBeWc",1780949759617]