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
Related
I have experience with Angular/Ember/React and in every of these frameworks you have a clear notion of component that accepts argument as initial data and callbacks as reference to the parent (I believe in Ember its called "Data down, action up"). I tried to learn ExtJS but i dont see what should i use as equivalent of this interface, how communication between nested components should look like?
The common pattern in Ext for passing initial data is through an object parameter. The properties of the passed object correspond to a config object in the class definition.
Ext automatically creates getter and setter methods for all properties in the config object, as well as apply and update methods for transforming values before and after they're set.
For communication between components, you can wire up listeners that handle events fired from the nested component. The events can be standard provided by Ext (such as 'click' fired from an Ext.button.Button) or custom ones you make up and send using fireEvent.
Another way of communicating between components is to use the parent's view model and the component's bind config.
View models come with the benefit of being visible by all descendant components, so child components can bind to a view model property, which if not found on the child component itself, will be propagated up the chain of ancestors until a matching view model property is found.
Initial data
• In Ext JS every component accepts a JSON object as its initial config data.
• You can even set the configs at initComponent method associated with every component you create. The initComponent template method is an important initialization step for a Component. The initComponent method of the class being created is called first, with each initComponent method up until the hierarchy to Ext.Component being called thereafter. This makes it easy to implement and, if needed, override the constructor logic of the Component at any step in the hierarchy.
Ext.create('Ext.Component', {
html: 'Hello world!',
width: 300,
height: 200,
padding: 20,
style: {
color: '#FFFFFF',
backgroundColor:'#000000'
},
renderTo: Ext.getBody()
});
Data Communication between components.
• In Ext JS communication between components is via event (listeners and fireEvent with listen config).
• Communication can be also between child and parent ViewModels. The most commonly used aspect of a ViewModel is the bind method. This method takes a "bind descriptor" and a callback to call when the data indicated by the bind descriptor either becomes available or changes.
Every component can be associated with ViewModel and Controller. These are the most commonly used once.
I currently have the following interfaces defined in a project
interface foo {
fooProperty: number;
fooFunction(): void;
}
interface bar extends foo {
barProperty: string;
barFunction(): void;
}
Now I want to define a class like
class myBar implements bar {
public barProperty: string ="bar";
public barFunction() {
// do dosmething
}
}
However I don't want to have to implement foo's functions and properties too, as these are already defined in an existing JS class which the interfaces define.
Essentially what I'm trying to do is create a typescript class that is extended from a 3rd party JS library for which I have "d.ts" files that describe the implementation, but which is NOT in typescript in it's source form.
In order to use a particular function in the 3rd party library, I have to create a custom class that derives from a class they provide in JS, but which I then override using "prototype", however I need to do this using typescript.
Update 1
It seems that folks reading this are not quite getting what I'm trying to achieve here, so let me elaborate a bit more.
In the project I'm working on we use a 3rd party JS lib, lets call this "thirdLib.js"
Within "thirdLib.js" there is functionality, that in order to use it, requires me to extend a JS style class provided as part of "thirdLib.js" like so:
function myClass(){
thirdlib.thirdclass.call(this);
}
thirdLib.utilities.extendclass(myClass, thirdLib.thirdClass);
myClass.prototype.overriddenFunc = function(){
// Do custom function here
}
The "extendClass" method in "thirdlib" copys the constructor of the class I'm deriving from into my constructor or so I'm lead to believe.
Which is then used later on, elsewhere in "thirdlib.js" EG:
var myStuff = new thirdLib();
var theClass = new myClass();
myStuff.registerSomething(theClass);
myStuff.doAThing();
At the point where I call "doAThing", thirdLib.js has the new JS class registered with it, and that class has ALL the original functionality as present in "thirdClass" but with MY customisations added to it.
All I have for "thirdLib.js" is the JavaScript code (Minified) and a set of Typescript definition files (Which I've written myself, as none where provided with the lib), and I need to be able to create "myClass()" using normal Typescript functionality, while still extending and consuming everything that's in the original JS class, and while being able to add my functionality to the TS class and have that override the functionality in the base JS class when I do.
Update April 2022
For those who are wondering, about 6 months after my last comment I moved on from the company I was doing this project for, and so I never got to see it through to a resolution, since I don't have the code or access to it anymore, I doubt very much it will ever be resolved. For those who are interested, the "Custom Drawing Handler" I was trying to implement, was a custom drawing class for a (Then Commercial, now it's OSS) 3rd party JS library called "MXGraph"
If I got your problem right you could just have your class myBar extend the class myFoo, hence inheriting the given implementation and fulfilling the criteria defined in the interface.
Say I want two views (polymer-elements) to share a model for example.
In Angular the model would live in a singleton service that gets injected into the views, both views read from the same source.
I tried emulating this approach with Polymer so I can do something like:
<polymer-element name="view1">
<template>
<my-model></my-model>
...
</template>
...
</polymer-element>
<polymer-element name="view2">
<template>
<my-model></my-model>
...
</template>
...
</polymer-element>
I like this approach because it's a declarative way of defining dependencies, and it basically works the same as <core-ajax> and other "out of the box" Polymer elements.
With this way I need to wait for the domReady lifecycle callback before I can interface with any element declared in the template, so this is where I'm holding my initialisation logic at the minute. The problem is that this callback gets called once for each <my-model> element declared (so <my-model> would be initialised twice in this example because it's present both in <view1> and <view2>). To make sure that my model follows the singleton pattern I have to move state outside of the element instance, something like this:
<polymer-element name="my-model">
<script>
(function(){
// private shared state
var instances = [], registered; // pattern variables
var foo; // state, model, whatever
// element init logic
Polymer('my-model', {
// Polymer callbacks
domReady: function(){
if (registered === (registered=true)) return;
// singleton init logic
foo = 'something';
// event handlers
this.addEventListener('foo', function(){ foo += 'baz'; });
},
attached: function() { instances.push(this); },
detached: function(){
instances = instances.filter(function(instance){
return instance !== this;
}.bind(this));
},
// element API
update: doSomething,
get state() { return foo; }
});
// private functions
function doSomething(){ foo += 'bar' }
})();
</script>
</polymer-element>
So it works but it looks wrong to me. Is using <polymer-element> generally incompatible with the singleton pattern? Should I move away from Polymer for models and services? How do Polymer core-elements get away with it?
[EDIT] I added some event listeners to the initialising code above. They're only registered in one instance to avoid the listeners triggering multiple times across multiple instances. What would happen if the instance where the event handlers are declared gets removed? Will that not break the asynchronous logic?
I'd go like this:
Define your model on the main page and call it from your views.
if it gets removed you could:
1 - listen for the "detached" lifecycle callback and inside it register it imperatively or
2 - store stuff on a prototype build in a higher level object and access it the way you fancy the most.
3 - if all fails, (i'm not sure it will but i guess so as i've yet to use this kind of implementation, as of now i talk to php and pass around objects i need persistent) you could use a "prepareForRemoval" knowing you will leave the instance, local storage your stuff and do number 1 then "recoverFromRemoval" if you know what i mean by camel casing prototype suggestions.
Anyways i'm not very fond of singletons. Polymer is powerful front-end stuff but i'm not sure it's the best way to go about it.
in the API docs they do not mention the possibility of getting it cut off (as you can see)
but i honestly think you're right and you would lose your stuff.
That's just my 2 cents actually just a inellegant sollution i came up for at this very moment, maybe #ebidel, #DocDude or #dodson can help us in that matter but you can't really tag em here on SO i'll tag em on G+ for us, you sir got me intrigued.
BTW why would you move away from your main page? there's no point for it in polymer you should change the content dynamically not get away from it. what would be the usage scenario?
ps.: sorry, i hate capitalizing proper nouns.Get over it
EDIT (wouldn't fit on the comments):
I expressed myself wrong. Anyways i strongly think i wasn't understanding what you wanted.
Well, if i got it right this time yes it will fire multiple times (they are supposed to), but it shouldn't cut others out once a particular view gets removed.
As for your initialisation logic i would go about adding a listener to the window or document (i think window is more advisable) itself waiting for the 'polymer-ready' event.
"To make sure that my model follows the singleton pattern I have to
move state outside of the element instance"
Yes thats right. but don't wait for the domready in it's prototype, instead use a construct or contruct-like and call it it as the callback of the aforementioned event listener. i'll edit my answer to make it clearer (if it's not, let me know) when i get back home. i hope you got i meant.
if you don't i'll be back soon.
In browsers, window == singleton object by definition.
Simple use:
var window.instances = [];
var window.registered;
var window.foo;
instead.
Another way is to use Polymer core-meta element:
<core-meta id="x-foo" label="foo"></core-meta>
<core-meta id="x-bar" label="bar"></core-meta>
<core-meta id="x-zot" label="zot"></core-meta>
<core-meta id="apple" label="apple" type="fruit"></core-meta>
<core-meta id="orange" label="orange" type="fruit"></core-meta>
<core-meta id="grape" label="grape" type="fruit"></core-meta>
<h2>meta-data</h2>
<template id="default" repeat="{{metadata}}">
<div>{{label}}</div>
</template>
<h2>meta-data (type: fruit)</h2>
<template id="fruit" repeat="{{metadata}}">
<div>{{label}}</div>
</template>
<script>
document.addEventListener('polymer-ready', function() {
var meta = document.createElement('core-meta');
document.querySelector('template#default').model = {
metadata: meta.list
};
var fruitMeta = document.createElement('core-meta');
fruitMeta.type = 'fruit';
document.querySelector('template#fruit').model = {
metadata: fruitMeta.list
};
});
</script>
I have a single page web app with multiple backbone.js views. The views must sometimes communicate with each other. Two examples:
When there are two ways views presenting a collection in different ways simultaneously and a click on an item in one view must be relayed to the other view.
When a user transitions to the next stage of the process and the first view passes data to the second.
To decouple the views as much as possible I currently use custom events to pass the data ($(document).trigger('customEvent', data)). Is there a better way to do this?
One widely used technique is extending the Backbone.Events -object to create your personal global events aggregator.
var vent = {}; // or App.vent depending how you want to do this
_.extend(vent, Backbone.Events);
Depending if you're using requirejs or something else, you might want to separate this into its own module or make it an attribute of your Application object. Now you can trigger and listen to events anywhere in your app.
// View1
vent.trigger('some_event', data1, data2, data3, ...);
// View2
vent.on('some_event', this.reaction_to_some_event);
This also allows you to use the event aggregator to communicate between models, collections, the router etc. Here is Martin Fowler's concept for the event aggregator (not in javascript). And here is a more backboney implementation and reflection on the subject more in the vein of Backbone.Marionette, but most of it is applicable to vanilla Backbone.
Hope this helped!
I agree with #jakee at first part
var vent = {};
_.extend(vent, Backbone.Events);
however, listening a global event with "on" may cause a memory leak and zombie view problem and that also causes multiple action handler calls etc.
Instead of "on", you should use "listenTo" in your view
this.listenTo(vent, "someEvent", yourHandlerFunction);
thus, when you remove your view by view.remove(), this handler will be also removed, because handler is bound to your view.
When triggering your global event, just use
vent.trigger("someEvent",parameters);
jakee's answer suggests a fine approach that I myself have used, but there is another interesting way, and that is to inject a reference to an object into each view instance, with the injected object in turn containing references to as many views as you want to aggregate.
In essence the view-aggregator is a sort of "App" object, and things beside views could be attached, e.g. collections. It does involve extending the view(s) and so might not be to everybody's taste, but on the other hand the extending serves as a simple example for doing so.
I used the code at http://arturadib.com/hello-backbonejs/docs/1.html as the basis for my ListView and then I got the following to work:
define(
['./listView'],
function (ListView) {
var APP = {
VIEWS : {}
}
ListView.instantiator = ListView.extend({
initialize : function() {
this.app = APP;
ListView.prototype.initialize.apply(this, arguments);
}
});
APP.VIEWS.ListView = new ListView.instantiator();
console.log(APP.VIEWS.ListView.app);
}
);
I'm wrapping up a Javascript widget in a Wicket component. I want to let the JS side talk to the component. What I've got so far:
Component in question goes like
talker = new GridAjaxBehavior();
this.add(talker);
in constructor
and then, later on, puts something like
"var MyGridTalker = new talker(" + this.talker.getCallbackUrl() + ");";
into the JS.
where GridAjaxBehavior extends AbstractDefaultAjaxBehavior. I want GridAjaxBehavior to spit back some XML when the JS calls it.
Am I doing this the right way? What should GridAjaxBehaviour do to spit back the XML?
Thanks
Spit back some XML for what? Presumably to update the model or the view, yes?
The strength of Wicket is that you don't have to worry about the rendered HTML. In Model-View-Controller terms, you set up the Controller to correctly modify the Model, and Wicket takes care of the View.
The separation is not entirely clear: in fact you can show/hide view components, or change then, and that can be seen as altering the View.
But what you generally don't have to do is directly manage the browser or javascript. Wicket takes care of that, if you take care of making your changes in the Java code.
In Wicket, the Ajax will call a method on your AjaxBehavior with an AjaxRequestTarget target.
In that method (or in methods called from it), you do whatever you need to do, updating models or views, and then you add to the target any view component that that has changed. Wicket takes care of updating the browser.
Here's an example. It's taken from some code I did, but heavily altered just to make explication clearer. The idea is simple: "chained" dropdown choices, where the options in the child change when the select option in the parent changes, as in the series of [State] [County] [District].
(In the actual class, the Model change is passed to the child, which decides for itself if it has changed, and adds itself to the target if it has, then passes the target to its child. I've removed most of that to make a clearer example.)
Here's the ctor, which just adds to itself an anonymous subclass of an AjaxBehavior:
public AjaxChildNotifyingDropDownChoice(...code elided for clarity...) {
this.child = child;
// Ajax won't work without this:
setOutputMarkupId(true);
//
add( new OnChangeAjaxBehavior() {
#Override
public void onUpdate(final AjaxRequestTarget target) {
// tell child to update its list
// based on newly selected value
// when the Ajax is called,
// my owning component's model
// is already updated
// note we could just type getModel()
// I'm making explicit that we're calling it
// on the enclosing class
// (which a non-static inner class has a hidden ref to)
child.setNewModelBasedOnSelectionOf(
AjaxChildNotifyingDropDownChoice.this.getModel());
// now add the child to the target
// Wicket javascript will receive the new
// options and re-render the child dropdown
target.add(child);
}
});
}
We could also have hidden or un-hidden components, or added behaviors like CSS styles, or even swapped one Panel for another. As long as for each changed component we:
1) called setOutputMarkupId(true); so that the javascript can find it, and
2) added it to the AjaxRequestTarget
Note that different types (subclases) of Ajax Behavior have different callback functions, so be sure you're overriding the right one (add an #Override annotation so the compiler can complain if you got the name wrong).
But again, the basic wicket idea is that instead of sending raw data for the client to parse and act on, you update your model and view, and tell Wicket to re-render what you've changed, by adding the chnaged components to the target.
The only reason I can think of to send straight XML would to be to feed it to non-Wicket javascript. Let me know if that's your aim, and I completely missed the point. ;)
I don't really know what Wicket is or what it does, but there is a minor bug in your code (as it appears).
This:
"var MyGridTalker = new talker(" + this.talker.getCallbackUrl();
You seem to be missing your end parens:
"var MyGridTalker = new talker(" + this.talker.getCallbackUrl() + ")";
Anyway, not a big deal, but didn't know if it was intentional.