[483] | 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 | }); |
---|