ExtJS 6: Should I use the config object? - javascript

I'm building an application with ExtJS 6.
I've already read the guides, tutorials and best practice tips.
But what I dont understand yet is, why should I use the config object?
With config:
Ext.define('MyProject.foo.Bar', {
extends: 'Ext.window.Window',
...
config: {
title: 'My title'
}
});
Without config:
Ext.define('MyProject.foo.Bar', {
extends: 'Ext.window.Window',
...
title: 'My title'
});
Both are working as expected.
Can anyone tell me the difference and possible benefits?

It's all described in the Class System guide:
Configurations are completely encapsulated from other class members
Getter and setter methods for every config property are automatically generated into the class prototype during class creation
if methods are not already defined.
The auto-generated setter method calls the apply method (if defined on the class) internally before setting the value. You may override
the apply method for a config property if you need to run custom logic
before setting the value. If your apply method does not return a
value, the setter will not set the value. The update method (if
defined) will also be called when a different value is set. Both the
apply and update methods are passed the new value and the old value as
params.

Related

How can I access native HTML attributes in stenciljs? And how to make it appear in the documentation?

The web components made with stenciljs don't extend HTMLElement. How can I access the native attributes and use them in my component, say the title attribute? When I add #Prop() title and use it in my template an error is shown.
Adding #Prop() also makes the attributes visible in the generated documentation. How can I add the used or required native attributes to the generated documentation?
Compiler error I'm getting:
The #Prop() name "title" is a reserved public name. Please rename the "title" prop so it does not conflict
with an existing standardized prototype member. Reusing prop names that are already defined on the element's
prototype may cause unexpected runtime errors or user-interface issues on various browsers, so it's best to
avoid them entirely.
Yes, you are not allowed to do so but you can pass HTML attributes directly to the element without declaring them with the #Prop decorator. In your case, just pass title to your component.
<your-component title="test title"></your-component>
Then, if you would like to read the value of your title attribute, you have to get the reference to the host element first.
#Element() host: HTMLElement;
Then, you can read it with your host element's getAttribute method, for instance:
componentDidLoad() {
const title = this.host.getAttribute('title');
console.log(title); // or do something with it
}
#Prop() is like reading from tag to component
#Prop({"reflectToAttr": true}) will keep a two way binding and updates both - tag to component & component to tag
It's important to know that you are not allowed to update your #Prop variables inside the component until you specifically allow it with the mutable property. #Prop({"mutable": true})
If you want both just use comma seperated syntax like:
#Prop({"mutable": true, "reflectToAttr": true})
For details please go here:
https://stenciljs.com/docs/properties
I faced sometimes some issues using the native attributes like "title", "focus" and so on. The correct way would be using "data" before the attribute like "data-title", "data-focus" and in the component #Prop() dataTitle, #Prop() dataFocus.
But to be honest i don't like that the developer using the web-components have to learn a web-component specific syntax so i use the native attributes anyway. Which results sometimes in some errors that you can fix easily. But this would be a topic for another question.
#Update
I just realized that in newer StencilJS versions is just #Prop({"reflect": true}) but the idea is still the same

EmberJS: Observing JavaScript object's getter in computed properties

