-
Notifications
You must be signed in to change notification settings - Fork 12
Vision
Hereafter are some ideas and concepts - mainly by Erfan Ebrahimnia - which should guide the further development of the husky-framework. Most points just mention what should be improved or added and not how a possible implementation could look like. If you have further visionary ideas or some concrete implementation at hand, feel free to extend this document.
Aura-events, which are global, are at the moment the only way to establish a communication between two components. Current evils: Pollution of the event-space, parent-component has to communicate globally with its children. Global-event-handling is implemented in every component. The complexity of calling the callbacks for global events is Θ(n) where 'n' is the number of events and we are creating a lot of global events.
It'd be nice to have the possibility to listen for events of a specific component and not just register a string with sandbox.on
. This could happen with returning a 'component'-object after sandbox.start
. This 'component'-object would have methods for registering callbacks and for triggering functionality within the started component. Such a start could look something like this:
this.sandbox.start([{name: 'overlay@husky'}]).then(function(component) {
component.on('close', this.closeCallback.bind(this);
component.on('open', this.openCallback.bind(this);
}.bind(this)
this.component
could be added to every aura-component via an extension and automatically passed to the starter via a deferred. In our example the overlay-component could then just emit a component-based (local) event with this.component.emit('open')
.
Moreover the component-objects could be stored, so other components can request the component-objects. For example: Lets assume we have an overlay-component and a navigation-component on our page, which both are not parents or children of each other. The overlay-component can now call something like var navigation = this.sandbox.component.get('husky.navigation.navigationName');
. With the navigation
-object the overlay can then register callbacks for events of the navigation-component with the name "navigationName". To achieve this, components first need to have a name which could also be handled via extensions.
Although component-based-events would be a great thing, we will still need global ones. But at the moment we abuse global events for local purposes. Moreover the fact that we implement instanceName
and createEventname
is not exactly optimal. As mentioned above the functionality of instanceName
should be handled within a extension. Apart from that, global events shouldn't need an instanceName. If you encounter yourself using an instanceName for global events you should probably think about using component-based-events. Global events should be completely decoupled from the component which emitted it. An example for a global event would be 'user.permissions.changed', where probably various different components change their behavior and view completely independent of which component emitted the event.
Like with global events, we use extensions in a misleading way. Extensions should only be used to add real core functionality, because it gets added to every component. Moreover the jquery-extension should be removed, as the readability of the code suffers - this.$el.find('.container').remove();
is way more readable than this.sandbox.dom.remove(this.sandbox.dom.find('.container', this.$el));
. The argument for the jquery-extension, that we would only would have to change this one file if we wanted to switch from jQuery to e.g.prototype is not true, as we are using a lot of jQuery-plugins.
A lot of what we now use as an extension would be better when implemented as a service.
Services are singleton-classes which provide methods used by different components. An example of a service would be a class which formats text (cropping etc.). These type of functionality is now mostly implemented in the util-extension.
These services could be used by the components via require-js or even better via a service-extension (acts kind of like the dependency-injection-container in symfony2). Registering a service could look something like:
app.sandbox.service.register('text-formatter', TextFormatter);
where TextFormatter
is the constructor of the class.
Within a component you could use this service with:
var formatter = this.sandbox.services.get('text-formatter');
The service extension has to ensure that only one instance of the service-class gets created.
In most of our components we kind of use the same methods in almost the same places. We would definitely profit from more default methods. At the moment only the method initialize
gets automatically called.
Methods like render
, bindDomEvents
, bindCustomEvents
could be automatically called by the framework and also have a default implementation. For example the render method could by default look if the component has a template property specified and if so renders the template into this.$el
In addition to the methods above the framework could provide methods like preRender
, postRender
(after rendering and before placing into the DOM), postDisplay
(after placing the template into the dom).
This default methods (hooks) would lead to a better code quality, as an author of a new component gets guided through the creation by these methods.
The calls of these default methods would have to be implemented within the core of Aura.
An other way to improve code quality are state-machines. For example within the navigation-component we distinguish between "minimized" an "maximized" with a lot of if-statements. With state-machines we could handle this scenario more elegantly. In the navigation-component this could look something like this:
this.statemachine = this.sandbox.statemachine.create({
states: ['maximized', 'minimized', 'invisible'],
start: 'maximized'
});
this.statemachine.on('maximized > minimized', this.callbackFromMaxToMin.bind(this));
this.statemachine.on('> minimized', this.callbackToMin.bind(this));
this.statemachine.on('invisible >', this.callbackFromInvisible.bind(this));
In, for example, a click-handler you could the just call
this.statemachine.change('minimized');
And the corresponding callbacks get executed.
- Have all files of a component (main.js, scss, demo, etc.) in one folder
- Override jQuery's remove method and call sandbox.stop before remove a dom-element
- There is a
remove
method in Aura (gets called before a component gets destroyed) - use it! - Remove globally set dom-events