@@ -37,6 +37,7 @@ import {
3737 PHX_VIEWPORT_BOTTOM ,
3838 MAX_CHILD_JOIN_ATTEMPTS ,
3939 PHX_LV_PID ,
40+ PHX_NO_USED_CHECK ,
4041} from "./constants" ;
4142
4243import {
@@ -73,90 +74,6 @@ export const prependFormDataKey = (key, prefix) => {
7374 return baseKey ;
7475} ;
7576
76- const serializeForm = ( form , opts , onlyNames = [ ] ) => {
77- const { submitter } = opts ;
78-
79- // We must inject the submitter in the order that it exists in the DOM
80- // relative to other inputs. For example, for checkbox groups, the order must be maintained.
81- let injectedElement ;
82- if ( submitter && submitter . name ) {
83- const input = document . createElement ( "input" ) ;
84- input . type = "hidden" ;
85- // set the form attribute if the submitter has one;
86- // this can happen if the element is outside the actual form element
87- const formId = submitter . getAttribute ( "form" ) ;
88- if ( formId ) {
89- input . setAttribute ( "form" , formId ) ;
90- }
91- input . name = submitter . name ;
92- input . value = submitter . value ;
93- submitter . parentElement . insertBefore ( input , submitter ) ;
94- injectedElement = input ;
95- }
96-
97- const formData = new FormData ( form ) ;
98- const toRemove = [ ] ;
99-
100- formData . forEach ( ( val , key , _index ) => {
101- if ( val instanceof File ) {
102- toRemove . push ( key ) ;
103- }
104- } ) ;
105-
106- // Cleanup after building fileData
107- toRemove . forEach ( ( key ) => formData . delete ( key ) ) ;
108-
109- const params = new URLSearchParams ( ) ;
110-
111- const { inputsUnused, onlyHiddenInputs } = Array . from ( form . elements ) . reduce (
112- ( acc , input ) => {
113- const { inputsUnused, onlyHiddenInputs } = acc ;
114- const key = input . name ;
115- if ( ! key ) {
116- return acc ;
117- }
118-
119- if ( inputsUnused [ key ] === undefined ) {
120- inputsUnused [ key ] = true ;
121- }
122- if ( onlyHiddenInputs [ key ] === undefined ) {
123- onlyHiddenInputs [ key ] = true ;
124- }
125-
126- const isUsed =
127- DOM . private ( input , PHX_HAS_FOCUSED ) ||
128- DOM . private ( input , PHX_HAS_SUBMITTED ) ;
129- const isHidden = input . type === "hidden" ;
130- inputsUnused [ key ] = inputsUnused [ key ] && ! isUsed ;
131- onlyHiddenInputs [ key ] = onlyHiddenInputs [ key ] && isHidden ;
132-
133- return acc ;
134- } ,
135- { inputsUnused : { } , onlyHiddenInputs : { } } ,
136- ) ;
137-
138- for ( const [ key , val ] of formData . entries ( ) ) {
139- if ( onlyNames . length === 0 || onlyNames . indexOf ( key ) >= 0 ) {
140- const isUnused = inputsUnused [ key ] ;
141- const hidden = onlyHiddenInputs [ key ] ;
142- if ( isUnused && ! ( submitter && submitter . name == key ) && ! hidden ) {
143- params . append ( prependFormDataKey ( key , "_unused_" ) , "" ) ;
144- }
145- if ( typeof val === "string" ) {
146- params . append ( key , val ) ;
147- }
148- }
149- }
150-
151- // remove the injected element again
152- // (it would be removed by the next dom patch anyway, but this is cleaner)
153- if ( submitter && injectedElement ) {
154- submitter . parentElement . removeChild ( injectedElement ) ;
155- }
156-
157- return params . toString ( ) ;
158- } ;
159-
16077export default class View {
16178 static closestView ( el ) {
16279 const liveViewEl = el . closest ( PHX_VIEW_SELECTOR ) ;
@@ -1566,6 +1483,107 @@ export default class View {
15661483 return meta ;
15671484 }
15681485
1486+ serializeForm ( form , opts , onlyNames = [ ] ) {
1487+ const { submitter } = opts ;
1488+
1489+ // We must inject the submitter in the order that it exists in the DOM
1490+ // relative to other inputs. For example, for checkbox groups, the order must be maintained.
1491+ let injectedElement ;
1492+ if ( submitter && submitter . name ) {
1493+ const input = document . createElement ( "input" ) ;
1494+ input . type = "hidden" ;
1495+ // set the form attribute if the submitter has one;
1496+ // this can happen if the element is outside the actual form element
1497+ const formId = submitter . getAttribute ( "form" ) ;
1498+ if ( formId ) {
1499+ input . setAttribute ( "form" , formId ) ;
1500+ }
1501+ input . name = submitter . name ;
1502+ input . value = submitter . value ;
1503+ submitter . parentElement . insertBefore ( input , submitter ) ;
1504+ injectedElement = input ;
1505+ }
1506+
1507+ const formData = new FormData ( form ) ;
1508+ const toRemove = [ ] ;
1509+
1510+ formData . forEach ( ( val , key , _index ) => {
1511+ if ( val instanceof File ) {
1512+ toRemove . push ( key ) ;
1513+ }
1514+ } ) ;
1515+
1516+ // Cleanup after building fileData
1517+ toRemove . forEach ( ( key ) => formData . delete ( key ) ) ;
1518+
1519+ const params = new URLSearchParams ( ) ;
1520+
1521+ const { inputsUnused, onlyHiddenInputs } = Array . from ( form . elements ) . reduce (
1522+ ( acc , input ) => {
1523+ const { inputsUnused, onlyHiddenInputs } = acc ;
1524+ const key = input . name ;
1525+ if ( ! key ) {
1526+ return acc ;
1527+ }
1528+
1529+ if ( inputsUnused [ key ] === undefined ) {
1530+ inputsUnused [ key ] = true ;
1531+ }
1532+ if ( onlyHiddenInputs [ key ] === undefined ) {
1533+ onlyHiddenInputs [ key ] = true ;
1534+ }
1535+
1536+ const inputSkipUnusedField = input . hasAttribute (
1537+ this . binding ( PHX_NO_USED_CHECK ) ,
1538+ ) ;
1539+
1540+ const isUsed =
1541+ DOM . private ( input , PHX_HAS_FOCUSED ) ||
1542+ DOM . private ( input , PHX_HAS_SUBMITTED ) ||
1543+ inputSkipUnusedField ;
1544+
1545+ const isHidden = input . type === "hidden" ;
1546+ inputsUnused [ key ] = inputsUnused [ key ] && ! isUsed ;
1547+ onlyHiddenInputs [ key ] = onlyHiddenInputs [ key ] && isHidden ;
1548+
1549+ return acc ;
1550+ } ,
1551+ { inputsUnused : { } , onlyHiddenInputs : { } } ,
1552+ ) ;
1553+
1554+ const formSkipUnusedFields = form . hasAttribute (
1555+ this . binding ( PHX_NO_USED_CHECK ) ,
1556+ ) ;
1557+
1558+ for ( const [ key , val ] of formData . entries ( ) ) {
1559+ if ( onlyNames . length === 0 || onlyNames . indexOf ( key ) >= 0 ) {
1560+ const isUnused = inputsUnused [ key ] ;
1561+ const hidden = onlyHiddenInputs [ key ] ;
1562+ const skipUnusedCheck = formSkipUnusedFields ;
1563+
1564+ if (
1565+ ! skipUnusedCheck &&
1566+ isUnused &&
1567+ ! ( submitter && submitter . name == key ) &&
1568+ ! hidden
1569+ ) {
1570+ params . append ( prependFormDataKey ( key , "_unused_" ) , "" ) ;
1571+ }
1572+ if ( typeof val === "string" ) {
1573+ params . append ( key , val ) ;
1574+ }
1575+ }
1576+ }
1577+
1578+ // remove the injected element again
1579+ // (it would be removed by the next dom patch anyway, but this is cleaner)
1580+ if ( submitter && injectedElement ) {
1581+ submitter . parentElement . removeChild ( injectedElement ) ;
1582+ }
1583+
1584+ return params . toString ( ) ;
1585+ }
1586+
15691587 pushEvent ( type , el , targetCtx , phxEvent , meta , opts = { } , onReply ) {
15701588 this . pushWithReply (
15711589 ( maybePayload ) =>
@@ -1627,9 +1645,11 @@ export default class View {
16271645 serializeOpts . submitter = inputEl ;
16281646 }
16291647 if ( inputEl . getAttribute ( this . binding ( "change" ) ) ) {
1630- formData = serializeForm ( inputEl . form , serializeOpts , [ inputEl . name ] ) ;
1648+ formData = this . serializeForm ( inputEl . form , serializeOpts , [
1649+ inputEl . name ,
1650+ ] ) ;
16311651 } else {
1632- formData = serializeForm ( inputEl . form , serializeOpts ) ;
1652+ formData = this . serializeForm ( inputEl . form , serializeOpts ) ;
16331653 }
16341654 if (
16351655 DOM . isUploadInput ( inputEl ) &&
@@ -1806,7 +1826,7 @@ export default class View {
18061826 return this . undoRefs ( ref , phxEvent ) ;
18071827 }
18081828 const meta = this . extractMeta ( formEl , { } , opts . value ) ;
1809- const formData = serializeForm ( formEl , { submitter } ) ;
1829+ const formData = this . serializeForm ( formEl , { submitter } ) ;
18101830 this . pushWithReply ( proxyRefGen , "event" , {
18111831 type : "form" ,
18121832 event : phxEvent ,
@@ -1824,7 +1844,7 @@ export default class View {
18241844 )
18251845 ) {
18261846 const meta = this . extractMeta ( formEl , { } , opts . value ) ;
1827- const formData = serializeForm ( formEl , { submitter } ) ;
1847+ const formData = this . serializeForm ( formEl , { submitter } ) ;
18281848 this . pushWithReply ( refGenerator , "event" , {
18291849 type : "form" ,
18301850 event : phxEvent ,
0 commit comments