@@ -8,60 +8,77 @@ import {
88 getValueFromElement ,
99 elementBelongsToThisController ,
1010} from './dom_utils' ;
11- import Component , { createComponent } from "./Component" ;
11+ import Component , { proxifyComponent } from "./Component" ;
1212import Backend from "./Backend" ;
13- import { DataModelElementResolver } from "./Component/ModelElementResolver" ;
13+ import {
14+ DataModelElementResolver ,
15+ } from "./Component/ModelElementResolver" ;
1416import LoadingHelper from "./LoadingHelper" ;
1517
1618interface UpdateModelOptions {
1719 dispatch ?: boolean ;
1820 debounce ?: number | boolean ;
1921}
2022
21- export interface LiveController {
22- dataValue : any ;
23- element : Element ,
24- childComponentControllers : Array < LiveController >
23+ export interface LiveEvent extends CustomEvent {
24+ detail : {
25+ controller : LiveController ,
26+ component : Component
27+ } ,
2528}
2629
27- export default class extends Controller implements LiveController {
30+ export default class LiveController extends Controller {
2831 static values = {
2932 url : String ,
3033 data : Object ,
3134 csrf : String ,
3235 debounce : { type : Number , default : 150 } ,
36+ id : String ,
3337 }
3438
3539 readonly urlValue ! : string ;
3640 dataValue ! : any ;
3741 readonly csrfValue ! : string ;
3842 readonly hasDebounceValue : boolean ;
3943 readonly debounceValue : number ;
44+ readonly idValue : string ;
45+
46+ /** The component, wrapped in the convenience Proxy */
47+ private proxiedComponent : Component ;
48+ /** The raw Component object */
49+ private component : Component ;
4050
41- component : Component ;
4251 isConnected = false ;
43- childComponentControllers : Array < LiveController > = [ ] ;
4452 pendingActionTriggerModelElement : HTMLElement | null = null ;
4553
4654 private elementEventListeners : Array < { event : string , callback : ( event : any ) => void } > = [
47- { event : 'input' , callback : ( event ) => this . handleInputEvent ( event ) } ,
48- { event : 'change' , callback : ( event ) => this . handleChangeEvent ( event ) } ,
55+ { event : 'input' , callback : ( event ) => this . handleInputEvent ( event ) } ,
56+ { event : 'change' , callback : ( event ) => this . handleChangeEvent ( event ) } ,
57+ { event : 'live:connect' , callback : ( event ) => this . handleConnectedControllerEvent ( event ) } ,
4958 ] ;
5059
5160 initialize ( ) {
52- this . handleConnectedControllerEvent = this . handleConnectedControllerEvent . bind ( this ) ;
53- this . handleDisconnectedControllerEvent = this . handleDisconnectedControllerEvent . bind ( this ) ;
61+ this . handleDisconnectedChildControllerEvent = this . handleDisconnectedChildControllerEvent . bind ( this ) ;
5462
5563 if ( ! ( this . element instanceof HTMLElement ) ) {
5664 throw new Error ( 'Invalid Element Type' ) ;
5765 }
5866
59- this . component = createComponent (
67+
68+ const id = this . idValue || null ;
69+
70+ this . component = new Component (
6071 this . element ,
6172 this . dataValue ,
73+ id ,
6274 new Backend ( this . urlValue , this . csrfValue ) ,
6375 new DataModelElementResolver ( ) ,
6476 ) ;
77+ this . proxiedComponent = proxifyComponent ( this . component ) ;
78+
79+ // @ts -ignore Adding the dynamic property
80+ this . element . __component = this . proxiedComponent ;
81+
6582 if ( this . hasDebounceValue ) {
6683 this . component . defaultDebounce = this . debounceValue ;
6784 }
@@ -100,9 +117,7 @@ export default class extends Controller implements LiveController {
100117 throw new Error ( 'Invalid Element Type' ) ;
101118 }
102119
103- this . element . addEventListener ( 'live:connect' , this . handleConnectedControllerEvent ) ;
104-
105- this . _dispatchEvent ( 'live:connect' , { controller : this } ) ;
120+ this . _dispatchEvent ( 'live:connect' ) ;
106121 }
107122
108123 disconnect ( ) {
@@ -112,11 +127,8 @@ export default class extends Controller implements LiveController {
112127 this . component . element . removeEventListener ( event , callback ) ;
113128 } ) ;
114129
115- this . element . removeEventListener ( 'live:connect' , this . handleConnectedControllerEvent ) ;
116- this . element . removeEventListener ( 'live:disconnect' , this . handleDisconnectedControllerEvent ) ;
117-
118- this . _dispatchEvent ( 'live:disconnect' , { controller : this } ) ;
119130 this . isConnected = false ;
131+ this . _dispatchEvent ( 'live:disconnect' ) ;
120132 }
121133
122134 /**
@@ -334,37 +346,49 @@ export default class extends Controller implements LiveController {
334346 this . component . set ( modelDirective . action , finalValue , shouldRender , debounce ) ;
335347 }
336348
337- handleConnectedControllerEvent ( event : any ) {
349+ handleConnectedControllerEvent ( event : LiveEvent ) {
338350 if ( event . target === this . element ) {
339351 return ;
340352 }
341353
342- this . childComponentControllers . push ( event . detail . controller ) ;
354+ const childController = event . detail . controller ;
355+ if ( childController . component . getParent ( ) ) {
356+ // child already has a parent - we are a grandparent
357+ return ;
358+ }
359+
360+ this . component . addChild ( childController . component ) ;
361+
343362 // live:disconnect needs to be registered on the child element directly
344363 // that's because if the child component is removed from the DOM, then
345364 // the parent controller is no longer an ancestor, so the live:disconnect
346365 // event would not bubble up to it.
347- event . detail . controller . element . addEventListener ( 'live:disconnect' , this . handleDisconnectedControllerEvent ) ;
366+ // @ts -ignore TS doesn't like the LiveEvent arg in the listener, not sure how to fix
367+ childController . element . addEventListener ( 'live:disconnect' , this . handleDisconnectedChildControllerEvent ) ;
348368 }
349369
350- handleDisconnectedControllerEvent ( event : any ) {
351- if ( event . target === this . element ) {
352- return ;
353- }
370+ handleDisconnectedChildControllerEvent ( event : LiveEvent ) : void {
371+ const childController = event . detail . controller ;
354372
355- const index = this . childComponentControllers . indexOf ( event . detail . controller ) ;
373+ // @ts -ignore TS doesn't like the LiveEvent arg in the listener, not sure how to fix
374+ childController . element . removeEventListener ( 'live:disconnect' , this . handleDisconnectedChildControllerEvent ) ;
356375
357- // Remove value from an array
358- if ( index > - 1 ) {
359- this . childComponentControllers . splice ( index , 1 ) ;
376+ // this shouldn't happen: but double-check we're the parent
377+ if ( childController . component . getParent ( ) !== this . component ) {
378+ return ;
360379 }
380+
381+ this . component . removeChild ( childController . component ) ;
361382 }
362383
363- _dispatchEvent ( name : string , payload : any = null , canBubble = true , cancelable = false ) {
384+ _dispatchEvent ( name : string , detail : any = { } , canBubble = true , cancelable = false ) {
385+ detail . controller = this ;
386+ detail . component = this . proxiedComponent ;
387+
364388 return this . element . dispatchEvent ( new CustomEvent ( name , {
365389 bubbles : canBubble ,
366390 cancelable,
367- detail : payload
391+ detail
368392 } ) ) ;
369393 }
370394
0 commit comments