11import Model from './Model' ;
22
3- import { parseDOM , scanTemplate , stringifyDOM , attributeMap } from './utility' ;
3+ import {
4+ parseDOM ,
5+ scanTemplate ,
6+ stringifyDOM ,
7+ attributeMap ,
8+ nextTick
9+ } from './utility' ;
410
511import Template from './Template' ;
612
713const { forEach, push } = Array . prototype ;
814
15+ const iterator = [ ] [ Symbol . iterator ] ;
16+
917const view_template = Symbol ( 'View template' ) ,
10- view_top = new Map ( ) ,
18+ view_top = new WeakMap ( ) ,
1119 view_injection = Symbol ( 'View injection' ) ,
12- view_varible = [ 'view' , 'scope' ] ;
20+ view_varible = [ 'view' , 'scope' ] ,
21+ element_view = new WeakMap ( ) ;
1322
1423export default class View extends Model {
1524 /**
@@ -23,22 +32,28 @@ export default class View extends Model {
2332 ( this [ view_template ] = template + '' ) ,
2433 ( this [ view_injection ] = injection ) ;
2534
35+ const top = [ ] ;
36+
37+ view_top . set ( this , top ) ;
38+
2639 forEach . call ( parseDOM ( template ) . childNodes , node => {
27- view_top . set ( node , this ) ;
40+ top . push ( node ) ;
2841
2942 if ( node . nodeType === 1 ) this . parseTree ( node ) ;
3043 } ) ;
44+
45+ Object . freeze ( top ) ;
46+ }
47+
48+ [ Symbol . iterator ] ( ) {
49+ return iterator . call ( this ) ;
3150 }
3251
3352 /**
3453 * @type {Node[] }
3554 */
3655 get topNodes ( ) {
37- const list = [ ] ;
38-
39- view_top . forEach ( ( view , node ) => view === this && list . push ( node ) ) ;
40-
41- return list ;
56+ return view_top . get ( this ) ;
4257 }
4358
4459 /**
@@ -48,15 +63,6 @@ export default class View extends Model {
4863 return stringifyDOM ( this . topNodes ) ;
4964 }
5065
51- /**
52- * @param {Element } root
53- */
54- static clear ( root ) {
55- Array . from ( view_top . keys ( ) )
56- . filter ( node => root . compareDocumentPosition ( node ) & 16 )
57- . forEach ( node => ( view_top . delete ( node ) , node . remove ( ) ) ) ;
58- }
59-
6066 /**
6167 * @return {View }
6268 */
@@ -80,6 +86,31 @@ export default class View extends Model {
8086 return ( root . innerHTML = '' ) || raw ;
8187 }
8288
89+ /**
90+ * @protected
91+ *
92+ * @param {String } type
93+ * @param {Element } element
94+ * @param {Template|View } renderer
95+ */
96+ addNode ( type , element , renderer ) {
97+ const name = element . dataset . view ;
98+
99+ push . call ( this , { type, element, renderer, name } ) ;
100+
101+ if ( type !== 'View' ) return ;
102+
103+ const sub_view = [ ] ;
104+
105+ element_view . set ( element , sub_view ) ;
106+
107+ if ( ! ( name in this ) )
108+ Object . defineProperty ( this , name , {
109+ get : ( ) => ( sub_view [ 1 ] ? sub_view : sub_view [ 0 ] ) ,
110+ enumerable : true
111+ } ) ;
112+ }
113+
83114 /**
84115 * @protected
85116 *
@@ -94,39 +125,39 @@ export default class View extends Model {
94125 attribute : ( { ownerElement, name, value } ) => {
95126 name = attributeMap [ name ] || name ;
96127
97- push . call ( this , {
98- type : 'Attr' ,
99- element : ownerElement ,
100- renderer : new Template (
128+ this . addNode (
129+ 'Attr' ,
130+ ownerElement ,
131+ new Template (
101132 value ,
102133 injection ,
103134 name in ownerElement
104135 ? value => ( ownerElement [ name ] = value )
105136 : value => ownerElement . setAttribute ( name , value )
106137 )
107- } ) ;
138+ ) ;
108139 } ,
109140 text : node => {
110141 const { parentNode } = node ;
111142
112- push . call ( this , {
113- type : 'Text' ,
114- element : parentNode ,
115- renderer : new Template (
143+ this . addNode (
144+ 'Text' ,
145+ parentNode ,
146+ new Template (
116147 node . nodeValue ,
117148 injection ,
118149 parentNode . firstElementChild
119150 ? value => ( node . nodeValue = value )
120151 : value => ( parentNode . innerHTML = value )
121152 )
122- } ) ;
153+ ) ;
123154 } ,
124155 view : node =>
125- push . call ( this , {
126- type : 'View' ,
127- element : node ,
128- renderer : new View ( View . getTemplate ( node ) . trim ( ) , this . data )
129- } )
156+ this . addNode (
157+ 'View' ,
158+ node ,
159+ new View ( View . getTemplate ( node ) . trim ( ) , this . data )
160+ )
130161 } ) ;
131162 }
132163
@@ -135,42 +166,63 @@ export default class View extends Model {
135166 *
136167 * @return {View }
137168 */
138- render ( data ) {
169+ async render ( data ) {
139170 data = this . patch ( data ) ;
140171
141172 const injection = [ data , this . scope ] . concat (
142173 Object . values ( this [ view_injection ] )
143174 ) ;
144175
145176 forEach . call ( this , ( { type, element, renderer } ) => {
146- switch ( type ) {
147- case 'Attr' :
148- case 'Text' :
149- return renderer . evaluate . apply (
150- renderer ,
151- [ element ] . concat ( injection )
152- ) ;
177+ if ( type !== 'View' )
178+ renderer . evaluate . apply ( renderer , [ element ] . concat ( injection ) ) ;
179+ } ) ;
180+
181+ for ( let { type, element, renderer, name } of this )
182+ if ( type === 'View' ) {
183+ await nextTick ( ) ;
184+
185+ await this . renderSub ( data [ name ] , element , renderer ) ;
153186 }
187+ }
188+
189+ destroy ( ) {
190+ view_top . get ( this ) . forEach ( node => node . remove ( ) ) ;
191+
192+ view_top . delete ( this ) ;
193+ }
194+
195+ /**
196+ * @protected
197+ *
198+ * @param {Object } data
199+ * @param {Element } element
200+ * @param {View } renderer
201+ */
202+ async renderSub ( data , element , renderer ) {
203+ if ( ! data && data !== null ) return ;
154204
155- var _data_ = data [ element . dataset . view ] ;
205+ const sub = element_view . get ( element ) ;
156206
157- if ( ! _data_ && _data_ !== null ) return ;
207+ if ( ! data ) {
208+ sub . forEach ( view => view . destroy ( ) ) ;
158209
159- View . clear ( element ) ;
210+ return ( sub . length = 0 ) ;
211+ }
160212
161- if ( ! _data_ ) return ;
213+ if ( ! ( data instanceof Array ) ) data = [ data ] ;
162214
163- if ( ! ( _data_ instanceof Array ) ) _data_ = [ _data_ ] ;
215+ sub . splice ( data . length , Infinity ) . forEach ( view => view . destroy ( ) ) ;
164216
165- element . append . apply (
166- element ,
167- [ ] . concat . apply (
168- [ ] ,
169- _data_ . map ( item => renderer . clone ( ) . render ( item ) . topNodes )
170- )
171- ) ;
172- } ) ;
217+ for ( let i = 0 ; data [ i ] ; i ++ ) {
218+ sub [ i ] = sub [ i ] || renderer . clone ( ) ;
173219
174- return this ;
220+ await sub [ i ] . render ( data [ i ] ) ;
221+ }
222+
223+ element . append . apply (
224+ element ,
225+ [ ] . concat . apply ( [ ] , sub . map ( view => view . topNodes ) )
226+ ) ;
175227 }
176228}
0 commit comments