1 | define([ |
---|
2 | "dojo/_base/declare", |
---|
3 | "dojo/_base/lang", |
---|
4 | "dojo/_base/sniff", |
---|
5 | "dojo/_base/window", |
---|
6 | "dojo/dom", |
---|
7 | "dojo/dom-construct", |
---|
8 | "dojo/_base/array", |
---|
9 | "dojo/query", |
---|
10 | "dojo/when", |
---|
11 | "dijit/registry", |
---|
12 | "./_Container" |
---|
13 | ], function(declare, lang, has, win, dom, domconstruct, array, query, when, registry, _Container){ |
---|
14 | |
---|
15 | return declare("dojox.mvc.Repeat", _Container, { |
---|
16 | // summary: |
---|
17 | // A model-bound container which binds to a collection within a data model |
---|
18 | // and produces a repeating user-interface from a template for each |
---|
19 | // iteration within the collection. |
---|
20 | // |
---|
21 | // description: |
---|
22 | // A repeat is bound to an intermediate dojo/Stateful node corresponding |
---|
23 | // to an array in the data model. Child dijits or custom view components |
---|
24 | // inside it inherit their parent data binding context from it. |
---|
25 | |
---|
26 | // index: Integer |
---|
27 | // An index used to track the current iteration when the repeating UI is |
---|
28 | // produced. This may be used to parameterize the content in the repeat |
---|
29 | // template for the current iteration. |
---|
30 | // |
---|
31 | // For example, consider a collection of search or query results where |
---|
32 | // each item contains a "Name" property used to prime the "Results" data |
---|
33 | // model. Then, the following CRUD-style UI displays all the names in |
---|
34 | // the search results in text boxes where they may be updated or such. |
---|
35 | // |
---|
36 | // | <div dojoType="dojox/mvc/Repeat" ref="Results"> |
---|
37 | // | <div class="row" dojoType="dojox/mvc/Group" ref="${this.index}"> |
---|
38 | // | <label for="nameInput${this.index}">Name:</label> |
---|
39 | // | <input dojoType="dijit/form/TextBox" id="nameInput${this.index}" ref="'Name'"></input> |
---|
40 | // | </div> |
---|
41 | // | </div> |
---|
42 | index : 0, |
---|
43 | |
---|
44 | // useParent: String |
---|
45 | // id of the DOM node to use as the parent for the repeating items, similar to useParentId processed a little differently |
---|
46 | useParent : "", |
---|
47 | |
---|
48 | // removeRepeatNode: boolean |
---|
49 | // When true the dom node for the Repeat and Groups within the Repeat |
---|
50 | // will be removed, their children will be placed into the parent node |
---|
51 | // of the Repeat node. This should be set to true when working with |
---|
52 | // a Repeat inside of a dojox.mobile list. |
---|
53 | removeRepeatNode : false, |
---|
54 | |
---|
55 | // children: dojox/mvc/StatefulArray |
---|
56 | // The array of data model that is used to render child nodes. |
---|
57 | children: null, |
---|
58 | |
---|
59 | // _relTargetProp: String |
---|
60 | // The name of the property that is used by child widgets for relative data binding. |
---|
61 | _relTargetProp : "children", |
---|
62 | |
---|
63 | startup: function(){ |
---|
64 | // This code needed for ticket 14423 is using removeRepeatNode to work with mobile.lists |
---|
65 | // this.select and this.onCheckStateChanged are called by ListItem so they need to be set |
---|
66 | // but it seems like a bit of a hack. |
---|
67 | if(this.removeRepeatNode){ |
---|
68 | var parent = null; |
---|
69 | if(lang.isFunction(this.getParent)){ |
---|
70 | if(this.getParent()){ |
---|
71 | this.select = this.getParent().select; |
---|
72 | this.onCheckStateChanged = this.getParent().onCheckStateChanged; |
---|
73 | } |
---|
74 | } |
---|
75 | } |
---|
76 | |
---|
77 | this.inherited(arguments); |
---|
78 | this._setChildrenAttr(this.children); |
---|
79 | }, |
---|
80 | |
---|
81 | // summary: |
---|
82 | // Override and save template from body. |
---|
83 | postscript: function(params, srcNodeRef){ |
---|
84 | //this.srcNodeRef = dom.byId(srcNodeRef); |
---|
85 | if(this.useParent && dom.byId(this.useParent)){ |
---|
86 | this.srcNodeRef = dom.byId(this.useParent); |
---|
87 | } else{ |
---|
88 | this.srcNodeRef = dom.byId(srcNodeRef); |
---|
89 | } |
---|
90 | if(this.srcNodeRef){ |
---|
91 | var prop = this._attachTemplateNodes ? "inlineTemplateString" : "templateString"; |
---|
92 | if(this[prop] == ""){ // only overwrite templateString if it has not been set |
---|
93 | this[prop] = this.srcNodeRef.innerHTML; |
---|
94 | } |
---|
95 | try{ |
---|
96 | this.srcNodeRef.innerHTML = ""; |
---|
97 | }catch(e){ |
---|
98 | while(this.srcNodeRef.firstChild){ this.srcNodeRef.removeChild(this.srcNodeRef.firstChild); } |
---|
99 | } |
---|
100 | |
---|
101 | } |
---|
102 | this.inherited(arguments); |
---|
103 | }, |
---|
104 | |
---|
105 | ////////////////////// PRIVATE METHODS //////////////////////// |
---|
106 | |
---|
107 | _setChildrenAttr: function(/*dojo/Stateful*/ value){ |
---|
108 | // summary: |
---|
109 | // Handler for calls to set("children", val). |
---|
110 | // description: |
---|
111 | // Sets "ref" property so that child widgets can refer to, and then rebuilds the children. |
---|
112 | |
---|
113 | var children = this.children; |
---|
114 | this._set("children", value); |
---|
115 | // this.binding is the resolved ref, so not matching with the new value means change in repeat target. |
---|
116 | if(this.binding != value){ |
---|
117 | this.set("ref", value); |
---|
118 | } |
---|
119 | if(this._started && (!this._builtOnce || children != value)){ |
---|
120 | this._builtOnce = true; |
---|
121 | this._buildContained(value); |
---|
122 | } |
---|
123 | }, |
---|
124 | |
---|
125 | _buildContained: function(/*dojox/mvc/StatefulArray*/ children){ |
---|
126 | // summary: |
---|
127 | // Destroy any existing contained view, recreate the repeating UI |
---|
128 | // markup and parse the new contents. |
---|
129 | // children: dojox/mvc/StatefulArray |
---|
130 | // The array of child widgets. |
---|
131 | // tags: |
---|
132 | // private |
---|
133 | |
---|
134 | if(!children){ return; } |
---|
135 | |
---|
136 | // TODO: Potential optimization: only create new widgets for insert, only destroy for delete. |
---|
137 | if(this.useParent && dom.byId(this.useParent)){ |
---|
138 | this.srcNodeRef = dom.byId(this.useParent); |
---|
139 | } |
---|
140 | |
---|
141 | this._destroyBody(); |
---|
142 | this._updateAddRemoveWatch(children); |
---|
143 | |
---|
144 | var insert = [], prop = this._attachTemplateNodes ? "inlineTemplateString" : "templateString"; |
---|
145 | for(this.index = 0; lang.isFunction(children.get) ? children.get(this.index) : children[this.index]; this.index++){ |
---|
146 | insert.push(this._exprRepl(this[prop])); |
---|
147 | } |
---|
148 | |
---|
149 | var repeatNode = this.containerNode || this.srcNodeRef || this.domNode; |
---|
150 | if(has("ie") && /^(table|tbody)$/i.test(repeatNode.tagName)){ |
---|
151 | var div = win.doc.createElement("div"); |
---|
152 | div.innerHTML = "<table><tbody>" + insert.join("") + "</tbody></table>"; |
---|
153 | for(var tbody = div.getElementsByTagName("tbody")[0]; tbody.firstChild;){ |
---|
154 | repeatNode.appendChild(tbody.firstChild); |
---|
155 | } |
---|
156 | }else if(has("ie") && /^td$/i.test(repeatNode.tagName)){ |
---|
157 | var div = win.doc.createElement("div"); |
---|
158 | div.innerHTML = "<table><tbody><tr>" + insert.join("") + "</tr></tbody></table>"; |
---|
159 | for(var tr = div.getElementsByTagName("tr")[0]; tr.firstChild;){ |
---|
160 | repeatNode.appendChild(tr.firstChild); |
---|
161 | } |
---|
162 | }else{ |
---|
163 | repeatNode.innerHTML = insert.join(""); |
---|
164 | } |
---|
165 | |
---|
166 | // srcNodeRef is used in _createBody, so in the programmatic create case where repeatNode was set |
---|
167 | // from this.domNode we need to set srcNodeRef from repeatNode |
---|
168 | this.srcNodeRef = repeatNode; |
---|
169 | |
---|
170 | var _self = this; |
---|
171 | |
---|
172 | when(this._createBody(), function(){ |
---|
173 | if(!_self.removeRepeatNode){ return; } |
---|
174 | |
---|
175 | var repeatnode = _self.domNode; |
---|
176 | if(!_self.savedParentId && _self.domNode.parentNode && _self.domNode.parentNode.id){ |
---|
177 | _self.savedParentId = _self.domNode.parentNode.id; |
---|
178 | } |
---|
179 | var repeatParent = dom.byId(_self.savedParentId); |
---|
180 | if(repeatnode && repeatnode.children){ |
---|
181 | var t3 = registry.findWidgets(repeatnode); |
---|
182 | var parentcnt = t3.length; |
---|
183 | for(var j = parentcnt;j > 0;j--){ |
---|
184 | if(t3[j-1].declaredClass == "dojox.mvc.Group"){ |
---|
185 | var cnt = repeatnode.children[j-1].children.length; |
---|
186 | var selForList = registry.byId(repeatParent.id).select; |
---|
187 | for(var i = cnt;i > 0;i--){ |
---|
188 | registry.byId(repeatnode.children[j-1].id).select = selForList; |
---|
189 | domconstruct.place(repeatnode.children[j-1].removeChild(repeatnode.children[j-1].children[i-1]), repeatParent, "first"); |
---|
190 | } |
---|
191 | }else{ |
---|
192 | domconstruct.place(repeatnode.removeChild(repeatnode.children[j-1]), repeatParent, "first"); |
---|
193 | } |
---|
194 | } |
---|
195 | domconstruct.destroy(repeatnode); |
---|
196 | } |
---|
197 | }); |
---|
198 | }, |
---|
199 | |
---|
200 | _updateAddRemoveWatch: function(/*dojo/Stateful*/ children){ |
---|
201 | // summary: |
---|
202 | // Updates the watch handle when binding changes. |
---|
203 | // children: dojo/Stateful |
---|
204 | // The array of child widgets. |
---|
205 | // tags: |
---|
206 | // private |
---|
207 | if(this._addRemoveWatch){ |
---|
208 | this._addRemoveWatch.unwatch(); |
---|
209 | } |
---|
210 | var pThis = this; |
---|
211 | this._addRemoveWatch = lang.isFunction(children.watchElements) && children.watchElements(function(idx, removals, adds){ |
---|
212 | if(!removals || !adds || removals.length || adds.length){ |
---|
213 | pThis._buildContained(pThis.children); |
---|
214 | } |
---|
215 | }); |
---|
216 | } |
---|
217 | }); |
---|
218 | }); |
---|