@@ -468,7 +468,7 @@ export default class extends Controller implements LiveController {
468468 this . _onLoadingStart ( ) ;
469469 const paramsString = params . toString ( ) ;
470470 const thisPromise = fetch ( `${ url } ${ paramsString . length > 0 ? `?${ paramsString } ` : '' } ` , fetchOptions ) ;
471- this . backendRequest = new BackendRequest ( thisPromise ) ;
471+ this . backendRequest = new BackendRequest ( thisPromise , actions . map ( action => action . name ) ) ;
472472 thisPromise . then ( ( response ) => {
473473 response . text ( ) . then ( ( html ) => {
474474 this . #processRerender( html , response ) ;
@@ -536,12 +536,13 @@ export default class extends Controller implements LiveController {
536536 this . _handleLoadingToggle ( true ) ;
537537 }
538538
539- _onLoadingFinish ( ) {
540- this . _handleLoadingToggle ( false ) ;
539+ _onLoadingFinish ( targetElement : HTMLElement | SVGElement | null = null ) {
540+ this . _handleLoadingToggle ( false , targetElement ) ;
541541 }
542542
543- _handleLoadingToggle ( isLoading : boolean ) {
544- this . _getLoadingDirectives ( ) . forEach ( ( { element, directives } ) => {
543+ _handleLoadingToggle ( isLoading : boolean , targetElement : HTMLElement | SVGElement | null = null ) {
544+
545+ this . _getLoadingDirectives ( targetElement ) . forEach ( ( { element, directives } ) => {
545546 // so we can track, at any point, if an element is in a "loading" state
546547 if ( isLoading ) {
547548 this . _addAttributes ( element , [ 'data-live-is-loading' ] ) ;
@@ -561,6 +562,38 @@ export default class extends Controller implements LiveController {
561562 _handleLoadingDirective ( element : HTMLElement | SVGElement , isLoading : boolean , directive : Directive ) {
562563 const finalAction = parseLoadingAction ( directive . action , isLoading ) ;
563564
565+ const targetedActions : string [ ] = [ ] ;
566+ let delay = 0 ;
567+ directive . modifiers . forEach ( ( modifier => {
568+ switch ( modifier . name ) {
569+ case 'delay' : {
570+ // if loading has *stopped*, the delay modifier has no effect
571+ if ( ! isLoading ) {
572+ break ;
573+ }
574+
575+ delay = modifier . value ? parseInt ( modifier . value ) : 200 ;
576+
577+ break ;
578+ }
579+ case 'action' : {
580+ if ( ! modifier . value ) {
581+ throw new Error ( `The "action" in data-loading must have an action name - e.g. action(foo). It's missing for ${ directive . getString ( ) } ` ) ;
582+ }
583+ targetedActions . push ( modifier . value ) ;
584+ break ;
585+ }
586+
587+ default :
588+ throw new Error ( `Unknown modifier ${ modifier . name } used in the loading directive ${ directive . getString ( ) } ` )
589+ }
590+ } ) ) ;
591+
592+ // if loading is being activated + action modifier, only apply if the action is on the request
593+ if ( isLoading && targetedActions . length > 0 && this . backendRequest && ! this . backendRequest . containsOneOfActions ( targetedActions ) ) {
594+ return ;
595+ }
596+
564597 let loadingDirective : ( ( ) => void ) ;
565598
566599 switch ( finalAction ) {
@@ -594,41 +627,24 @@ export default class extends Controller implements LiveController {
594627 throw new Error ( `Unknown data-loading action "${ finalAction } "` ) ;
595628 }
596629
597- let isHandled = false ;
598- directive . modifiers . forEach ( ( modifier => {
599- switch ( modifier . name ) {
600- case 'delay' : {
601- // if loading has *stopped*, the delay modifier has no effect
602- if ( ! isLoading ) {
603- break ;
604- }
605-
606- const delayLength = modifier . value ? parseInt ( modifier . value ) : 200 ;
607- window . setTimeout ( ( ) => {
608- if ( element . hasAttribute ( 'data-live-is-loading' ) ) {
609- loadingDirective ( ) ;
610- }
611- } , delayLength ) ;
612-
613- isHandled = true ;
614-
615- break ;
630+ if ( delay ) {
631+ window . setTimeout ( ( ) => {
632+ if ( this . isRequestActive ( ) ) {
633+ loadingDirective ( ) ;
616634 }
617- default :
618- throw new Error ( `Unknown modifier ${ modifier . name } used in the loading directive ${ directive . getString ( ) } ` )
619- }
620- } ) ) ;
635+ } , delay ) ;
621636
622- // execute the loading directive
623- if ( ! isHandled ) {
624- loadingDirective ( ) ;
637+ return ;
625638 }
639+
640+ loadingDirective ( ) ;
626641 }
627642
628- _getLoadingDirectives ( ) {
643+ _getLoadingDirectives ( targetElement : HTMLElement | SVGElement | null = null ) {
629644 const loadingDirectives : ElementLoadingDirectives [ ] = [ ] ;
645+ const element = targetElement || this . element ;
630646
631- this . element . querySelectorAll ( '[data-loading]' ) . forEach ( ( element => {
647+ element . querySelectorAll ( '[data-loading]' ) . forEach ( ( element => {
632648 if ( ! ( element instanceof HTMLElement ) && ! ( element instanceof SVGElement ) ) {
633649 throw new Error ( 'Invalid Element Type' ) ;
634650 }
@@ -687,6 +703,8 @@ export default class extends Controller implements LiveController {
687703
688704 _executeMorphdom ( newHtml : string , modifiedElements : Array < HTMLElement > ) {
689705 const newElement = htmlToElement ( newHtml ) ;
706+ // make sure everything is in non-loading state, the same as the HTML currently on the page
707+ this . _onLoadingFinish ( newElement ) ;
690708 morphdom ( this . element , newElement , {
691709 getNodeKey : ( node : Node ) => {
692710 if ( ! ( node instanceof HTMLElement ) ) {
@@ -732,11 +750,7 @@ export default class extends Controller implements LiveController {
732750 }
733751
734752 // look for data-live-ignore, and don't update
735- if ( fromEl . hasAttribute ( 'data-live-ignore' ) ) {
736- return false ;
737- }
738-
739- return true ;
753+ return ! fromEl . hasAttribute ( 'data-live-ignore' ) ;
740754 } ,
741755
742756 onBeforeNodeDiscarded ( node ) {
@@ -745,10 +759,7 @@ export default class extends Controller implements LiveController {
745759 return true ;
746760 }
747761
748- if ( node . hasAttribute ( 'data-live-ignore' ) ) {
749- return false ;
750- }
751- return true ;
762+ return ! node . hasAttribute ( 'data-live-ignore' ) ;
752763 }
753764 } ) ;
754765 // restore the data-original-data attribute
@@ -1036,13 +1047,26 @@ export default class extends Controller implements LiveController {
10361047 this . requestDebounceTimeout = null ;
10371048 }
10381049 }
1050+
1051+ private isRequestActive ( ) : boolean {
1052+ return ! ! this . backendRequest ;
1053+ }
10391054}
10401055
10411056class BackendRequest {
10421057 promise : Promise < any > ;
1058+ actions : string [ ] ;
10431059
1044- constructor ( promise : Promise < any > ) {
1060+ constructor ( promise : Promise < any > , actions : string [ ] ) {
10451061 this . promise = promise ;
1062+ this . actions = actions ;
1063+ }
1064+
1065+ /**
1066+ * Does this BackendRequest contain at least on action in targetedActions?
1067+ */
1068+ containsOneOfActions ( targetedActions : string [ ] ) {
1069+ return ( this . actions . filter ( action => targetedActions . includes ( action ) ) ) . length > 0 ;
10461070 }
10471071}
10481072
0 commit comments