[483] | 1 | define([ |
---|
| 2 | "dojo/_base/array", |
---|
| 3 | "dojo/_base/lang", |
---|
| 4 | "dojo/_base/declare", |
---|
| 5 | "./_Container", |
---|
| 6 | "./at", |
---|
| 7 | "./Group", |
---|
| 8 | "dijit/form/TextBox" |
---|
| 9 | ], function(array, lang, declare, Container, at){ |
---|
| 10 | |
---|
| 11 | return declare("dojox.mvc.Generate", [Container], { |
---|
| 12 | // summary: |
---|
| 13 | // A container that generates a view based on the data model its bound to. |
---|
| 14 | // |
---|
| 15 | // description: |
---|
| 16 | // A generate introspects its data binding and creates a view contained in |
---|
| 17 | // it that allows displaying the bound data. Child dijits or custom view |
---|
| 18 | // components inside it inherit their parent data binding context from it. |
---|
| 19 | |
---|
| 20 | // _counter: [private] Integer |
---|
| 21 | // A count maintained internally to always generate predictable widget |
---|
| 22 | // IDs in the view generated by this container. |
---|
| 23 | _counter : 0, |
---|
| 24 | |
---|
| 25 | // defaultWidgetMapping: Object |
---|
| 26 | // The mapping of types to a widget class. Set widgetMapping to override this. |
---|
| 27 | // |
---|
| 28 | _defaultWidgetMapping: {"String" : "dijit/form/TextBox"}, |
---|
| 29 | |
---|
| 30 | // defaultClassMapping: Object |
---|
| 31 | // The mapping of class to use. Set classMapping to override this. |
---|
| 32 | // |
---|
| 33 | _defaultClassMapping: {"Label" : "generate-label-cell", "String" : "generate-dijit-cell", "Heading" : "generate-heading", "Row" : "row"}, |
---|
| 34 | |
---|
| 35 | |
---|
| 36 | // defaultIdNameMapping: Object |
---|
| 37 | // The mapping of id and name to use. Set idNameMapping to override this. A count will be added to the id and name |
---|
| 38 | // |
---|
| 39 | _defaultIdNameMapping: {"String" : "textbox_t"}, |
---|
| 40 | |
---|
| 41 | // children: dojo/Stateful |
---|
| 42 | // The array of data model that is used to render child nodes. |
---|
| 43 | children: null, |
---|
| 44 | |
---|
| 45 | // _relTargetProp: String |
---|
| 46 | // The name of the property that is used by child widgets for relative data binding. |
---|
| 47 | _relTargetProp : "children", |
---|
| 48 | |
---|
| 49 | startup: function(){ |
---|
| 50 | this.inherited(arguments); |
---|
| 51 | this._setChildrenAttr(this.children); |
---|
| 52 | }, |
---|
| 53 | |
---|
| 54 | ////////////////////// PRIVATE METHODS //////////////////////// |
---|
| 55 | |
---|
| 56 | _setChildrenAttr: function(/*dojo/Stateful*/ value){ |
---|
| 57 | // summary: |
---|
| 58 | // Handler for calls to set("children", val). |
---|
| 59 | // description: |
---|
| 60 | // Sets "ref" property so that child widgets can refer to, and then rebuilds the children. |
---|
| 61 | |
---|
| 62 | var children = this.children; |
---|
| 63 | this._set("children", value); |
---|
| 64 | // this.binding is the resolved ref, so not matching with the new value means change in repeat target. |
---|
| 65 | if(this.binding != value){ |
---|
| 66 | this.set("ref", value); |
---|
| 67 | } |
---|
| 68 | if(this._started && (!this._builtOnce || children != value)){ |
---|
| 69 | this._builtOnce = true; |
---|
| 70 | this._buildContained(value); |
---|
| 71 | } |
---|
| 72 | }, |
---|
| 73 | |
---|
| 74 | _buildContained: function(/*dojo/Stateful*/ children){ |
---|
| 75 | // summary: |
---|
| 76 | // Destroy any existing generated view, recreate it from scratch |
---|
| 77 | // parse the new contents. |
---|
| 78 | // children: dojo/Stateful |
---|
| 79 | // The array of child widgets. |
---|
| 80 | // tags: |
---|
| 81 | // private |
---|
| 82 | |
---|
| 83 | if(!children){ return; } |
---|
| 84 | |
---|
| 85 | this._destroyBody(); |
---|
| 86 | |
---|
| 87 | this._counter = 0; |
---|
| 88 | this.srcNodeRef.innerHTML = this._generateBody(children); |
---|
| 89 | |
---|
| 90 | this._createBody(); |
---|
| 91 | }, |
---|
| 92 | |
---|
| 93 | _generateBody: function(/*dojo/Stateful*/ children, /*Boolean*/ hideHeading){ |
---|
| 94 | // summary: |
---|
| 95 | // Generate the markup for the view associated with this generate |
---|
| 96 | // container. |
---|
| 97 | // children: dojo/Stateful |
---|
| 98 | // The associated data to generate a view for. |
---|
| 99 | // hideHeading: Boolean |
---|
| 100 | // Whether the property name should be displayed as a heading. |
---|
| 101 | // tags: |
---|
| 102 | // private |
---|
| 103 | |
---|
| 104 | if(children === void 0){ return ""; } |
---|
| 105 | |
---|
| 106 | var body = []; |
---|
| 107 | var isStatefulModel = lang.isFunction(children.toPlainObject); |
---|
| 108 | |
---|
| 109 | function generateElement(value, prop){ |
---|
| 110 | if(isStatefulModel ? (value && lang.isFunction(value.toPlainObject)) : !lang.isFunction(value)){ |
---|
| 111 | if(lang.isArray(value)){ |
---|
| 112 | body.push(this._generateRepeat(value, prop)); |
---|
| 113 | }else if(isStatefulModel ? value.value : ((value == null || {}.toString.call(value) != "[object Object]") && (!(value || {}).set || !(value || {}).watch))){ |
---|
| 114 | // TODO: Data types based widgets |
---|
| 115 | body.push(this._generateTextBox(prop, isStatefulModel)); |
---|
| 116 | }else{ |
---|
| 117 | body.push(this._generateGroup(value, prop, hideHeading)); |
---|
| 118 | } |
---|
| 119 | } |
---|
| 120 | } |
---|
| 121 | |
---|
| 122 | if(lang.isArray(children)){ |
---|
| 123 | array.forEach(children, generateElement, this); |
---|
| 124 | }else{ |
---|
| 125 | for(var s in children){ |
---|
| 126 | if(children.hasOwnProperty(s)){ |
---|
| 127 | generateElement.call(this, children[s], s); |
---|
| 128 | } |
---|
| 129 | } |
---|
| 130 | } |
---|
| 131 | |
---|
| 132 | return body.join(""); |
---|
| 133 | }, |
---|
| 134 | |
---|
| 135 | _generateRepeat: function(/*dojox/mvc/StatefulArray*/ children, /*String*/ repeatHeading){ |
---|
| 136 | // summary: |
---|
| 137 | // Generate a repeating model-bound view. |
---|
| 138 | // children: dojox/mvc/StatefulArray |
---|
| 139 | // The bound node (a collection/array node) to generate a |
---|
| 140 | // repeating UI/view for. |
---|
| 141 | // repeatHeading: String |
---|
| 142 | // The heading to be used for this portion. |
---|
| 143 | // tags: |
---|
| 144 | // private |
---|
| 145 | |
---|
| 146 | var headingClass = (this.classMapping && this.classMapping["Heading"]) ? this.classMapping["Heading"] : this._defaultClassMapping["Heading"]; |
---|
| 147 | return '<div data-dojo-type="dojox/mvc/Group" data-dojo-props="target: at(\'rel:\', \'' + repeatHeading + '\')" + id="' + this.id + '_r' + this._counter++ + '">' |
---|
| 148 | + '<div class="' + headingClass + '\">' + repeatHeading + '</div>' |
---|
| 149 | + this._generateBody(children, true) |
---|
| 150 | + '</div>'; |
---|
| 151 | }, |
---|
| 152 | |
---|
| 153 | _generateGroup: function(/*dojo/Stateful*/ model, /*String*/ groupHeading, /*Boolean*/ hideHeading){ |
---|
| 154 | // summary: |
---|
| 155 | // Generate a hierarchical model-bound view. |
---|
| 156 | // model: dojo/Stateful |
---|
| 157 | // The bound (intermediate) model to generate a hierarchical view portion for. |
---|
| 158 | // groupHeading: String |
---|
| 159 | // The heading to be used for this portion. |
---|
| 160 | // hideHeading: Boolean |
---|
| 161 | // Whether the heading should be hidden for this portion. |
---|
| 162 | // tags: |
---|
| 163 | // private |
---|
| 164 | |
---|
| 165 | var html = ['<div data-dojo-type="dojox/mvc/Group" data-dojo-props="target: at(\'rel:\', \'' + groupHeading + '\')" + id="' + this.id + '_g' + this._counter++ + '">']; |
---|
| 166 | if(!hideHeading){ |
---|
| 167 | var headingClass = (this.classMapping && this.classMapping["Heading"]) ? this.classMapping["Heading"] : this._defaultClassMapping["Heading"]; |
---|
| 168 | html.push('<div class="' + headingClass + '\">' + groupHeading + '</div>'); |
---|
| 169 | } |
---|
| 170 | html.push(this._generateBody(model) + '</div>'); |
---|
| 171 | return html.join(""); |
---|
| 172 | }, |
---|
| 173 | |
---|
| 174 | _generateTextBox: function(/*String*/ prop, /*Boolean*/ referToValue){ |
---|
| 175 | // summary: |
---|
| 176 | // Produce a widget for a simple value. |
---|
| 177 | // prop: String |
---|
| 178 | // The data model property name. |
---|
| 179 | // referToValue: Boolean |
---|
| 180 | // True if the property is dojox/mvc/StatefulModel with "value" attribute. |
---|
| 181 | // tags: |
---|
| 182 | // private |
---|
| 183 | // TODO: Data type based widget generation / enhanced meta-data |
---|
| 184 | |
---|
| 185 | var idname = this.idNameMapping ? this.idNameMapping["String"] : this._defaultIdNameMapping["String"]; |
---|
| 186 | idname = idname + this._counter++; |
---|
| 187 | var widClass = this.widgetMapping ? this.widgetMapping["String"] : this._defaultWidgetMapping["String"]; |
---|
| 188 | var labelClass = (this.classMapping && this.classMapping["Label"]) ? this.classMapping["Label"] : this._defaultClassMapping["Label"]; |
---|
| 189 | var stringClass = (this.classMapping && this.classMapping["String"]) ? this.classMapping["String"] : this._defaultClassMapping["String"]; |
---|
| 190 | var rowClass = (this.classMapping && this.classMapping["Row"]) ? this.classMapping["Row"] : this._defaultClassMapping["Row"]; |
---|
| 191 | var bindingSyntax = 'value: at(\'rel:' + (referToValue && prop || '') + '\', \'' + (referToValue ? 'value' : prop) + '\')'; |
---|
| 192 | |
---|
| 193 | return '<div class="' + rowClass + '\">' + |
---|
| 194 | '<label class="' + labelClass + '\">' + prop + ':</label>' + |
---|
| 195 | '<input class="' + stringClass + '\" data-dojo-type="' + widClass + '\"' + |
---|
| 196 | ' data-dojo-props="name: \'' + idname + '\', ' + bindingSyntax + '" id="' + idname + '\"></input>' + |
---|
| 197 | '</div>'; |
---|
| 198 | } |
---|
| 199 | }); |
---|
| 200 | }); |
---|