source: Dev/trunk/src/client/dojox/mvc/ModelRefController.js @ 531

Last change on this file since 531 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 7.5 KB
Line 
1define([
2        "dojo/_base/array",
3        "dojo/_base/declare",
4        "dojo/_base/lang",
5        "dojo/Stateful",
6        "./_Controller"
7], function(array, declare, lang, Stateful, _Controller){
8        return declare("dojox.mvc.ModelRefController", _Controller, {
9                // summary:
10                //              A controller that keeps a reference to dojo/Stateful-based data model.
11                // description:
12                //              Does the following on behalf of such model:
13                //
14                //              - Provides data from model via dojo/Stateful get() interface
15                //              - Stores data to model via dojo/Stateful set() interface
16                //              - Watches for change in model via dojo/Stateful watch() interface (The callback is called when there is a change in data model, as well as when the data model itself is replaced with different one)
17                //
18                //              Can also be used to do some application-specific stuffs upon change in properties in model, by defining setter functions.
19                //              Doing so will help keep models and widgets free from application-specific logic, and will help keep application logic free from specifics of models and widgets.
20                //              Such kind of setter functions can be defined in the same manner as widgets (_setXXXAttr()).
21                //
22                //              NOTE - If this class is used with a widget by data-dojo-mixins, make sure putting the widget in data-dojo-type and putting this class to data-dojo-mixins.
23                // example:
24                //              The text box refers to "value" property in the controller (with "ctrl" ID).
25                //              The controller provides the "value" property on behalf of the model ("model" property in the controller).
26                //              Two seconds later, the text box changes from "Foo" to "Bar" as the controller changes the data model it refers to.
27                // |            <html>
28                // |                    <head>
29                // |                            <script src="/path/to/dojo-toolkit/dojo/dojo.js" type="text/javascript" data-dojo-config="parseOnLoad: 0"></script>
30                // |                            <script type="text/javascript">
31                // |                                    require([
32                // |                                            "dojo/parser", "dojo/Stateful", "dijit/registry",
33                // |                                            "dijit/form/TextBox", "dojox/mvc/ModelRefController", "dojo/domReady!"
34                // |                                    ], function(parser, Stateful, registry){
35                // |                                            modelFoo = new Stateful({value: "Foo"});
36                // |                                            modelBar = new Stateful({value: "Bar"});
37                // |                                            setTimeout(function(){ registry.byId("ctrl").set("model", modelBar); }, 2000);
38                // |                                            parser.parse();
39                // |                                    });
40                // |                            </script>
41                // |                    </head>
42                // |                    <body>
43                // |                            <script type="dojo/require">at: "dojox/mvc/at"</script>
44                // |                            <span id="ctrl" data-dojo-type="dojox/mvc/ModelRefController" data-dojo-props="model: modelFoo"></span>
45                // |                            <input type="text" data-dojo-type="dijit/form/TextBox" data-dojo-props="value: at('widget:ctrl', 'value')">
46                // |                    </body>
47                // |            </html>
48
49                // ownProps: Object
50                //              List of property names owned by this controller, instead of the data model.
51                ownProps: null,
52
53                // _refModelProp: String
54                //              The property name for the data model.
55                _refModelProp: "model",
56
57                // _refInModelProp: String
58                //              The property name for the data model, used as the input.
59                //              Used when this controller needs data model (as input) that is different from the data model this controller provides.
60                _refInModelProp: "model",
61
62                // model: dojo/Stateful
63                //              The data model.
64                model: null,
65
66                postscript: function(/*Object?*/ params, /*DomNode|String?*/ srcNodeRef){
67                        // summary:
68                        //              Sets _relTargetProp so that the property specified by _refModelProp is used for relative data binding.
69
70                        this._relTargetProp = (params || {})._refModelProp || this._refModelProp;
71                        this.inherited(arguments);
72                },
73
74                get: function(/*String*/ name){
75                        // summary:
76                        //              If getter function is there, use it. Otherwise, get the data from data model of this object.
77                        // name: String
78                        //              The property name.
79
80                        if(!this.hasControllerProperty(name)){
81                                var model = this[this._refModelProp];
82                                return !model ? void 0 : model.get ? model.get(name) : model[name];
83                        }
84                        return this.inherited(arguments);
85                },
86
87                _set: function(/*String*/ name, /*Anything*/ value){
88                        // summary:
89                        //              Set the value to the data model or to this object.
90                        // name: String
91                        //              The property name.
92                        // value: Anything
93                        //              The property value.
94
95                        if(!this.hasControllerProperty(name)){
96                                var model = this[this._refModelProp];
97                                model && (model.set ? model.set(name, value) : (model[name] = value));
98                                return this;
99                        }
100                        return this.inherited(arguments);
101                },
102
103                watch: function(/*String?*/ name, /*Function*/ callback){
104                        // summary:
105                        //              Watch a property in the data model or in this object.
106                        // name: String?
107                        //              The property name.
108                        // callback: Function
109                        //              The callback function.
110
111                        if(this.hasControllerProperty(name)){
112                                return this.inherited(arguments);
113                        }
114
115                        if(!callback){
116                                callback = name;
117                                name = null;
118                        }
119
120                        var hm = null, hp = null, _self = this;
121
122                        function watchPropertiesInModel(/*dojo/Stateful*/ model){
123                                // summary:
124                                //              Watch properties in referred model.
125                                // model: dojo/Stateful
126                                //              The model to watch for.
127
128                                // Unwatch properties of older model.
129                                if(hp){ hp.unwatch(); }
130                                // Watch properties of newer model.
131                                if(model && lang.isFunction(model.set) && lang.isFunction(model.watch)){
132                                        hp = model.watch.apply(model, (name ? [name] : []).concat([function(name, old, current){ callback.call(_self, name, old, current); }]));
133                                }
134                        }
135
136                        function reflectChangeInModel(/*dojo/Stateful*/ old, /*dojo/Stateful*/ current){
137                                // summary:
138                                //              Upon change in model, detect change in properties, and call watch callbacks.
139                                // old: dojo/Stateful
140                                //              The older model.
141                                // current: dojo/Stateful
142                                //              The newer model.
143
144                                // Gather list of properties to notify change in value as model changes.
145                                var props = {};
146                                if(!name){
147                                        // If all properties are being watched, find out all properties from older model as well as from newer model.
148                                        array.forEach([old, current], function(model){
149                                                var list = model && model.get("properties");
150                                                if(list){
151                                                        // If the model explicitly specifies the list of properties, use it.
152                                                        array.forEach(list, function(item){
153                                                                if(!_self.hasControllerProperty(item)){ props[item] = 1; }
154                                                        });
155                                                }else{
156                                                        // Otherwise, iterate through own properties.
157                                                        for(var s in model){
158                                                                if(model.hasOwnProperty(s) && !_self.hasControllerProperty(s)){ props[s] = 1; }
159                                                        }
160                                                }
161                                        });
162                                }else{
163                                        props[name] = 1;
164                                }
165
166                                // Call watch callbacks for properties.
167                                for(var s in props){
168                                        callback.call(_self, s, !old ? void 0 : old.get ? old.get(s) : old[s], !current ? void 0 : current.get ? current.get(s) : current[s]);
169                                }
170                        }
171
172                        // Watch for change in model.
173                        hm = Stateful.prototype.watch.call(this, this._refModelProp, function(name, old, current){
174                                if(old === current){ return; }
175                                reflectChangeInModel(old, current);
176                                watchPropertiesInModel(current);
177                        });
178
179                        // Watch for properties in model.
180                        watchPropertiesInModel(this.get(this._refModelProp));
181
182                        var h = {};
183                        h.unwatch = h.remove = function(){
184                                if(hp){ hp.unwatch(); hp = null; } if(hm){ hm.unwatch(); hm = null; }
185                        };
186                        return h; // dojo/handle
187                },
188
189                hasControllerProperty: function(/*String*/ name){
190                        // summary:
191                        //              Returns true if this controller itself owns the given property.
192                        // name: String
193                        //              The property name.
194
195                        return name == "_watchCallbacks" || name == this._refModelProp || name == this._refInModelProp || (name in (this.ownProps || {})) || (name in this.constructor.prototype) || /^dojoAttach(Point|Event)$/i.test(name); // Let dojoAttachPoint/dojoAttachEvent be this controller's property to support <span data-dojo-type="dojox/mvc/ModelRefController" data-dojo-attach-point="controllerNode"> in widgets-in-template
196                }
197        });
198});
Note: See TracBrowser for help on using the repository browser.