@@ -17,6 +17,10 @@ import { Component } from '@compo/exports';
17
17
18
18
export const CustomProviders = { } ;
19
19
20
+ const A_dom_slot = 'dom-slot' ;
21
+ const A_property = 'property' ;
22
+ const A_change_event = 'change-event' ;
23
+
20
24
export class BindingProvider {
21
25
validations = null
22
26
ctx = null
@@ -33,31 +37,37 @@ export class BindingProvider {
33
37
mapToObj : string
34
38
mapToDom : string
35
39
changeEvent : string
36
- typeof : string
40
+ typeOf : string
37
41
38
42
slots : any
39
43
pipes : any
40
44
41
45
parent : any
42
46
47
+ private dismiss : number = 0
48
+ bindingType : 'dual' | 'single'
43
49
44
- dismiss : number = 0
45
- bindingType : string
46
- log = false
47
- logExpression : string
48
- signal_domChanged : string
49
- signal_objectChanged : string
50
+ private log = false
51
+ private logExpression : string
52
+ private signal_domChanged : string
53
+ private signal_objectChanged : string
50
54
51
- pipe_domChanged : { pipe : string , signal : string }
52
- pipe_objectChanged : { pipe : string , signal : string }
53
- locked = false
55
+ private pipe_domChanged : { pipe : string , signal : string }
56
+ private pipe_objectChanged : { pipe : string , signal : string }
57
+ private locked = false
54
58
55
59
domWay : IDomWay = DomObjectTransport . domWay
56
60
objectWay : IObjectWay = DomObjectTransport . objectWay
57
-
61
+
62
+ // -
58
63
binder : Function
64
+ domObserveBinder : Function
65
+
66
+ domListenerType : 'event' | 'signal' | 'pipe' | 'observe'
59
67
60
- constructor ( public model , public element : HTMLElement , public ctr , bindingType ?: string ) {
68
+ owner ;
69
+
70
+ constructor ( public model , public element : HTMLElement , public ctr , bindingType ?: 'dual' | 'single' ) {
61
71
if ( bindingType == null ) {
62
72
bindingType = 'dual' ;
63
73
@@ -68,40 +78,66 @@ export class BindingProvider {
68
78
}
69
79
let attr = ctr . attr ;
70
80
81
+ this . bindingType = bindingType ;
71
82
this . value = attr . value ;
72
- this . property = attr . property ;
83
+ this . property = attr [ A_property ] ;
73
84
this . domSetter = attr [ 'dom-setter' ] || attr . setter ;
74
85
this . domGetter = attr [ 'dom-getter' ] || attr . getter ;
75
86
this . objSetter = attr [ 'obj-setter' ] ;
76
87
this . objGetter = attr [ 'obj-getter' ] ;
77
88
this . mapToObj = attr [ 'map-to-obj' ] ;
78
89
this . mapToDom = attr [ 'map-to-dom' ] ;
79
- this . changeEvent = attr [ 'change-event' ] || 'change' ;
90
+ this . owner = ctr . parent ;
91
+
92
+ this . changeEvent = attr [ A_change_event ] || 'change' ;
80
93
81
94
/* Convert to an instance, e.g. Number, on domchange event */
82
- this [ 'typeof' ] = attr [ 'typeof' ] || null ;
83
-
84
- this . bindingType = bindingType ;
95
+ this . typeOf = attr [ 'typeof' ] || null ;
96
+
97
+ let isCompoBinder = ctr . node . parent . tagName === this . owner . compoName ;
98
+ switch ( true ) {
99
+ case ( A_dom_slot in attr ) :
100
+ this . domListenerType = 'signal' ;
101
+ break ;
102
+ case ( A_change_event in attr ) :
103
+ this . domListenerType = 'event' ;
104
+ break ;
105
+ case ( isCompoBinder && ( A_property in attr ) ) :
106
+ this . domListenerType = 'observe' ;
107
+ break ;
108
+ }
85
109
86
- let isCompoBinder = ctr . node . parent . tagName === ctr . parent . compoName ;
87
- if (
88
- isCompoBinder &&
89
- ( element . nodeType !== 1 || element . tagName !== 'INPUT' )
90
- ) {
91
- if ( this . domSetter == null ) this . domSetter = 'setValue' ;
92
- if ( this . domGetter == null ) this . domGetter = 'getValue' ;
93
- if ( attr [ 'dom-slot' ] == null ) attr [ 'dom-slot' ] = 'input' ;
110
+ if ( isCompoBinder ) {
111
+ if ( this . domListenerType === 'observe' ) {
112
+ this . domWay = DomObjectTransport . domModelWay ;
113
+ } else {
114
+ let isInput = element . nodeType === 1 && ( element . tagName === 'INPUT' || element . tagName === 'TEXTAREA' ) ;
115
+ if ( isInput === false ) {
116
+ if ( this . domSetter == null ) this . domSetter = 'setValue' ;
117
+ if ( this . domGetter == null ) this . domGetter = 'getValue' ;
118
+ if ( attr [ A_dom_slot ] == null ) attr [ A_dom_slot ] = 'input' ;
119
+ }
120
+ }
121
+ }
122
+ if ( this . domListenerType == null ) {
123
+ this . domListenerType = 'event' ;
94
124
}
95
125
96
126
if ( this . property == null && this . domGetter == null ) {
97
127
switch ( element . tagName ) {
98
128
case 'INPUT' :
99
129
// Do not use .type accessor, as some browsers do not support e.g. date
100
- var type = element . getAttribute ( 'type' ) ;
130
+ let type = element . getAttribute ( 'type' ) ;
101
131
if ( 'checkbox' === type ) {
102
132
this . property = 'element.checked' ;
103
133
break ;
104
- } else if (
134
+ }
135
+ if ( 'radio' === type ) {
136
+ this . domWay = DomObjectTransport . RADIO . domWay ;
137
+ break ;
138
+ }
139
+
140
+ if (
105
141
'date' === type ||
106
142
'time' === type ||
107
143
'month' === type
@@ -110,13 +146,9 @@ export class BindingProvider {
110
146
this . domWay = x . domWay ;
111
147
this . objectWay = x . objectWay ;
112
148
} else if ( 'number' === type ) {
113
- this [ 'typeof' ] = 'Number' ;
114
- } else if ( 'radio' === type ) {
115
- var x = DomObjectTransport . RADIO ;
116
- this . domWay = x . domWay ;
117
- break ;
118
- }
119
- this . changeEvent = attr [ 'change-event' ] || 'change,input' ;
149
+ this [ 'typeOf' ] = 'Number' ;
150
+ }
151
+ this . changeEvent = attr [ A_change_event ] || 'change,input' ;
120
152
this . property = 'element.value' ;
121
153
break ;
122
154
case 'TEXTAREA' :
@@ -170,7 +202,7 @@ export class BindingProvider {
170
202
}
171
203
}
172
204
173
- var domSlot = attr [ 'dom-slot' ] ;
205
+ var domSlot = attr [ A_dom_slot ] ;
174
206
if ( domSlot != null ) {
175
207
this . slots = { } ;
176
208
// @hack - place dualb. provider on the way of a signal
@@ -221,7 +253,12 @@ export class BindingProvider {
221
253
this . expression = this . value ;
222
254
}
223
255
dispose ( ) {
224
- expression_unbind ( this . expression , this . model , this . ctr , this . binder ) ;
256
+ if ( this . binder != null ) {
257
+ expression_unbind ( this . expression , this . model , this . ctr , this . binder ) ;
258
+ }
259
+ if ( this . domObserveBinder != null ) {
260
+ expression_unbind ( this . property , this . ctr , this . ctr , this . domObserveBinder ) ;
261
+ }
225
262
}
226
263
objectChanged ( val ?) {
227
264
if ( this . dismiss -- > 0 ) {
@@ -272,7 +309,7 @@ export class BindingProvider {
272
309
if ( val == null ) {
273
310
val = this . domWay . get ( this ) ;
274
311
}
275
- let typeof_ = this [ 'typeof ' ] ;
312
+ let typeof_ = this [ 'typeOf ' ] ;
276
313
if ( typeof_ != null ) {
277
314
let Converter = window [ typeof_ ] ;
278
315
val = Converter ( val ) ;
@@ -375,11 +412,9 @@ export class BindingProvider {
375
412
376
413
377
414
function apply_bind ( provider : BindingProvider ) {
378
- var expr = provider . expression ,
415
+ let expr = provider . expression ,
379
416
model = provider . model ,
380
- onObjChanged = ( provider . objectChanged = provider . objectChanged . bind (
381
- provider
382
- ) ) ;
417
+ onObjChanged = provider . objectChanged = provider . objectChanged . bind ( provider ) ;
383
418
384
419
provider . binder = expression_createBinder (
385
420
expr ,
@@ -392,25 +427,31 @@ function apply_bind(provider: BindingProvider) {
392
427
expression_bind ( expr , model , provider . ctx , provider . ctr , provider . binder ) ;
393
428
394
429
if ( provider . bindingType === 'dual' ) {
395
- var attr = provider . ctr . attr ;
396
-
397
- if ( ! attr [ 'dom-slot' ] && ! attr [ 'change-pipe-event' ] ) {
398
- var element = provider . element ,
399
- eventType = provider . changeEvent ,
400
- onDomChange = provider . domChanged . bind ( provider ) ,
401
- doListen = Component . Dom . addEventListener ;
402
-
403
- if ( eventType . indexOf ( ',' ) !== - 1 ) {
404
- let arr = eventType . split ( ',' ) ;
405
- for ( let i = 0 ; i < arr . length ; i ++ ) {
406
- doListen ( element , arr [ i ] . trim ( ) , onDomChange ) ;
430
+
431
+ let onDomChange = provider . domChanged . bind ( provider ) ;
432
+ switch ( provider . domListenerType ) {
433
+ case 'event' : {
434
+ let el = provider . element ,
435
+ event = provider . changeEvent ,
436
+ attachListener = Component . Dom . addEventListener ;
437
+
438
+ if ( event . indexOf ( ',' ) !== - 1 ) {
439
+ let arr = event . split ( ',' ) ;
440
+ for ( let i = 0 ; i < arr . length ; i ++ ) {
441
+ attachListener ( el , arr [ i ] . trim ( ) , onDomChange ) ;
442
+ }
407
443
}
444
+ attachListener ( el , event , onDomChange ) ;
445
+ break ;
446
+ }
447
+ case 'observe' : {
448
+ provider . domObserveBinder = onDomChange ;
449
+ expression_bind ( provider . property , provider . owner , provider . ctx , null , onDomChange ) ;
450
+ break ;
408
451
}
409
- doListen ( element , eventType , onDomChange ) ;
410
452
}
411
-
412
453
if ( provider . objectWay . get ( provider , provider . expression ) == null ) {
413
- // object has no value, so check the dom
454
+ // object has no value, so check the dom
414
455
setTimeout ( function ( ) {
415
456
if ( provider . domWay . get ( provider ) )
416
457
// and apply when exists
0 commit comments