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 | }); |
---|