I have a JavaScript object with dynamically computed properties (using getter and setters), returned by a third party library and I want to observe them on my Ember computed properties.
If I pass such a property as a dependency in my computed property like below,
someProperty: computed('jsObject.property', function () {
// Do something
})
Ember makes it undefined. Is this is bug in Ember or am I doing something wrong?
Ember supports watching ES5 getters only from version 2.4 (https://github.com/emberjs/ember.js/pull/12491). Updating fixed my issue.

ExtJs Two way binding between parent and child component

I have two components: A panel and a custom text field.
The panel has a viewmodel and I want to bind a value (called testData) from that viewmodel to a property (called test) of the custom text field.
That works fine ...basically.
But when the test property of the text field is changed, the testData in the viewmodel of the panel does not update accordingly. I mean when the test property of the child element (the textfield) is modified, the testData property of the panel's viewmodel should contain the same value that is in test, just like a normal two-way bind.
I'm not sure what I'm doing wrong, but here is what I've tried to far:
https://fiddle.sencha.com/#fiddle/20pu&view/editor
Ext.define('MyMain', {
extend: 'Ext.panel.Panel',
alias: 'widget.main',
width: '100%',
bodyPadding: 10,
viewModel: {
data: {
testData: 'Example Data'
}
},
bind: {
title: '{testData}'
},
items: {
xtype: 'myField',
bind: {
test: '{testData}'
}
}
})
Ext.define('MyField', {
extend: 'Ext.form.field.Text',
alias: 'widget.myField',
fieldLabel: 'Data',
width: '100%',
config: {
test: null // when test is changed, it should also affect the {testData} bind of the main component, causing the title to change
},
setTest(value) {
this.test = value + ' modified!' // because of the bind, this /should/ automatically get appied to the viewmodel's `testData` and thus to the panel title
this.setValue(this.test) // whenever the `test` property is changed, we write the contents to the value of the text field (just to visualize the `test` property).
// But as you can see, the panel title will still just say `Example Data` and not `Example Data modified!` as it should.
},
getTest(){
return this.test
}
})
Ext.application({
name : 'Fiddle',
launch : function() {
Ext.create('Ext.container.Viewport', {
items: [{
xtype: 'main'
}]
})
}
})
Update: (after reading your comments on other answers)
In general, mentioning the property in the config block and include it in publishes will make any property two-way bindable.
ExtJS will generate the getter and setter methods for it. The setter method takes care of binding. Now, whenever anyone updates the property value (using the setter), the new value will be passed on to the bound viewModel and in turn to the other components.
Accessing the property directly, this.test or this.viewModel.data.testData and assigning values to them will not be reflected in the controls bound to this property.
In case you are providing an implementation for the setter function (setTest) of a published property, ensure that this.callParent(...) gets called from it.
The usage of field's value property to display the contents of test caused the earlier confusion. Here is a fiddle with two-way bindable test property without any special handling in the MyField class.
Click on the 'Get test' button, the value should be 'Example Data' (from viewModel).
'Set testData' button will update the value in the viewModel. Use the 'Get test' button again to verify that the value of test has also been updated.
'Set test' button assigns a new value to the field's test property and this will be reflected in the panel's title.
Have a look at this forked fiddle.
In your implementation, the setTest method is directly changing the value of this.test to value + ' modified!'. This will not update the value of testData in viewModel as binding works via the getter and setter functions implemented of the properties specified in the config.
If you want to change title while changing Textfield then you have to bind value property because changing textfield's value only changes value property of field.
bind: {
test: '{testData}',
value : '{testData}'
},
If you don't want to bind it with value then on change event you have to set value of test property.
listeners : {
change : function(field, newValue, oldValue, eOpts ){
field.setTest(newValue);
}
}
Please refer fiddle.
First of all, you need to make the test config twoWayBindable.
This object holds a map of config properties that will update their
binding as they are modified.
Secondly, you don't need to define getters and setters for the config object, in your case.
Each config item will have its own setter and getter method
automatically generated inside the class prototype during class
creation time, if the class does not have those methods explicitly
defined.
You might to, but it will override the default methods which take care of updating the binding, among other things.
By standardize this common pattern, the default generated setters
provide two extra template methods that you can put your own custom
logic into, i.e: an "applyFoo" and "updateFoo" method for a "foo"
config item, which are executed before and after the value is actually
set, respectively.
The twoWayBindable config relies on the update template method, and when you specify your own setter, the update method will never get called, and the binding won't be updated.
In other words, when leveraging the config feature, you mostly never
need to define setter and getter methods explicitly. Instead, "apply"
and "update" methods should be implemented where necessary.
So, in your example, here are the steps you need to take:
Remove the setTest and getTest method declarations.
Add the twoWayBindable config containing test.
twoWayBindable: ['test']`
Hook up any additional logic into the applyTest or updateTest template methods. For example, updating the field value after the test value gets set.
updateTest(testValue) {
this.setValue(testValue)
}
Here is the working fiddle: https://fiddle.sencha.com/#fiddle/20rs&view/editor
In order to be able to bind custom class properties you need to list these in the twoWayBindable config.
Don't modify the value to be set in the setter and don't call the setter recursively. It is better to write an update<Fieldname>() function. Those are meant to handle updates in your view and they usually don't modify your data structures.
Based on 2.): Override the view update function of the form field to catch changes done to the value.
Here is the complete fiddle:
https://fiddle.sencha.com/#fiddle/218m&view/editor
Some things to note here:
after 3 seconds, the ViewModel testData value is updated
after 6 seconds, the setTest() setter of the field is called
after 9 seconds, the setValue() method from your input field is triggered
at the end, you could change the input field value to change the panel title
This is to illustrate the various scenarios.

Get access to Aurelia's Dependency Injection system without constructor injection

Is there a way to get access to Aurelia's Dependency Injection system without constructor injection.
I have a class called Box. I need to know when one of its properties change so I can update my validation. I found that I can use bindingEngine.propertyObserver from this answer.
But my instances of Box are created by BreezeJs, not Aurelia. So using #inject (or #autoinject in my case) to get the instance of bindingEngine is not going to work.
I saw aurelia.container.get will let me resolve from Aurelia's DI framework. But that needs the current instance of the Aurelia object. The only way I can see to get that is... constructor injection!
So, to get around constructor injection, you need... constructor injection!
I hope I am missing something and there is another way to get an instance of bindingEngine without constructor injection.
NOTE: For now I will just convert my variable in to a javascript property and fire an changed event on my own. But I know that this is going to move me to dirty checking... :(
If you want to know when a breeze entity's properties change, use the entityAspect.propertyChanged event:
http://breeze.github.io/doc-js/api-docs/classes/EntityAspect.html#event_propertyChanged
order.entityAspect.propertyChanged.subscribe(
function (propertyChangedArgs) {
// this code will be executed anytime a property value changes on the 'order' entity.
var entity = propertyChangedArgs.entity; // Note: entity === order
var propertyNameChanged = propertyChangedArgs.propertyName;
var oldValue = propertyChangedArgs.oldValue;
var newValue = propertyChangedArgs.newValue;
});
Circumventing constructor injection is not recommended. It violates the dependency inversion principle, however there is a mechanism for doing so:
main.js
export function configure(aurelia) {
aurelia.container.makeGlobal();
...
}
box.js
import {Container} from 'aurelia-dependency-injection';
let bindingEngine = Container.instance.get(BindingEngine);

Extjs: extend class via constructor or initComponent?

In extjs you can always extend an extjs class via the constructor(). For classes derinving from Component you can also extend via initComponent().
I am wondering why so many code extend via initComponent, whereas constructor seems to be the universal extension method. Does initComponent offer clear advantage over constructor?
First off, the ability to override via constructor was added in a later version of Ext than initComponent, so all code of a certain age would have to use initComponent. These days, you would still override initComponent if you want to do anything after the base class initComponent is called (constructor would be too early for this), but before the component is rendered. In many cases (like the most common, setting up configs), it does not practically matter either way and most people do whatever is most convenient. However, there are some cases where it matters.
Let me try an updated answer in terms of ExtJS versions 4.0-4.2 and beyond.
The constructor() is the object/class before create method. And initComponent() is the component before show method.
constructor: function(config) {
// ctor #1 - insert code here to modify config or run code to inject config
// probably the cheapest place to make changes - before anything has been built
this.callParent(arguments);
// ctor #2 - insert code here if you need to make changes
// after everything has been rendered and shown, IIUC
},
initComponent: function() {
// this function runs between ctor #1 and ctor #2
// initComponent #1 - the UI component object tree is created,
// (this object and child objects from config { items: [{...}]})
// but they have not yet been rendered to DOM or shown.
this.callParent(arguments);
// initComponent #2 - I believe this is equivalent to ctor #2,
// I would prefer ctor as it is more universal.
}
Panels with children or complex layout you'll probably need to use initComponent, because you'll need to inspect and manipulate the components (the UI object graph).
But for individual form elements (combobox, button, etc.) then I stick with constructor, which I believe is lighter (before any complex object construction or DOM changes) and is more universal. IOW constructors can be used for simple UI, models, and data stores; the latter two can't use initComponent.
So I only use initComponent when there's a reason to do so. Often when I write an initComponent function I'm trying to manipulate child UI objects, and my next step is to extract that child control into its own Ext.define(), the move the custom code to run in the child control class, which removes the complex init from the parent panel. This process I've repeated 4 times in my latest page.
Here are some relevant quotes from Jay Garcia's book ExtJS in Action:
initComponent is executed inside the Component class’s constructor, but only after a few crucial setup tasks for the Component have taken place. These tasks include the caching and application of the configuration object properties to the instance of the class
And later, and in light of constructor being where config parameters get applied to the instance:
if configured instances of the subclass will ever need to be cloned via the cloneConfig ....then extending via the constructor is the best choice.
By the way, despite Jay's book being about ExtJS 3 it appears that cloneConfig is still relevant in ExtJS4; see:
http://docs.sencha.com/ext-js/3-4/#!/api/Ext.Component-method-cloneConfig
and
http://docs.sencha.com/ext-js/4-0/#!/api/Ext.Component-method-cloneConfig

Categories