For initComponent () or not for initComponent ()

I'm afraid when creating an application in ExtJS 4, and part of this confusion is when you need to configure something in initComponent (), and when not ...

For example, in Sencha, their own MVC Application Architecture doc, when they first created the grid view, they defined the built-in storage in the initComponent () method. (See Section “Species Definition”)

Further, when they drove the repository into a separate class, they moved the definition outside of initComponent (). There is a useful comment that draws attention to this fact, but there is no explanation. (See Section "Creating the" Model and Store "section).

I suppose the reason should be obvious, but I miss it. Any pointers?

+42
extjs extjs4
Jan 24 '13 at 1:00
source share
4 answers

If you don’t have a deep understanding of how the ExtJS class system works, you can follow this:

Declare all non-primitive types in initComponent() .

Terminology

  • Primitive types - strings, booleans, integers, etc.
  • Non - primitives are arrays and objects.

Explanation

If the component you are extending needs to be created several times, any non-primitive configs declared as a configuration option (outside of initComponent ) will be shared by all instances.

Because of this, many people run into problems when an extended component (usually an extended grid) is created on several tabs.

This behavior is explained in sra's answer below and in this Skirtle Den article . You can also read this SO question .

+42
Jan 24 '13 at 2:08
source share
— -

First, I will focus on my comments:

@ Alexander Tokarev Do not get me wrong. I'm not talking about component configurations or, even worse, about cases, and porting them to initComponent() , this is not my point.

Now I think about it.

initComponent () should allow all necessary time for an instance of this class. No more no less.

You can spoil the load when defining classes, and most of them happen because people don’t understand how the ExtJS class system works. Since these are components, the focus will be on these. This will also be a simplified example, which should only show some error that I have seen many times.

Let's get started: we have a custom panel that does a lot of great stuff. This necessitates a custom configuration, we call it foo . We add it along with our default configuration option to define the class so that we can access it:

 Ext.define('Custom', { extend: 'Ext.panel.Panel', alias: 'widget.custpanel', foo: { bar: null }, initComponent: function() { this.callParent(arguments); } }); 

But after testing everything becomes strange. The values ​​of our configurations seem to magically change. Here JSFiddle It so happens that all created instances refer to the same instance of foo . But lately I made it

 store: { fields: [ ... ], proxy: { type: 'direct', directFn: 'Direct.Store.getData' } } 

with the store, and it worked. So why doesn't foo work?

Most people see no difference between this small foo object and the configuration (ExtJS), which is basically correct because both are objects (instances). But the difference is that all classes sent by sencha are well aware of what configuration properties they expect and take care of them.

For example, the grid storage property is enabled by StoreManager and therefore may be:

  • Line
  • storeId or
  • store an instance or
  • save configuration object.

During grid initialization, any of them gets permission and is overwritten by the actual storage instance. A store is just one example. I think the more famous is the array of elements. This is an array at definition time and it is redefined for each instance using a MixedCollection (if I'm not mistaken).

Yes, there is a difference between a class definition and the instance it creates. But we need to take care of any new property that contains a link like foo top, and it's not that difficult. Here is what we need to do to fix it for foo example

 Ext.define('Custom', { extend: 'Ext.panel.Panel', alias: 'widget.custpanel', foo: { bar: null }, initComponent: function() { this.foo = Ext.apply({}, this.foo); this.callParent(arguments); } }); 

Here is jsfiddle

Now we take care of config foo when creating the instance. Now this foo example is simplified and it will not always be easy to resolve the configuration.

Conclusion

Always write class definitions as configurations! They should not contain any of the instances mentioned, except for a simple configuration, and should take care of this to allow them to be instantiated.

Renouncement

I do not pretend to cover all this very short letter!

+21
Jan 26 '13 at 9:29
source share

I usually advocate the maximum possible configuration in the class’s configuration settings, because it reads better and is easier to override in subclasses. In addition, there is a high probability that in the future Sencha Cmd will have an optimizing compiler, so if you keep your code declarative, it may benefit from optimization.

For comparison:

 Ext.define('MyPanel', { extend: 'Ext.grid.Panel', initComponent: function() { this.callParent(); this.store = new Ext.data.Store({ fields: [ ... ], proxy: { type: 'direct', directFn: Direct.Store.getData } }); this.foo = 'bar'; } }); ... var panel = new MyPanel(); 

and

 Ext.define('MyPanel', { extend: 'Ext.grid.Panel', alias: 'widget.mypanel', foo: 'bar', store: { fields: [ ... ], proxy: { type: 'direct', directFn: 'Direct.Store.getData' } } }); ... var panel = Ext.widget({ xtype: 'mypanel', foo: 'baz' }); 

Please note that these approaches are very different. In the first example, we strongly encode: the values ​​of the property of the object, the configuration of the repository, the class name MyPanel when using it; we practically kill the idea of ​​the class, because it becomes irrational. In the second example, we create a template that can be reused with a possibly different configuration - mainly with regard to the entire class system.

However, the actual difference lies deeper. In the first case, we actually delay the configuration of the class until runtime, while in the second case, we determine the configuration of the class and apply it at very different stages. In fact, we can easily say that the second approach introduces something that JavaScript is missing from the beginning: the compile-time phase. And this gives us many opportunities that are used in the framework code itself; if you need some examples, see Ext.app.Controller and Ext.app.Application in the latest beta version 4.2.

From a more practical point of view, the second approach is better because it is easier to read and solve. Once you understand the idea, you will find that you are writing all your code, because it is simply simpler.

Look at it this way: if you write an old style web application that creates HTML and stuff on the server side, you would try not to mix the HTML code with the code, would you? Templates on the left, code on the right. This is pretty much the same as initComponent data in initComponent : sure this works, right down to the point. Then it becomes a bowl of spaghetti, which is difficult to maintain and stretch. Oh, and check it all out! Ugh.

Now time when you need to do something with an instance at run time, as opposed to class definition time — a classic example — using event listeners or calling control in controllers, you will need to take the actual function references from the object instance, and you have to do this is in initComponent or init . Nevertheless, we are working to mitigate this problem - there should not be a strict requirement to hard code all of this; Observable.on() already supports string listener names, and MVC stuff is coming soon too.

As I said in the comments above, I will have to write an article or a guide for documents explaining things. It will probably have to wait until 4.2 is released; meanwhile, this answer must shed light on this question.

+13
Jan 24 '13 at 1:17
source share

I was looking for the answer to the same question when I was here, and when I saw these answers, I was disappointed. None of them answer the question: initComponent () or constructor?

It's nice to know that the objects of the configuration class are separated, and you need to initialize / process them on one instance, but the code can go to the constructor, as well as to the initComponent () function.

My assumption was that the constructor of the Component class calls initComponent () somewhere in the middle, and I was not very right: I just had to look at the source code, in fact, the AbstractComponent Constructor .

So it looks like this:

 AbstractComponent/ctor: - stuffBeforeIC() - initComponent() - stuffAfterIC() 

Now, if you extend the component, you will get something like this:

 constructor: function () { yourStuffBefore(); this.callParent(arguments); yourStuffAfter(); }, initComponent: function () { this.callParent(); yourInitComp() } 

The final order he calls:

 - yourStuffBefore() - base ctor by callParent: - stuffBeforeIC() - initComponent: - base initComponent by callParent - yourInitComp() - stuffAfterIC() - yourStuffAfter() 

So, in the end, it all depends on whether you want / need to enter the code between stuffBeforeIC and stuffAfterIC, which you can find in the constructor of the class that will be expanded.

+12
May 01 '13 at 11:29
source share



All Articles