I have a very simple extension in Ext JS from Ext.form.Panel:
Ext.define('path.to.SomeClass', {
extend : 'Ext.form.panel',
xtype : 'some-class'
config : {
hasDog : true
},
constructor : function (config) {
if (this.config.hasDog) {
// do something dog related
} else {
// do something not dog related
}
}
});
I then have a "container" for this custom componet:
Ext.define('path.to.OtherClass', {
extend : 'Ext.window.Window',
// ....
items : [{
xtype : 'some-class',
hasDog : false
}]
});
However, for some reason unknown to me, the if...else evaluation in SomeClass is always picking up the default configuration for hasDog. Am I not correctly configuring some-class in OtherClass's items config?
To add a bit more context, OtherClass is called via some using code:
var window = Ext.create('path.to.OtherClass');
window.show();
From what I can see, the above is pretty standard stuff - at least in thought.
The reason why you always get the default configuration is because you're accessing this.config, which is the declaration of your configuration, instead of the actual configuration from the constructor argument. So either use config or - once you've called the parent class constructor - this.
constructor : function (config) {
// before parent class constructor or this.initConfig was called:
console.log(config.hasDog);
// call parent class constructor
this.callParent(arguments);
// after parent class constructor or this.initConfig was called:
console.log(this.hasDog);
}
Also have a look at the documentation:
Note: You need to make sure Ext.Base.initConfig is called from your constructor if you are defining your own class or singleton, unless you are extending a Component. Otherwise the generated getter and setter methods will not be initialized.
In your case, since your extending a component, calling the parent constructor should suffice (as shown in the example above).
Related
This is a follow-up question from the previous: Why is the parent prototype method still executable after overriding?.
As we see, the literal object {metadata, aaa, init} was initialized by the extend function as a 2nd import variable, so this object should be the object automatically automatically instantiated and represents the extended sub-class. Then inside the object, it inherited the init method from its parent class and overrode it. first call the parent init method to trigger some necessary things, then setup the data for this extended component.
My question is: the init method in this literal object is its own init method, because this refered to the literal object, right? if it's yes, why I can't see it under this proprieties in my debugger tool.
enter image description here
sap.ui.define([
"sap/ui/core/UIComponent",
"sap/ui/model/json/JSONModel",
"sap/ui/model/resource/ResourceModel",
], function (UIComponent, JSONModel, ResourceModel) {
"use strict";
return UIComponent.extend("sap.ui.demo.walkthrough.Component", {
metadata: {
"interfaces": [
"sap.ui.core.IAsyncContentCreation",
],
"rootView": {
"viewName": "sap.ui.demo.walkthrough.view.App",
"type": "XML",
/*"async": true, // implicitly set via the sap.ui.core.IAsyncContentCreation interface*/
"id": "app",
},
},
aaa: function () {
console.log("aaa");
},
init: function () {
// call the init function of the parent
UIComponent.prototype.init.apply(this, arguments);
// set data model
var oData = {
recipient: {
name: "World",
},
};
var oModel = new JSONModel(oData);
this.setModel(oModel);
// set i18n model
var i18nModel = new ResourceModel({
bundleName: "sap.ui.demo.walkthrough.i18n.i18n",
});
this.setModel(i18nModel, "i18n");
},
});
});
Hope this other answer clears some things up:
__proto__ is the actual object that is used in the lookup chain to resolve methods, etc. prototype is the object that is used to build __proto__ when you create an object with new
This means when you see methods in the prototype section it doesn't mean that the original parent class is overwritten. Your new "class" sap.ui.demo.walkthrough.Component also has/is a prototype and this (which is an instance of your new "class") has it's own __proto__. This new prototype/proto constains methods form the parent prototype and can also define new methods.
And from the documentation of sap.ui.base.Object.extend:
Creates a subclass of class sap.ui.base.Object with name sClassName and enriches it with the information contained in oClassInfo.
oClassInfo might contain three kinds of information:
[...]
any-other-name: any other property in the oClassInfo is copied into the prototype object of the newly created class. Callers can thereby add methods or properties to all instances of the class. [...]
So when you do this.__proto you will see the build plan for the current instance.
Do this.__proto__.__proto__ to get the build plan from the parent class. Do this.__proto__.__proto__.__proto__ to get the grand parent class (and so on).
The protos of the parent class are not affected by your child class. But your child classes include everything from the parent class. When you "override" a method you are are adding a method to your new prototype. The parent method still exists in the UIComponent prototype and can therefore be called by explicitly stating UIComponent.prototype.init.apply.
I have a base class which has some properties. I'm inheriting this class into two other controllers. Whenever I set a content to this variable, though, the content is shared between the two controllers. I don't this to happen, otherwise I'd have used Mixins. I want to set the content in one child to something and when I go to the other route, the content is still blank.
// base controller
export default Ember.Controller.extend({
method: null,
actions: {
changeMethod() {
this.set('method', 'content is shared');
}
}
}
// children.js
import BaseClass from './base-class';
export default BaseClass.extend({ //code here });
// child1.hbs
{{method}} - it shows the same as child2.hbs
// child2.hbs
{{method}} - it shows the same as child1.hbs
Ok, what I had to do is the same as they recommend for Mixins: I've created a init() method setting the variables to the default value. In the example I gave it became:
export default Ember.Controller.extend({
method: null,
init() {
this._super():
this.set('method', null);
},
actions: {
changeMethod() {
this.set('method', 'content is shared');
}
}
}
The objects created share the same prototype. According to Mozilla:
Changes to the Object prototype object are seen by all objects through prototype chaining, unless the properties and methods subject to those changes are overridden further along the prototype chain. This provides a very powerful although potentially dangerous mechanism to override or extend object behavior.
More into this topic can be seen here
There are two ways to avoid the sharing behavior.
Use computed properties
Initializing on the init function, as I did
More reference in the subject here as #lock said
Say I have a custom component as my-panel
And in the backing JS (my-panel.js)
this.get('targetObject')
Also I reference/use this component from another custom component say my-sections.hbs as
{{my-panel}}
My question is when I do
this.get('targetObject') //Called inside my-panel.js
How/Where does it lookup the property targetObject in ? What does the chain look like?
It looks it up within the scope of the backing custom component js (probably).
The chain/scope really starts from the this object, so depending what this is in that scope, that's really where it is looking it up on. If you had a reference to a different object, or were within a callback function my statement may not be true.
App.MyCompComponent = Ember.Component.extend({
foo: 'bar',
actions: {
doit: function(){
// `this` is the scope of an instance of this component.
alert(this.get('foo'));
}
}
});
http://emberjs.jsbin.com/totibidohe/edit?html,js,output
You can think of it outside the scope of being in the template as well, and just create an instance and reference that instance and it would be the same.
var MyCompComponent = Ember.Component.extend({
foo: 'bar',
actions: {
doit: function(){
alert(this.get('foo'));
}
}
});
var j = MyCompComponent.create();
alert(j.get('foo'));
http://emberjs.jsbin.com/xekohoguto/1/edit?html,js,output
I am having a hard time overriding a constructor from a singleton class in ExtJS 4.1. I have an override defined, but by the time my override statement gets processed, the constructor has already executed.
Ext.define('singleton', {
singleton: true,
constructor: function() {
alert('replace me');
}
});
Overriding the constructor of a singleton doesn't make sense, a singleton is a class that is very early on in the execution of Extjs turned into an instance of itself. This means that you are trying to override an instance of a class and not the class itself.
I can recommend that whatever you want done to the singleton be done in a separate method in the class definition that you can call very early in Ext.onReady() or your apps instantiation.
You can't override a singleton class, that is true, but you can override the singleton instance:
Ext.define('singleton', {
singleton: true,
constructor: function() {
// does whatever
}
});
Ext.define('singletonOverride', {
override: 'singleton',
// adding new property
foo: 'bar',
// adding new method
baz: function() {},
initSingletonOverride: function() {
// do whatever is needed
// to augment the singleton
// behavior the way you want
}
},
function() {
// `this` is the singleton instance
this.initSingletonOverride();
});
Keep in mind that all this fluff comes down to the JavaScript objects. A class is an Object, and a class instance is another Object. You can override either of them, or both; the class system is here to help you not to lose track of what you're doing.
I am trying to convert a class that extends Ext.app.Controller to extending Ext.app.Application. Since Ext.app.Application is a child class of Ext.app.Controller I assumed that simply changing the class being extended would work, instead however, it causes an error in the console that says Uncaught TypeError: Cannot call method 'substring' of undefined. The error occurs at the this.callParent(arguments) inside the constructor: function, Does anyone have any suggestions as to what might be causing this?
You cannot use a constructor within a Ext.app.Application class changes will come with 4.2 but till that use the launch method for example to apply stuff. And do NOT extend.
Application is sort of a singleton instance an get just intialized by calling
Ext.application({
name: 'MyApp',
launch: function() {
Ext.create('Ext.container.Viewport', {
items: {
html: 'My App'
}
});
}
});
Trying to run more instances result in problems but you will be able to do this with 4.2 like so
Ext.define('MyApp.Application', {
extend: 'Ext.app.Application',
name: 'MyApp'
...
});
Ext.application('MyApp.Application');