Ember lookup property from inside custom component - javascript

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

Related

Understanding extend and method overwriting

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.

AngularJS 1.5 component won't invoke $onChanges upon change

I have an AngularJS 1.5 component inside an html page (not parent component)
and the component won't invoke $onChanges upon change.
HTML
<my-comp standards-data="standardsData"></my-comp>
Component
angular.module("bla").component('myComp', {
templateUrl: '/my-comp.tpl.html',
controller: myController,
bindings: {
standardsData: '<'
}
});
function myController() {
var self = this;
self.$onInit = function () {
self.standardsData = {};
}
self.$onChanges = function (changes) {
self.standardsData.something = changes.standardsData.currentValue.something;
};
}
When I fetch new data in the ctrl of the html that contains my component,
it won't influence the component. I get only into the $onInit of the component
and after $scope.standardsData changes (in the containing html/ctrl),
the $OnChanges of my component won't invoke.
Hope I described the problem correctly,
Thanks !
Your component binding to standardsData is a binding to an object reference, which does not change even when one of its properties is modified. As a result, $onChanges is not triggered. This is due to the fact that $onChanges uses a shallow $watch.
In other words, $onChanges is triggered only if we use primitives (i.e. non-objects) or change the reference of the javascript object bound into the component
So, you need to either: 1) Bind manually to the property/properties you want, rather than the object reference, or 2) Change the entire standardsData object when its data changes. You could also 3) Rewrite $onChanges functionality, I suppose (not recommended).
Option 1: Bind only to the property
Use if the parent controller/component merely changes a property or properties.
<my-comp standards-data-something="standardsData.something"></my-comp>
and
bindings: {
standardsDataSomething: '<'
}
Option 2: Change the reference
Use if the parent controller/component obtains completely new standardsData. You would keep your current HTML for this option and set:
standardsData = {newData}; //Reset the new data from http or wherever
to change the reference.
Some further reading you may find interesting:
http://blog.kwintenp.com/the-onchanges-lifecycle-hook/
http://www.codelord.net/2016/12/20/replacing-angulars-deep-watches-with-the-%24docheck-lifecycle-hook/

Controller parent sharing content between its child

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

Accessing Ext Component Config

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).

Dojo declare - the scope of default properties (instance or static)

I've spent a lot of time debugging strage error in my component. That component have disabled/enabled buttons, but I haven't seen the effect. After a while, I've noticed, the buttons are changed in the last component instance I've created. The declaration looks so:
constructor: function(options) {
for(var i in options){
this[i] = options[i];
}
},
domNode: null,
grid: null,
data: [],
buttons: {},
In the debug, I've seen, that when I create second instance of my object:
new CustomComponent({domNode: dojo.byId('secondid')})
the buttons are already set - their instance is shared by all instances!
In end effect, I've created a static variable in my component. This is not what I wanted! What is wrong in that declaration? How should I made 'buttons' instance separate for each component instance?
I suppose that CustomComponent is a widget? Then you're doing some stuff wrong. The thing you do in your constructor (I suppose that's to populate your widget properties?) is not even necessary since that's already there by default when you use dijit/_WidgetBase.
Same with your property domNode, it's also there already by default if you use dijit/_WidgetBase.
My guess is that by overriding the constructor to act like this, you're actually doing some steps that the WidgetBase should do and thus messing up the private scope of the properties.
An example widget:
var CustomComponent = declare("my/CustomComponent", [WidgetBase], {
grid: null,
data: [],
buttons: {}
});
This code does exactly the same as your widget and is a lot shorter.
An example JSFiddle that has instance scoped properties (as you can see in the console log).
I've inspected the problem more exact. The problem is, that the declare block is executed only once, therefore creating object prototype, which values are copied to instances.
So when I do buttons: {}, I create Object, which is then copied to all children. In end effect, all children have the same buttons instance - I've created a quasi-static field.
All bugs where gone when I've created the object in the constructor:
constructor: function(options) {
for(var i in options){
this[i] = options[i];
}
this.buttons = {}
this.data = []
},
Now every instance of my component have own buttons object.
In fact, my problem was exactly described here:
dojo.declare("my.classes.bar", my.classes.foo, {
someData: [1, 2, 3, 4], // doesn't do what I want: ends up being

Categories

Resources