@@ -459,21 +459,27 @@ export default class extends Controller implements LiveController {
459459 const thisPromise = fetch ( `${ url } ${ paramsString . length > 0 ? `?${ paramsString } ` : '' } ` , fetchOptions ) ;
460460 const reRenderPromise = new ReRenderPromise ( thisPromise , this . unsyncedInputs . clone ( ) ) ;
461461 this . renderPromiseStack . addPromise ( reRenderPromise ) ;
462- thisPromise . then ( ( response ) => {
462+ thisPromise . then ( async ( response ) => {
463463 if ( action ) {
464464 this . isActionProcessing = false ;
465465 }
466466
467+ // if the response does not contain a component, render as an error
468+ const html = await response . text ( ) ;
469+ if ( response . headers . get ( 'Content-Type' ) !== 'application/vnd.live-component+html' ) {
470+ this . renderError ( html ) ;
471+
472+ return ;
473+ }
474+
467475 // if another re-render is scheduled, do not "run it over"
468476 if ( this . renderDebounceTimeout ) {
469477 return ;
470478 }
471479
472480 const isMostRecent = this . renderPromiseStack . removePromise ( thisPromise ) ;
473481 if ( isMostRecent ) {
474- response . text ( ) . then ( ( html ) => {
475- this . _processRerender ( html , response , reRenderPromise . unsyncedInputContainer ) ;
476- } ) ;
482+ this . _processRerender ( html , response , reRenderPromise . unsyncedInputContainer ) ;
477483 }
478484 } )
479485 }
@@ -1038,6 +1044,56 @@ export default class extends Controller implements LiveController {
10381044 clearInterval ( interval ) ;
10391045 } ) ;
10401046 }
1047+
1048+ // inspired by Livewire!
1049+ private async renderError ( html : string ) {
1050+ let modal = document . getElementById ( 'live-component-error' ) ;
1051+ if ( modal ) {
1052+ modal . innerHTML = '' ;
1053+ } else {
1054+ modal = document . createElement ( 'div' ) ;
1055+ modal . id = 'live-component-error' ;
1056+ modal . style . padding = '50px' ;
1057+ modal . style . backgroundColor = 'rgba(0, 0, 0, .5)' ;
1058+ modal . style . zIndex = '100000' ;
1059+ modal . style . position = 'fixed' ;
1060+ modal . style . width = '100vw' ;
1061+ modal . style . height = '100vh' ;
1062+ }
1063+
1064+ const iframe = document . createElement ( 'iframe' ) ;
1065+ iframe . style . borderRadius = '5px' ;
1066+ iframe . style . width = '100%' ;
1067+ iframe . style . height = '100%' ;
1068+ modal . appendChild ( iframe ) ;
1069+
1070+ document . body . prepend ( modal ) ;
1071+ document . body . style . overflow = 'hidden' ;
1072+ if ( iframe . contentWindow ) {
1073+ iframe . contentWindow . document . open ( ) ;
1074+ iframe . contentWindow . document . write ( html ) ;
1075+ iframe . contentWindow . document . close ( ) ;
1076+ }
1077+
1078+ const closeModal = ( modal : HTMLElement | null ) => {
1079+ if ( modal ) {
1080+ modal . outerHTML = ''
1081+ }
1082+ document . body . style . overflow = 'visible'
1083+ }
1084+
1085+ // close on click
1086+ modal . addEventListener ( 'click' , ( ) => closeModal ( modal ) ) ;
1087+
1088+ // close on escape
1089+ modal . setAttribute ( 'tabindex' , '0' ) ;
1090+ modal . addEventListener ( 'keydown' , e => {
1091+ if ( e . key === 'Escape' ) {
1092+ closeModal ( modal ) ;
1093+ }
1094+ } ) ;
1095+ modal . focus ( ) ;
1096+ }
10411097}
10421098
10431099/**
0 commit comments