@@ -352,37 +352,23 @@ const parseDeepData = function (data, propertyPath) {
352352 parts,
353353 } ;
354354} ;
355- function setDeepData ( data , propertyPath , value ) {
356- const { currentLevelData, finalData, finalKey, parts } = parseDeepData ( data , propertyPath ) ;
357- if ( typeof currentLevelData !== 'object' ) {
358- const lastPart = parts . pop ( ) ;
359- if ( typeof currentLevelData === 'undefined' ) {
360- throw new Error ( `Cannot set data-model="${ propertyPath } ". The parent "${ parts . join ( '.' ) } " data does not exist. Did you forget to expose "${ parts [ 0 ] } " as a LiveProp?` ) ;
361- }
362- throw new Error ( `Cannot set data-model="${ propertyPath } ". The parent "${ parts . join ( '.' ) } " data does not appear to be an object (it's "${ currentLevelData } "). Did you forget to add exposed={"${ lastPart } "} to its LiveProp?` ) ;
363- }
364- if ( currentLevelData [ finalKey ] === undefined ) {
365- const lastPart = parts . pop ( ) ;
366- if ( parts . length > 0 ) {
367- throw new Error ( `The model name ${ propertyPath } was never initialized. Did you forget to add exposed={"${ lastPart } "} to its LiveProp?` ) ;
368- }
369- else {
370- throw new Error ( `The model name "${ propertyPath } " was never initialized. Did you forget to expose "${ lastPart } " as a LiveProp? Available models values are: ${ Object . keys ( data ) . length > 0 ? Object . keys ( data ) . join ( ', ' ) : '(none)' } ` ) ;
371- }
372- }
373- currentLevelData [ finalKey ] = value ;
374- return finalData ;
375- }
376355
377356class ValueStore {
378357 constructor ( props ) {
379358 this . identifierKey = '@id' ;
380- this . updatedModels = [ ] ;
381359 this . props = { } ;
360+ this . dirtyProps = { } ;
361+ this . pendingProps = { } ;
382362 this . props = props ;
383363 }
384364 get ( name ) {
385365 const normalizedName = normalizeModelName ( name ) ;
366+ if ( this . dirtyProps [ normalizedName ] !== undefined ) {
367+ return this . dirtyProps [ normalizedName ] ;
368+ }
369+ if ( this . pendingProps [ normalizedName ] !== undefined ) {
370+ return this . pendingProps [ normalizedName ] ;
371+ }
386372 const value = getDeepData ( this . props , normalizedName ) ;
387373 if ( null === value ) {
388374 return value ;
@@ -396,26 +382,31 @@ class ValueStore {
396382 return this . get ( name ) !== undefined ;
397383 }
398384 set ( name , value ) {
399- let normalizedName = normalizeModelName ( name ) ;
400- if ( this . isPropNameTopLevel ( normalizedName )
401- && this . props [ normalizedName ] !== null
402- && typeof this . props [ normalizedName ] === 'object'
403- && this . props [ normalizedName ] [ this . identifierKey ] !== undefined ) {
404- normalizedName = normalizedName + '.' + this . identifierKey ;
405- }
385+ const normalizedName = normalizeModelName ( name ) ;
406386 const currentValue = this . get ( normalizedName ) ;
407- if ( currentValue !== value && ! this . updatedModels . includes ( normalizedName ) ) {
408- this . updatedModels . push ( normalizedName ) ;
387+ if ( currentValue === value ) {
388+ return false ;
409389 }
410- this . props = setDeepData ( this . props , normalizedName , value ) ;
411- return currentValue !== value ;
390+ this . dirtyProps [ normalizedName ] = value ;
391+ return true ;
412392 }
413- all ( ) {
393+ getOriginalProps ( ) {
414394 return Object . assign ( { } , this . props ) ;
415395 }
396+ getDirtyProps ( ) {
397+ return Object . assign ( { } , this . dirtyProps ) ;
398+ }
399+ flushDirtyPropsToPending ( ) {
400+ this . pendingProps = Object . assign ( { } , this . dirtyProps ) ;
401+ this . dirtyProps = { } ;
402+ }
416403 reinitializeAllProps ( props ) {
417- this . updatedModels = [ ] ;
418404 this . props = props ;
405+ this . pendingProps = { } ;
406+ }
407+ pushPendingPropsBackToDirty ( ) {
408+ this . dirtyProps = Object . assign ( Object . assign ( { } , this . pendingProps ) , this . dirtyProps ) ;
409+ this . pendingProps = { } ;
419410 }
420411 reinitializeProvidedProps ( props ) {
421412 let changed = false ;
@@ -1414,6 +1405,9 @@ class Component {
14141405 this . resetPromise ( ) ;
14151406 this . onChildComponentModelUpdate = this . onChildComponentModelUpdate . bind ( this ) ;
14161407 }
1408+ _swapBackend ( backend ) {
1409+ this . backend = backend ;
1410+ }
14171411 addPlugin ( plugin ) {
14181412 plugin . attachToComponent ( this ) ;
14191413 }
@@ -1535,10 +1529,10 @@ class Component {
15351529 const thisPromiseResolve = this . nextRequestPromiseResolve ;
15361530 this . resetPromise ( ) ;
15371531 this . unsyncedInputsTracker . resetUnsyncedFields ( ) ;
1538- this . backendRequest = this . backend . makeRequest ( this . valueStore . all ( ) , this . pendingActions , this . valueStore . updatedModels , this . getChildrenFingerprints ( ) ) ;
1532+ this . backendRequest = this . backend . makeRequest ( this . valueStore . getOriginalProps ( ) , this . pendingActions , this . valueStore . getDirtyProps ( ) , this . getChildrenFingerprints ( ) ) ;
15391533 this . hooks . triggerHook ( 'loading.state:started' , this . element , this . backendRequest ) ;
15401534 this . pendingActions = [ ] ;
1541- this . valueStore . updatedModels = [ ] ;
1535+ this . valueStore . flushDirtyPropsToPending ( ) ;
15421536 this . isRequestPending = false ;
15431537 this . backendRequest . promise . then ( async ( response ) => {
15441538 this . backendRequest = null ;
@@ -1547,6 +1541,7 @@ class Component {
15471541 const headers = backendResponse . response . headers ;
15481542 if ( headers . get ( 'Content-Type' ) !== 'application/vnd.live-component+html' && ! headers . get ( 'X-Live-Redirect' ) ) {
15491543 const controls = { displayError : true } ;
1544+ this . valueStore . pushPendingPropsBackToDirty ( ) ;
15501545 this . hooks . triggerHook ( 'response:error' , backendResponse , controls ) ;
15511546 if ( controls . displayError ) {
15521547 this . renderError ( html ) ;
@@ -1580,7 +1575,7 @@ class Component {
15801575 }
15811576 this . hooks . triggerHook ( 'loading.state:finished' , this . element ) ;
15821577 const modifiedModelValues = { } ;
1583- this . valueStore . updatedModels . forEach ( ( modelName ) => {
1578+ Object . keys ( this . valueStore . getDirtyProps ( ) ) . forEach ( ( modelName ) => {
15841579 modifiedModelValues [ modelName ] = this . valueStore . get ( modelName ) ;
15851580 } ) ;
15861581 let newElement ;
@@ -1734,12 +1729,12 @@ class BackendRequest {
17341729 }
17351730}
17361731
1737- class Backend {
1732+ class RequestBuilder {
17381733 constructor ( url , csrfToken = null ) {
17391734 this . url = url ;
17401735 this . csrfToken = csrfToken ;
17411736 }
1742- makeRequest ( data , actions , updatedModels , childrenFingerprints ) {
1737+ buildRequest ( props , actions , updated , childrenFingerprints ) {
17431738 const splitUrl = this . url . split ( '?' ) ;
17441739 let [ url ] = splitUrl ;
17451740 const [ , queryString ] = splitUrl ;
@@ -1749,25 +1744,19 @@ class Backend {
17491744 Accept : 'application/vnd.live-component+html' ,
17501745 } ;
17511746 const hasFingerprints = Object . keys ( childrenFingerprints ) . length > 0 ;
1752- const hasUpdatedModels = Object . keys ( updatedModels ) . length > 0 ;
17531747 if ( actions . length === 0 &&
1754- this . willDataFitInUrl ( JSON . stringify ( data ) , params , JSON . stringify ( childrenFingerprints ) ) ) {
1755- params . set ( 'data' , JSON . stringify ( data ) ) ;
1748+ this . willDataFitInUrl ( JSON . stringify ( props ) , JSON . stringify ( updated ) , params , JSON . stringify ( childrenFingerprints ) ) ) {
1749+ params . set ( 'props' , JSON . stringify ( props ) ) ;
1750+ params . set ( 'updated' , JSON . stringify ( updated ) ) ;
17561751 if ( hasFingerprints ) {
17571752 params . set ( 'childrenFingerprints' , JSON . stringify ( childrenFingerprints ) ) ;
17581753 }
1759- updatedModels . forEach ( ( model ) => {
1760- params . append ( 'updatedModels[]' , model ) ;
1761- } ) ;
17621754 fetchOptions . method = 'GET' ;
17631755 }
17641756 else {
17651757 fetchOptions . method = 'POST' ;
17661758 fetchOptions . headers [ 'Content-Type' ] = 'application/json' ;
1767- const requestData = { data } ;
1768- if ( hasUpdatedModels ) {
1769- requestData . updatedModels = updatedModels ;
1770- }
1759+ const requestData = { props, updated } ;
17711760 if ( hasFingerprints ) {
17721761 requestData . childrenFingerprints = childrenFingerprints ;
17731762 }
@@ -1787,14 +1776,27 @@ class Backend {
17871776 fetchOptions . body = JSON . stringify ( requestData ) ;
17881777 }
17891778 const paramsString = params . toString ( ) ;
1790- return new BackendRequest ( fetch ( `${ url } ${ paramsString . length > 0 ? `?${ paramsString } ` : '' } ` , fetchOptions ) , actions . map ( ( backendAction ) => backendAction . name ) , updatedModels ) ;
1779+ return {
1780+ url : `${ url } ${ paramsString . length > 0 ? `?${ paramsString } ` : '' } ` ,
1781+ fetchOptions,
1782+ } ;
17911783 }
1792- willDataFitInUrl ( dataJson , params , childrenFingerprintsJson ) {
1793- const urlEncodedJsonData = new URLSearchParams ( dataJson + childrenFingerprintsJson ) . toString ( ) ;
1784+ willDataFitInUrl ( propsJson , updatedJson , params , childrenFingerprintsJson ) {
1785+ const urlEncodedJsonData = new URLSearchParams ( propsJson + updatedJson + childrenFingerprintsJson ) . toString ( ) ;
17941786 return ( urlEncodedJsonData + params . toString ( ) ) . length < 1500 ;
17951787 }
17961788}
17971789
1790+ class Backend {
1791+ constructor ( url , csrfToken = null ) {
1792+ this . requestBuilder = new RequestBuilder ( url , csrfToken ) ;
1793+ }
1794+ makeRequest ( props , actions , updated , childrenFingerprints ) {
1795+ const { url, fetchOptions } = this . requestBuilder . buildRequest ( props , actions , updated , childrenFingerprints ) ;
1796+ return new BackendRequest ( fetch ( url , fetchOptions ) , actions . map ( ( backendAction ) => backendAction . name ) , Object . keys ( updated ) ) ;
1797+ }
1798+ }
1799+
17981800class StandardElementDriver {
17991801 getModelName ( element ) {
18001802 const modelDirective = getModelDirectiveFromElement ( element , false ) ;
@@ -2219,7 +2221,7 @@ const ComponentRegistry = class {
22192221var ComponentRegistry$1 = new ComponentRegistry ( ) ;
22202222
22212223const getComponent = ( element ) => ComponentRegistry$1 . getComponent ( element ) ;
2222- class default_1 extends Controller {
2224+ class LiveControllerDefault extends Controller {
22232225 constructor ( ) {
22242226 super ( ...arguments ) ;
22252227 this . pendingActionTriggerModelElement = null ;
@@ -2388,7 +2390,7 @@ class default_1 extends Controller {
23882390 this . dispatch ( name , { detail, prefix : 'live' , cancelable, bubbles : canBubble } ) ;
23892391 }
23902392}
2391- default_1 . values = {
2393+ LiveControllerDefault . values = {
23922394 url : String ,
23932395 props : Object ,
23942396 csrf : String ,
@@ -2397,4 +2399,4 @@ default_1.values = {
23972399 fingerprint : String ,
23982400} ;
23992401
2400- export { Component , default_1 as default , getComponent } ;
2402+ export { Component , LiveControllerDefault as default , getComponent } ;
0 commit comments