-->

ExtJS - How to pass Component config options to XT

2019-07-09 04:02发布

问题:

I defined a following class extending Ext.view.View:

Ext.define('Aft.view.comments.CommentsList', {
  extend: 'Ext.view.View',
  xtype: 'comments-list',

  parameter: false,

  tpl: new Ext.XTemplate(
    '<tpl for=".">',
    '  <div class="comment">',
           // some code here
    '    <div class="fault">',
    '      <tpl if="this.parameter">',
             // some code also here         
    '      </tpl>',
    '    </div>',
    '  </div>',
    '</tpl>',
    {
      strict: true,
      // other methods and fields
    }),

  initComponent: function() {
    this.config = Ext.apply({}, this.config);
    this.tpl.config.parameter = this.config.parameter;
    this.callParent(arguments);
  }
});

As you can see, I am trying to pass a boolean parameter from outside of the component to XTemplate inside of it. I am trying to do it, because the component is used in 3 different places. In one of them, I want it to look slightly different (just without one div). I figured out that parametrised XTemplate would be a nice solution, but I can not force it to work. I am creating the component like this:

items: [
    {
        xtype: 'comments-list',
        parameter: false
    }
]

And regardless of what I put as parameter, everything that I put in config seems to be shared among other instances of my custom class. So either every CommentsList has parameter set to true, or every has it set to false. I obviously am missing something, but it seems that this topic creates confusion for others too. Despite that I did not find a proper way to solve this. I have tried various combinations with config, factoryConfig and variables straight in class definition, but nothing seems to work.

Therefore, I would be very grateful for a solution, or at least a valuable link to blog post or documentation. Thank you very much in advance.

If that is relevant, I am using ExtJS 6 classic.

回答1:

The reason is that your tpl is on the prototype and is therefore shared between instances. That is my biggest pet peeve against Ext's way of setting objects on the prototype without understanding what that really means. That also means you don't have access to this if you need it, as you'll see in my example because you need to "pass down" the configuration into the template.

Your great question is actually giving me a nice simplified example that proves a point I always try to make to my teams (been developing Ext-JS since it was yui-ext);

Your tpl object is being set on Aft.view.comments.CommentsList.prototype so it's being shared.

The correct solution is to initialize tpl from the constructor (or initComponent) so that a new template is created for each instance. See https://fiddle.sencha.com/#fiddle/111v

Ext.define('Aft.view.comments.CommentsList', {
  extend: 'Ext.view.View',
  xtype: 'comments-list',

  // Primitives are always OK on prototypes because if you write, you will
  // modify a property on the instance, not the prototype
  parameter: false, 

  initComponent: function() {
    this.tpl = new Ext.XTemplate(
    '<tpl for=".">',
    '  <div class="comment">',
           // some code here
    '    <div class="fault">',
    '      <tpl if="this.parameter">',
             // some code also here         
    '      </tpl>',
    '    </div>',
    '  </div>',
    '</tpl>',
    {
      strict: true,
      parameter: this.parameter
    });
    this.callParent(arguments);
  }
});

Ext Prototype Rant

When setting something on the prototype, it means that a caller could still override it when passing in a config object. For example, in the class above I could override the tpl (and break the functionality of the class) when instantiating it.

// Will likely break the class if we have some assumptions in the HTML about the code
items: [{xtype: 'comments-list', tpl: '<div>Broke you</div>'}]

If you define it within initComponent, you are going to override anything the user passes in. Use that wisely. If you define it on the prototype, it's simply a default and your code shouldn't depend on it.

Obviously, we still have to keep in mind that objects on the prototype are shared, so if you want a default object that is not shared, you should use

initComponent: function() {
   Ext.applyIf(this, {
      someProp: {defaultObject: true}
   });
   this.callParent();
}

And lastly, if you have an object that does not change (defaults), then it doesn't matter and it would be good to store in on the prototype to save memory, but you have to be careful that you don't modify it (unless you can use Object.freeze).



标签: Extjs extjs6