javascriptextjsextjs4extjs4.1extjs-mvc

ExtJS: What's the point of requires?


I've been trying to figure out what requires does in Ext JS 4, and I can't seem to come up with a reasonable answer. Let's say I have the following code:

app.js

Ext.Loader.setConfig({
  enabled: true
});

Ext.Loader.setPath('Ext.ux', 'examples/ux');

Ext.application({
  name: 'Test',
  appFolder: 'app',
  controllers: ['TheController'],
  requires: ['Test.Utils', 'Test.Utils2'],  // I don't think this does anything... couldn't find this option for Ext.application
  launch: function() {
    Ext.create('Ext.Viewport', {
      layout: 'border',
      items: [{
        xtype: 'thegrid',
        region: 'center',
        title: 'blah!'
      }]
    });
  }
});

app/controller/TheController.js

Ext.define('Test.controller.TheController', {
  extend: 'Ext.app.Controller',
  models: ['TheModel'],
  stores: ['TheStore'],
  views: ['TheGrid'],
  init: function() {
  }
});

app/view/TheGrid.js

Ext.define('Test.view.TheGrid', {
  extend: 'Ext.grid.Panel',
  alias: 'widget.thegrid',
  requires: ['Test.store.TheStore'],
  store: 'TheStore',
  columns: [
    {header: 'Name', dataIndex: 'name'},
    {header: 'Phone', dataIndex: 'phone'},
    {header: 'Hello', dataIndex: 'hello'}
  ]
});

app/store/TheStore.js

Ext.define('Test.store.TheStore', {
  extend: 'Ext.data.Store',
  requires: ['Test.model.TheModel', 'Test.Utils'],
  model: 'Test.model.TheModel',
  data: [
    {name: 'keanu reeves', phone: '1800matrices', hello: Test.Utils.getText()},
    {name: 'james earl jones', phone: '1800starwar', hello: 'nothing here'},
    {name: 'barack obama', phone: '1800prsidnt', hello: 'hello world'}
  ]
});

app/model/TheModel.js

Ext.define('Test.model.TheModel', {
  extend: 'Ext.data.Model',
  fields: [
    {name: 'name', type: 'string'},
    {name: 'phone', type: 'string'},
    {name: 'hello', type: 'string'}
  ]
});

app/Utils.js

Ext.define('Test.Utils', {
  singleton: true,
  requires: ['Test.Utils2'],
  getText: function() {
    return Test.Utils2.hello + 'world';
  }
});

app/Utils2.js

Ext.define('Test.Utils2', {
  singleton: true,
  hello: 'hello'
});

I realize this is a really long example, but I needed to make sure I fully portrayed what I was doing. Utils relies on Utils2 because it needs to call Utils2's hello variable. The rest of the code is setting up a grid and calling the Utils.getText function in TheStore. Firebug throws a Test.Utils is undefined on line 6 in TheStore.js, and at that point in time, Test.Utils obviously doesn't exist, but Test.Utils2 does.

My question is... why does Utils2 exist, but Utils doesn't? I thought requires brought in the classes that I needed, thus allowing me to use them, but I guess I'm wrong. I've read the Sencha docs and a multitude of threads, but nothing really made sense, and it doesn't really explain this example. Can anyone shed some insight here? I'd appreciate it.

**Also, I realize I'm doing some dumb things here, but it's merely for an example, so I'm not looking to combine the global Utils or not use globals at all... I'm just trying to figure out the requires option.

UPDATE

Thanks to Izhaki's answer below, I figured something out. If I want to use a required class in a class I'm defining, I would have to wait for the object to get created (IE, use initComponent), so my store and grid code changes to:

app/store/TheStore.js

Ext.define('Test.store.TheStore', {
  extend: 'Ext.data.Store',
  requires: ['Test.model.TheModel'],
  model: 'Test.model.TheModel'
});

app/view/TheGrid.js

Ext.define('Test.view.TheGrid', {
  extend: 'Ext.grid.Panel',
  alias: 'widget.thegrid',
  requires: ['Test.store.TheStore'],
  store: 'TheStore',
  columns: [
    {header: 'Name', dataIndex: 'name'},
    {header: 'Phone', dataIndex: 'phone'},
    {header: 'Hello', dataIndex: 'hello'}
  ],
  // This is the change that works
  initComponent: function() {
    this.callParent();
    this.store.loadData([
      {name: 'keanu reeves', phone: '1800matrices', hello: Test.Utils.getText()},
      {name: 'james earl jones', phone: '1800starwar', hello: 'nothing here'},
      {name: 'barack obama', phone: '1800prsidnt', hello: 'hello world'}
    ]);
  }
});

This works, but my last question is... do I have to have the requires for TheModel in TheStore, and/or TheStore in TheGrid? It seems like TheController is taking care of all of those requires because I can use Test.Utils in TheGrid, but TheGrid doesn't specifically state that it requires Test.Utils.

Also, this example from the Sencha docs makes me more confused because I'm clearly not using Test.Utils until TheStore is created, but this example seems like it can use the Child class without having to wait for it to initialize (using initComponent).


Solution

  • This is actually not a dumb question at all.

    You can look at requires as a way to tell ExtJS:

    "When you construct an object of this class, please make sure to dynamically load the required scripts first".

    You are right about this line:

    requires: ['Test.Utils', 'Test.Utils2'],
    

    not being needed in app.js, reason being is that the application already has:

    controllers: ['TheController'],
    

    which is the same as saying you require the js script in which TheController resides (any model/view/controller/store definition also mean that the related scripts are required, ie will be dynamically loaded).

    TheController has:

    requires: ['Test.model.TheModel', 'Test.Utils'],
    

    which will load these dynamically - this is why the same requires is not needed it app.js;

    The reason you get Firebug throwing Test.Utils is undefined is that you give a config (hello) with a reference to an object that is not yet dynamically loaded - there's no Test.Utils in the scope until TheStore is constructed.