@@ -457,7 +457,7 @@ export default class extends Controller implements LiveController {
457
457
this . _onLoadingStart ( ) ;
458
458
const paramsString = params . toString ( ) ;
459
459
const thisPromise = fetch ( `${ url } ${ paramsString . length > 0 ? `?${ paramsString } ` : '' } ` , fetchOptions ) ;
460
- this . backendRequest = new BackendRequest ( thisPromise ) ;
460
+ this . backendRequest = new BackendRequest ( thisPromise , actions . map ( action => action . name ) ) ;
461
461
thisPromise . then ( ( response ) => {
462
462
response . text ( ) . then ( ( html ) => {
463
463
this . #processRerender( html , response ) ;
@@ -525,12 +525,13 @@ export default class extends Controller implements LiveController {
525
525
this . _handleLoadingToggle ( true ) ;
526
526
}
527
527
528
- _onLoadingFinish ( ) {
529
- this . _handleLoadingToggle ( false ) ;
528
+ _onLoadingFinish ( targetElement : HTMLElement | SVGElement | null = null ) {
529
+ this . _handleLoadingToggle ( false , targetElement ) ;
530
530
}
531
531
532
- _handleLoadingToggle ( isLoading : boolean ) {
533
- this . _getLoadingDirectives ( ) . forEach ( ( { element, directives } ) => {
532
+ _handleLoadingToggle ( isLoading : boolean , targetElement : HTMLElement | SVGElement | null = null ) {
533
+
534
+ this . _getLoadingDirectives ( targetElement ) . forEach ( ( { element, directives } ) => {
534
535
// so we can track, at any point, if an element is in a "loading" state
535
536
if ( isLoading ) {
536
537
this . _addAttributes ( element , [ 'data-live-is-loading' ] ) ;
@@ -550,6 +551,38 @@ export default class extends Controller implements LiveController {
550
551
_handleLoadingDirective ( element : HTMLElement | SVGElement , isLoading : boolean , directive : Directive ) {
551
552
const finalAction = parseLoadingAction ( directive . action , isLoading ) ;
552
553
554
+ const targetedActions : string [ ] = [ ] ;
555
+ let delay = 0 ;
556
+ directive . modifiers . forEach ( ( modifier => {
557
+ switch ( modifier . name ) {
558
+ case 'delay' : {
559
+ // if loading has *stopped*, the delay modifier has no effect
560
+ if ( ! isLoading ) {
561
+ break ;
562
+ }
563
+
564
+ delay = modifier . value ? parseInt ( modifier . value ) : 200 ;
565
+
566
+ break ;
567
+ }
568
+ case 'action' : {
569
+ if ( ! modifier . value ) {
570
+ throw new Error ( `The "action" in data-loading must have an action name - e.g. action(foo). It's missing for ${ directive . getString ( ) } ` ) ;
571
+ }
572
+ targetedActions . push ( modifier . value ) ;
573
+ break ;
574
+ }
575
+
576
+ default :
577
+ throw new Error ( `Unknown modifier ${ modifier . name } used in the loading directive ${ directive . getString ( ) } ` )
578
+ }
579
+ } ) ) ;
580
+
581
+ // if loading is being activated + action modifier, only apply if the action is on the request
582
+ if ( isLoading && targetedActions . length > 0 && this . backendRequest && ! this . backendRequest . containsOneOfActions ( targetedActions ) ) {
583
+ return ;
584
+ }
585
+
553
586
let loadingDirective : ( ( ) => void ) ;
554
587
555
588
switch ( finalAction ) {
@@ -583,41 +616,24 @@ export default class extends Controller implements LiveController {
583
616
throw new Error ( `Unknown data-loading action "${ finalAction } "` ) ;
584
617
}
585
618
586
- let isHandled = false ;
587
- directive . modifiers . forEach ( ( modifier => {
588
- switch ( modifier . name ) {
589
- case 'delay' : {
590
- // if loading has *stopped*, the delay modifier has no effect
591
- if ( ! isLoading ) {
592
- break ;
593
- }
594
-
595
- const delayLength = modifier . value ? parseInt ( modifier . value ) : 200 ;
596
- window . setTimeout ( ( ) => {
597
- if ( element . hasAttribute ( 'data-live-is-loading' ) ) {
598
- loadingDirective ( ) ;
599
- }
600
- } , delayLength ) ;
601
-
602
- isHandled = true ;
603
-
604
- break ;
619
+ if ( delay ) {
620
+ window . setTimeout ( ( ) => {
621
+ if ( this . isRequestActive ( ) ) {
622
+ loadingDirective ( ) ;
605
623
}
606
- default :
607
- throw new Error ( `Unknown modifier ${ modifier . name } used in the loading directive ${ directive . getString ( ) } ` )
608
- }
609
- } ) ) ;
624
+ } , delay ) ;
610
625
611
- // execute the loading directive
612
- if ( ! isHandled ) {
613
- loadingDirective ( ) ;
626
+ return ;
614
627
}
628
+
629
+ loadingDirective ( ) ;
615
630
}
616
631
617
- _getLoadingDirectives ( ) {
632
+ _getLoadingDirectives ( targetElement : HTMLElement | SVGElement | null = null ) {
618
633
const loadingDirectives : ElementLoadingDirectives [ ] = [ ] ;
634
+ const element = targetElement || this . element ;
619
635
620
- this . element . querySelectorAll ( '[data-loading]' ) . forEach ( ( element => {
636
+ element . querySelectorAll ( '[data-loading]' ) . forEach ( ( element => {
621
637
if ( ! ( element instanceof HTMLElement ) && ! ( element instanceof SVGElement ) ) {
622
638
throw new Error ( 'Invalid Element Type' ) ;
623
639
}
@@ -676,6 +692,8 @@ export default class extends Controller implements LiveController {
676
692
677
693
_executeMorphdom ( newHtml : string , modifiedElements : Array < HTMLElement > ) {
678
694
const newElement = htmlToElement ( newHtml ) ;
695
+ // make sure everything is in non-loading state, the same as the HTML currently on the page
696
+ this . _onLoadingFinish ( newElement ) ;
679
697
morphdom ( this . element , newElement , {
680
698
getNodeKey : ( node : Node ) => {
681
699
if ( ! ( node instanceof HTMLElement ) ) {
@@ -721,11 +739,7 @@ export default class extends Controller implements LiveController {
721
739
}
722
740
723
741
// look for data-live-ignore, and don't update
724
- if ( fromEl . hasAttribute ( 'data-live-ignore' ) ) {
725
- return false ;
726
- }
727
-
728
- return true ;
742
+ return ! fromEl . hasAttribute ( 'data-live-ignore' ) ;
729
743
} ,
730
744
731
745
onBeforeNodeDiscarded ( node ) {
@@ -734,10 +748,7 @@ export default class extends Controller implements LiveController {
734
748
return true ;
735
749
}
736
750
737
- if ( node . hasAttribute ( 'data-live-ignore' ) ) {
738
- return false ;
739
- }
740
- return true ;
751
+ return ! node . hasAttribute ( 'data-live-ignore' ) ;
741
752
}
742
753
} ) ;
743
754
// restore the data-original-data attribute
@@ -1025,13 +1036,26 @@ export default class extends Controller implements LiveController {
1025
1036
this . requestDebounceTimeout = null ;
1026
1037
}
1027
1038
}
1039
+
1040
+ private isRequestActive ( ) : boolean {
1041
+ return ! ! this . backendRequest ;
1042
+ }
1028
1043
}
1029
1044
1030
1045
class BackendRequest {
1031
1046
promise : Promise < any > ;
1047
+ actions : string [ ] ;
1032
1048
1033
- constructor ( promise : Promise < any > ) {
1049
+ constructor ( promise : Promise < any > , actions : string [ ] ) {
1034
1050
this . promise = promise ;
1051
+ this . actions = actions ;
1052
+ }
1053
+
1054
+ /**
1055
+ * Does this BackendRequest contain at least on action in targetedActions?
1056
+ */
1057
+ containsOneOfActions ( targetedActions : string [ ] ) {
1058
+ return ( this . actions . filter ( action => targetedActions . includes ( action ) ) ) . length > 0 ;
1035
1059
}
1036
1060
}
1037
1061
0 commit comments