@@ -468,7 +468,7 @@ export default class extends Controller implements LiveController {
468
468
this . _onLoadingStart ( ) ;
469
469
const paramsString = params . toString ( ) ;
470
470
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 ) ) ;
472
472
thisPromise . then ( ( response ) => {
473
473
response . text ( ) . then ( ( html ) => {
474
474
this . #processRerender( html , response ) ;
@@ -536,12 +536,13 @@ export default class extends Controller implements LiveController {
536
536
this . _handleLoadingToggle ( true ) ;
537
537
}
538
538
539
- _onLoadingFinish ( ) {
540
- this . _handleLoadingToggle ( false ) ;
539
+ _onLoadingFinish ( targetElement : HTMLElement | SVGElement | null = null ) {
540
+ this . _handleLoadingToggle ( false , targetElement ) ;
541
541
}
542
542
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 } ) => {
545
546
// so we can track, at any point, if an element is in a "loading" state
546
547
if ( isLoading ) {
547
548
this . _addAttributes ( element , [ 'data-live-is-loading' ] ) ;
@@ -561,6 +562,38 @@ export default class extends Controller implements LiveController {
561
562
_handleLoadingDirective ( element : HTMLElement | SVGElement , isLoading : boolean , directive : Directive ) {
562
563
const finalAction = parseLoadingAction ( directive . action , isLoading ) ;
563
564
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
+
564
597
let loadingDirective : ( ( ) => void ) ;
565
598
566
599
switch ( finalAction ) {
@@ -594,41 +627,24 @@ export default class extends Controller implements LiveController {
594
627
throw new Error ( `Unknown data-loading action "${ finalAction } "` ) ;
595
628
}
596
629
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 ( ) ;
616
634
}
617
- default :
618
- throw new Error ( `Unknown modifier ${ modifier . name } used in the loading directive ${ directive . getString ( ) } ` )
619
- }
620
- } ) ) ;
635
+ } , delay ) ;
621
636
622
- // execute the loading directive
623
- if ( ! isHandled ) {
624
- loadingDirective ( ) ;
637
+ return ;
625
638
}
639
+
640
+ loadingDirective ( ) ;
626
641
}
627
642
628
- _getLoadingDirectives ( ) {
643
+ _getLoadingDirectives ( targetElement : HTMLElement | SVGElement | null = null ) {
629
644
const loadingDirectives : ElementLoadingDirectives [ ] = [ ] ;
645
+ const element = targetElement || this . element ;
630
646
631
- this . element . querySelectorAll ( '[data-loading]' ) . forEach ( ( element => {
647
+ element . querySelectorAll ( '[data-loading]' ) . forEach ( ( element => {
632
648
if ( ! ( element instanceof HTMLElement ) && ! ( element instanceof SVGElement ) ) {
633
649
throw new Error ( 'Invalid Element Type' ) ;
634
650
}
@@ -687,6 +703,8 @@ export default class extends Controller implements LiveController {
687
703
688
704
_executeMorphdom ( newHtml : string , modifiedElements : Array < HTMLElement > ) {
689
705
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 ) ;
690
708
morphdom ( this . element , newElement , {
691
709
getNodeKey : ( node : Node ) => {
692
710
if ( ! ( node instanceof HTMLElement ) ) {
@@ -732,11 +750,7 @@ export default class extends Controller implements LiveController {
732
750
}
733
751
734
752
// 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' ) ;
740
754
} ,
741
755
742
756
onBeforeNodeDiscarded ( node ) {
@@ -745,10 +759,7 @@ export default class extends Controller implements LiveController {
745
759
return true ;
746
760
}
747
761
748
- if ( node . hasAttribute ( 'data-live-ignore' ) ) {
749
- return false ;
750
- }
751
- return true ;
762
+ return ! node . hasAttribute ( 'data-live-ignore' ) ;
752
763
}
753
764
} ) ;
754
765
// restore the data-original-data attribute
@@ -1036,13 +1047,26 @@ export default class extends Controller implements LiveController {
1036
1047
this . requestDebounceTimeout = null ;
1037
1048
}
1038
1049
}
1050
+
1051
+ private isRequestActive ( ) : boolean {
1052
+ return ! ! this . backendRequest ;
1053
+ }
1039
1054
}
1040
1055
1041
1056
class BackendRequest {
1042
1057
promise : Promise < any > ;
1058
+ actions : string [ ] ;
1043
1059
1044
- constructor ( promise : Promise < any > ) {
1060
+ constructor ( promise : Promise < any > , actions : string [ ] ) {
1045
1061
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 ;
1046
1070
}
1047
1071
}
1048
1072
0 commit comments