/ext-4.1.0_b3/src/app/Controller.js

https://bitbucket.org/srogerf/javascript · JavaScript · 450 lines · 128 code · 43 blank · 279 comment · 24 complexity · c5a67115e832fc601661e736738e3849 MD5 · raw file

  1. /**
  2. * @class Ext.app.Controller
  3. *
  4. * Controllers are the glue that binds an application together. All they really do is listen for events (usually from
  5. * views) and take some action. Here's how we might create a Controller to manage Users:
  6. *
  7. * Ext.define('MyApp.controller.Users', {
  8. * extend: 'Ext.app.Controller',
  9. *
  10. * init: function() {
  11. * console.log('Initialized Users! This happens before the Application launch function is called');
  12. * }
  13. * });
  14. *
  15. * The init function is a special method that is called when your application boots. It is called before the
  16. * {@link Ext.app.Application Application}'s launch function is executed so gives a hook point to run any code before
  17. * your Viewport is created.
  18. *
  19. * The init function is a great place to set up how your controller interacts with the view, and is usually used in
  20. * conjunction with another Controller function - {@link Ext.app.Controller#control control}. The control function
  21. * makes it easy to listen to events on your view classes and take some action with a handler function. Let's update
  22. * our Users controller to tell us when the panel is rendered:
  23. *
  24. * Ext.define('MyApp.controller.Users', {
  25. * extend: 'Ext.app.Controller',
  26. *
  27. * init: function() {
  28. * this.control({
  29. * 'viewport > panel': {
  30. * render: this.onPanelRendered
  31. * }
  32. * });
  33. * },
  34. *
  35. * onPanelRendered: function() {
  36. * console.log('The panel was rendered');
  37. * }
  38. * });
  39. *
  40. * We've updated the init function to use this.control to set up listeners on views in our application. The control
  41. * function uses the new ComponentQuery engine to quickly and easily get references to components on the page. If you
  42. * are not familiar with ComponentQuery yet, be sure to check out the {@link Ext.ComponentQuery documentation}. In brief though,
  43. * it allows us to pass a CSS-like selector that will find every matching component on the page.
  44. *
  45. * In our init function above we supplied 'viewport > panel', which translates to "find me every Panel that is a direct
  46. * child of a Viewport". We then supplied an object that maps event names (just 'render' in this case) to handler
  47. * functions. The overall effect is that whenever any component that matches our selector fires a 'render' event, our
  48. * onPanelRendered function is called.
  49. *
  50. * <u>Using refs</u>
  51. *
  52. * One of the most useful parts of Controllers is the new ref system. These use the new {@link Ext.ComponentQuery} to
  53. * make it really easy to get references to Views on your page. Let's look at an example of this now:
  54. *
  55. * Ext.define('MyApp.controller.Users', {
  56. * extend: 'Ext.app.Controller',
  57. *
  58. * refs: [
  59. * {
  60. * ref: 'list',
  61. * selector: 'grid'
  62. * }
  63. * ],
  64. *
  65. * init: function() {
  66. * this.control({
  67. * 'button': {
  68. * click: this.refreshGrid
  69. * }
  70. * });
  71. * },
  72. *
  73. * refreshGrid: function() {
  74. * this.getList().store.load();
  75. * }
  76. * });
  77. *
  78. * This example assumes the existence of a {@link Ext.grid.Panel Grid} on the page, which contains a single button to
  79. * refresh the Grid when clicked. In our refs array, we set up a reference to the grid. There are two parts to this -
  80. * the 'selector', which is a {@link Ext.ComponentQuery ComponentQuery} selector which finds any grid on the page and
  81. * assigns it to the reference 'list'.
  82. *
  83. * By giving the reference a name, we get a number of things for free. The first is the getList function that we use in
  84. * the refreshGrid method above. This is generated automatically by the Controller based on the name of our ref, which
  85. * was capitalized and prepended with get to go from 'list' to 'getList'.
  86. *
  87. * The way this works is that the first time getList is called by your code, the ComponentQuery selector is run and the
  88. * first component that matches the selector ('grid' in this case) will be returned. All future calls to getList will
  89. * use a cached reference to that grid. Usually it is advised to use a specific ComponentQuery selector that will only
  90. * match a single View in your application (in the case above our selector will match any grid on the page).
  91. *
  92. * Bringing it all together, our init function is called when the application boots, at which time we call this.control
  93. * to listen to any click on a {@link Ext.button.Button button} and call our refreshGrid function (again, this will
  94. * match any button on the page so we advise a more specific selector than just 'button', but have left it this way for
  95. * simplicity). When the button is clicked we use out getList function to refresh the grid.
  96. *
  97. * You can create any number of refs and control any number of components this way, simply adding more functions to
  98. * your Controller as you go. For an example of real-world usage of Controllers see the Feed Viewer example in the
  99. * examples/app/feed-viewer folder in the SDK download.
  100. *
  101. * <u>Generated getter methods</u>
  102. *
  103. * Refs aren't the only thing that generate convenient getter methods. Controllers often have to deal with Models and
  104. * Stores so the framework offers a couple of easy ways to get access to those too. Let's look at another example:
  105. *
  106. * Ext.define('MyApp.controller.Users', {
  107. * extend: 'Ext.app.Controller',
  108. *
  109. * models: ['User'],
  110. * stores: ['AllUsers', 'AdminUsers'],
  111. *
  112. * init: function() {
  113. * var User = this.getUserModel(),
  114. * allUsers = this.getAllUsersStore();
  115. *
  116. * var ed = new User({name: 'Ed'});
  117. * allUsers.add(ed);
  118. * }
  119. * });
  120. *
  121. * By specifying Models and Stores that the Controller cares about, it again dynamically loads them from the appropriate
  122. * locations (app/model/User.js, app/store/AllUsers.js and app/store/AdminUsers.js in this case) and creates getter
  123. * functions for them all. The example above will create a new User model instance and add it to the AllUsers Store.
  124. * Of course, you could do anything in this function but in this case we just did something simple to demonstrate the
  125. * functionality.
  126. *
  127. * <u>Further Reading</u>
  128. *
  129. * For more information about writing Ext JS 4 applications, please see the
  130. * [application architecture guide](#/guide/application_architecture). Also see the {@link Ext.app.Application} documentation.
  131. *
  132. * @docauthor Ed Spencer
  133. */
  134. Ext.define('Ext.app.Controller', {
  135. mixins: {
  136. observable: 'Ext.util.Observable'
  137. },
  138. /**
  139. * @cfg {String} id The id of this controller. You can use this id when dispatching.
  140. */
  141. /**
  142. * @cfg {String[]} models
  143. * Array of models to require from AppName.model namespace. For example:
  144. *
  145. * Ext.define("MyApp.controller.Foo", {
  146. * extend: "Ext.app.Controller",
  147. * models: ['User', 'Vehicle']
  148. * });
  149. *
  150. * This is equivalent of:
  151. *
  152. * Ext.define("MyApp.controller.Foo", {
  153. * extend: "Ext.app.Controller",
  154. * requires: ['MyApp.model.User', 'MyApp.model.Vehicle']
  155. * });
  156. *
  157. */
  158. /**
  159. * @cfg {String[]} views
  160. * Array of views to require from AppName.view namespace. For example:
  161. *
  162. * Ext.define("MyApp.controller.Foo", {
  163. * extend: "Ext.app.Controller",
  164. * views: ['List', 'Detail']
  165. * });
  166. *
  167. * This is equivalent of:
  168. *
  169. * Ext.define("MyApp.controller.Foo", {
  170. * extend: "Ext.app.Controller",
  171. * requires: ['MyApp.view.List', 'MyApp.view.Detail']
  172. * });
  173. *
  174. */
  175. /**
  176. * @cfg {String[]} stores
  177. * Array of stores to require from AppName.store namespace. For example:
  178. *
  179. * Ext.define("MyApp.controller.Foo", {
  180. * extend: "Ext.app.Controller",
  181. * stores: ['Users', 'Vehicles']
  182. * });
  183. *
  184. * This is equivalent of:
  185. *
  186. * Ext.define("MyApp.controller.Foo", {
  187. * extend: "Ext.app.Controller",
  188. * requires: ['MyApp.store.Users', 'MyApp.store.Vehicles']
  189. * });
  190. *
  191. */
  192. /**
  193. * @cfg {Object[]} refs
  194. * Array of configs to build up references to views on page. For example:
  195. *
  196. * Ext.define("MyApp.controller.Foo", {
  197. * extend: "Ext.app.Controller",
  198. * refs: [
  199. * {
  200. * ref: 'list',
  201. * selector: 'grid'
  202. * }
  203. * ],
  204. * });
  205. *
  206. * This will add method `getList` to the controller which will internally use
  207. * Ext.ComponentQuery to reference the grid component on page.
  208. */
  209. onClassExtended: function(cls, data, hooks) {
  210. var className = Ext.getClassName(cls),
  211. match = className.match(/^(.*)\.controller\./);
  212. if (match !== null) {
  213. var namespace = Ext.Loader.getPrefix(className) || match[1],
  214. onBeforeClassCreated = hooks.onBeforeCreated,
  215. requires = [],
  216. modules = ['model', 'view', 'store'],
  217. prefix;
  218. hooks.onBeforeCreated = function(cls, data) {
  219. var i, ln, module,
  220. items, j, subLn, item;
  221. for (i = 0,ln = modules.length; i < ln; i++) {
  222. module = modules[i];
  223. items = Ext.Array.from(data[module + 's']);
  224. for (j = 0,subLn = items.length; j < subLn; j++) {
  225. item = items[j];
  226. prefix = Ext.Loader.getPrefix(item);
  227. if (prefix === '' || prefix === item) {
  228. requires.push(namespace + '.' + module + '.' + item);
  229. }
  230. else {
  231. requires.push(item);
  232. }
  233. }
  234. }
  235. Ext.require(requires, Ext.Function.pass(onBeforeClassCreated, arguments, this));
  236. };
  237. }
  238. },
  239. /**
  240. * Creates new Controller.
  241. * @param {Object} config (optional) Config object.
  242. */
  243. constructor: function(config) {
  244. this.mixins.observable.constructor.call(this, config);
  245. Ext.apply(this, config || {});
  246. this.createGetters('model', this.models);
  247. this.createGetters('store', this.stores);
  248. this.createGetters('view', this.views);
  249. if (this.refs) {
  250. this.ref(this.refs);
  251. }
  252. },
  253. /**
  254. * A template method that is called when your application boots. It is called before the
  255. * {@link Ext.app.Application Application}'s launch function is executed so gives a hook point to run any code before
  256. * your Viewport is created.
  257. *
  258. * @param {Ext.app.Application} application
  259. * @template
  260. */
  261. init: function(application) {},
  262. /**
  263. * A template method like {@link #init}, but called after the viewport is created.
  264. * This is called after the {@link Ext.app.Application#launch launch} method of Application is executed.
  265. *
  266. * @param {Ext.app.Application} application
  267. * @template
  268. */
  269. onLaunch: function(application) {},
  270. createGetters: function(type, refs) {
  271. type = Ext.String.capitalize(type);
  272. var i = 0,
  273. length = (refs) ? refs.length : 0,
  274. fn, ref, parts, x, numparts;
  275. for (; i < length; i++) {
  276. fn = 'get';
  277. ref = refs[i];
  278. parts = ref.split('.');
  279. numParts = parts.length;
  280. // Handle namespaced class names. E.g. feed.Add becomes getFeedAddView etc.
  281. for (x = 0 ; x < numParts; x++) {
  282. fn += Ext.String.capitalize(parts[x]);
  283. }
  284. fn += type;
  285. if (!this[fn]) {
  286. this[fn] = Ext.Function.pass(this['get' + type], [ref], this);
  287. }
  288. // Execute it right away
  289. this[fn](ref);
  290. }
  291. },
  292. ref: function(refs) {
  293. refs = Ext.Array.from(refs);
  294. var me = this,
  295. i = 0,
  296. length = refs.length,
  297. info, ref, fn;
  298. for (; i < length; i++) {
  299. info = refs[i];
  300. ref = info.ref;
  301. fn = 'get' + Ext.String.capitalize(ref);
  302. if (!me[fn]) {
  303. me[fn] = Ext.Function.pass(me.getRef, [ref, info], me);
  304. }
  305. me.references = me.references || [];
  306. me.references.push(ref.toLowerCase());
  307. }
  308. },
  309. addRef: function(ref) {
  310. return this.ref([ref]);
  311. },
  312. getRef: function(ref, info, config) {
  313. this.refCache = this.refCache || {};
  314. info = info || {};
  315. config = config || {};
  316. Ext.apply(info, config);
  317. if (info.forceCreate) {
  318. return Ext.ComponentManager.create(info, 'component');
  319. }
  320. var me = this,
  321. cached = me.refCache[ref];
  322. if (!cached) {
  323. me.refCache[ref] = cached = Ext.ComponentQuery.query(info.selector)[0];
  324. if (!cached && info.autoCreate) {
  325. me.refCache[ref] = cached = Ext.ComponentManager.create(info, 'component');
  326. }
  327. if (cached) {
  328. cached.on('beforedestroy', function() {
  329. me.refCache[ref] = null;
  330. });
  331. }
  332. }
  333. return cached;
  334. },
  335. hasRef: function(ref) {
  336. return this.references && this.references.indexOf(ref.toLowerCase()) !== -1;
  337. },
  338. /**
  339. * Adds listeners to components selected via {@link Ext.ComponentQuery}. Accepts an
  340. * object containing component paths mapped to a hash of listener functions.
  341. *
  342. * In the following example the `updateUser` function is mapped to to the `click`
  343. * event on a button component, which is a child of the `useredit` component.
  344. *
  345. * Ext.define('AM.controller.Users', {
  346. * init: function() {
  347. * this.control({
  348. * 'useredit button[action=save]': {
  349. * click: this.updateUser
  350. * }
  351. * });
  352. * },
  353. *
  354. * updateUser: function(button) {
  355. * console.log('clicked the Save button');
  356. * }
  357. * });
  358. *
  359. * See {@link Ext.ComponentQuery} for more information on component selectors.
  360. *
  361. * @param {String/Object} selectors If a String, the second argument is used as the
  362. * listeners, otherwise an object of selectors -> listeners is assumed
  363. * @param {Object} listeners
  364. */
  365. control: function(selectors, listeners) {
  366. this.application.control(selectors, listeners, this);
  367. },
  368. /**
  369. * Returns instance of a {@link Ext.app.Controller controller} with the given name.
  370. * When controller doesn't exist yet, it's created.
  371. * @param {String} name
  372. * @return {Ext.app.Controller} a controller instance.
  373. */
  374. getController: function(name) {
  375. return this.application.getController(name);
  376. },
  377. /**
  378. * Returns instance of a {@link Ext.data.Store Store} with the given name.
  379. * When store doesn't exist yet, it's created.
  380. * @param {String} name
  381. * @return {Ext.data.Store} a store instance.
  382. */
  383. getStore: function(name) {
  384. return this.application.getStore(name);
  385. },
  386. /**
  387. * Returns a {@link Ext.data.Model Model} class with the given name.
  388. * A shorthand for using {@link Ext.ModelManager#getModel}.
  389. * @param {String} name
  390. * @return {Ext.data.Model} a model class.
  391. */
  392. getModel: function(model) {
  393. return this.application.getModel(model);
  394. },
  395. /**
  396. * Returns a View class with the given name. To create an instance of the view,
  397. * you can use it like it's used by Application to create the Viewport:
  398. *
  399. * this.getView('Viewport').create();
  400. *
  401. * @param {String} name
  402. * @return {Ext.Base} a view class.
  403. */
  404. getView: function(view) {
  405. return this.application.getView(view);
  406. }
  407. });