define("dojox/mvc/Repeat", [ "dojo/_base/declare", "dojo/_base/lang", "dojo/_base/sniff", "dojo/_base/window", "dojo/dom", "dojo/dom-construct", "dojo/_base/array", "dojo/query", "dojo/when", "dijit/registry", "./_Container" ], function(declare, lang, has, win, dom, domconstruct, array, query, when, registry, _Container){ return declare("dojox.mvc.Repeat", _Container, { // summary: // A model-bound container which binds to a collection within a data model // and produces a repeating user-interface from a template for each // iteration within the collection. // // description: // A repeat is bound to an intermediate dojo/Stateful node corresponding // to an array in the data model. Child dijits or custom view components // inside it inherit their parent data binding context from it. // index: Integer // An index used to track the current iteration when the repeating UI is // produced. This may be used to parameterize the content in the repeat // template for the current iteration. // // For example, consider a collection of search or query results where // each item contains a "Name" property used to prime the "Results" data // model. Then, the following CRUD-style UI displays all the names in // the search results in text boxes where they may be updated or such. // // |
// |
// | // | // |
// |
index : 0, // useParent: String // id of the DOM node to use as the parent for the repeating items, similar to useParentId processed a little differently useParent : "", // removeRepeatNode: boolean // When true the dom node for the Repeat and Groups within the Repeat // will be removed, their children will be placed into the parent node // of the Repeat node. This should be set to true when working with // a Repeat inside of a dojox.mobile list. removeRepeatNode : false, // children: dojox/mvc/StatefulArray // The array of data model that is used to render child nodes. children: null, // _relTargetProp: String // The name of the property that is used by child widgets for relative data binding. _relTargetProp : "children", startup: function(){ // This code needed for ticket 14423 is using removeRepeatNode to work with mobile.lists // this.select and this.onCheckStateChanged are called by ListItem so they need to be set // but it seems like a bit of a hack. if(this.removeRepeatNode){ var parent = null; if(lang.isFunction(this.getParent)){ if(this.getParent()){ this.select = this.getParent().select; this.onCheckStateChanged = this.getParent().onCheckStateChanged; } } } this.inherited(arguments); this._setChildrenAttr(this.children); }, // summary: // Override and save template from body. postscript: function(params, srcNodeRef){ //this.srcNodeRef = dom.byId(srcNodeRef); if(this.useParent && dom.byId(this.useParent)){ this.srcNodeRef = dom.byId(this.useParent); } else{ this.srcNodeRef = dom.byId(srcNodeRef); } if(this.srcNodeRef){ var prop = this._attachTemplateNodes ? "inlineTemplateString" : "templateString"; if(this[prop] == ""){ // only overwrite templateString if it has not been set this[prop] = this.srcNodeRef.innerHTML; } try{ this.srcNodeRef.innerHTML = ""; }catch(e){ while(this.srcNodeRef.firstChild){ this.srcNodeRef.removeChild(this.srcNodeRef.firstChild); } } } this.inherited(arguments); }, ////////////////////// PRIVATE METHODS //////////////////////// _setChildrenAttr: function(/*dojo/Stateful*/ value){ // summary: // Handler for calls to set("children", val). // description: // Sets "ref" property so that child widgets can refer to, and then rebuilds the children. var children = this.children; this._set("children", value); // this.binding is the resolved ref, so not matching with the new value means change in repeat target. if(this.binding != value){ this.set("ref", value); } if(this._started && (!this._builtOnce || children != value)){ this._builtOnce = true; this._buildContained(value); } }, _buildContained: function(/*dojox/mvc/StatefulArray*/ children){ // summary: // Destroy any existing contained view, recreate the repeating UI // markup and parse the new contents. // children: dojox/mvc/StatefulArray // The array of child widgets. // tags: // private if(!children){ return; } // TODO: Potential optimization: only create new widgets for insert, only destroy for delete. if(this.useParent && dom.byId(this.useParent)){ this.srcNodeRef = dom.byId(this.useParent); } this._destroyBody(); this._updateAddRemoveWatch(children); var insert = [], prop = this._attachTemplateNodes ? "inlineTemplateString" : "templateString"; for(this.index = 0; lang.isFunction(children.get) ? children.get(this.index) : children[this.index]; this.index++){ insert.push(this._exprRepl(this[prop])); } var repeatNode = this.containerNode || this.srcNodeRef || this.domNode; if(has("ie") && /^(table|tbody)$/i.test(repeatNode.tagName)){ var div = win.doc.createElement("div"); div.innerHTML = "" + insert.join("") + "
"; for(var tbody = div.getElementsByTagName("tbody")[0]; tbody.firstChild;){ repeatNode.appendChild(tbody.firstChild); } }else if(has("ie") && /^td$/i.test(repeatNode.tagName)){ var div = win.doc.createElement("div"); div.innerHTML = "" + insert.join("") + "
"; for(var tr = div.getElementsByTagName("tr")[0]; tr.firstChild;){ repeatNode.appendChild(tr.firstChild); } }else{ repeatNode.innerHTML = insert.join(""); } // srcNodeRef is used in _createBody, so in the programmatic create case where repeatNode was set // from this.domNode we need to set srcNodeRef from repeatNode this.srcNodeRef = repeatNode; var _self = this; when(this._createBody(), function(){ if(!_self.removeRepeatNode){ return; } var repeatnode = _self.domNode; if(!_self.savedParentId && _self.domNode.parentNode && _self.domNode.parentNode.id){ _self.savedParentId = _self.domNode.parentNode.id; } var repeatParent = dom.byId(_self.savedParentId); if(repeatnode && repeatnode.children){ var t3 = registry.findWidgets(repeatnode); var parentcnt = t3.length; for(var j = parentcnt;j > 0;j--){ if(t3[j-1].declaredClass == "dojox.mvc.Group"){ var cnt = repeatnode.children[j-1].children.length; var selForList = registry.byId(repeatParent.id).select; for(var i = cnt;i > 0;i--){ registry.byId(repeatnode.children[j-1].id).select = selForList; domconstruct.place(repeatnode.children[j-1].removeChild(repeatnode.children[j-1].children[i-1]), repeatParent, "first"); } }else{ domconstruct.place(repeatnode.removeChild(repeatnode.children[j-1]), repeatParent, "first"); } } domconstruct.destroy(repeatnode); } }); }, _updateAddRemoveWatch: function(/*dojo/Stateful*/ children){ // summary: // Updates the watch handle when binding changes. // children: dojo/Stateful // The array of child widgets. // tags: // private if(this._addRemoveWatch){ this._addRemoveWatch.unwatch(); } var pThis = this; this._addRemoveWatch = lang.isFunction(children.watchElements) && children.watchElements(function(idx, removals, adds){ if(!removals || !adds || removals.length || adds.length){ pThis._buildContained(pThis.children); } }); } }); });