aurajs / aura Goto Github PK
View Code? Open in Web Editor NEWA scalable, event-driven JavaScript architecture for developing component-based applications.
License: MIT License
A scalable, event-driven JavaScript architecture for developing component-based applications.
License: MIT License
I have some questions and comments about backbone-aura 0.8 developer preview.
That's it for now.
Thanks for the nice work and keep it up :-)
Howdy,
Apologies if "Issues" isn't the right place for a question. If there's a better place to post, please let me know.
If two Aura widgets require same data, how should widget2 get widget1's data?
For example, let's say that the example project has a fourth "Feed" widget that displays a list of todos.
In Aura, how should the feed widget get the todos data?
Thanks!
Adam
Hi, I have a question regarding main.js from backbone-aura example, I see you use mediator to init your application and I'm wondering why you chose mediator instead of facade? I believe the normal should be to interact with mediator through facade.
Just curious to see an example with facade.publish
:)
EDIT:
I'm asking because I believe that facade.publish
method should look more like this:
facade.publish = function() {
mediator.publish.apply(mediator, arguments);
}
instead of this:
facade.publish = function(channel){
mediator.publish( channel );
}
just want to confirm it :)
EDIT2:
Of-course we could also have something like this:
facade.publish = function() {
args = [].slice.apply(arguments, [0, 2]); // first two arguments for mediator
arguments = [].slice.call(arguments, 2); // rest for facade
mediator.publish.apply(mediator, args);
}
since we expect mediator to get only two args we could pass first two args from facade and the rest will be used from facade if they needed.
In that way we will be using only Facade in our app and we won't call facade and mediator as it is now in the examples, so facade will invoke with mediator (since this is more logic as it is facade's role to invoke with private/hidden stuff).
It seems like these are identical content, shouldn't there just be modules? I noticed subscribers was also referenced in a few of the other files:
Modules.js is only referenced in the QUnit tests...
Thanks,
Matt
So I've really been having a problem with the models and collections in the aura-example. Circular dependencies, deferred hell, overly complex code, etc. It prompted me to go back and re-watch Nicholas Zakas' Scalable JavaScript Application Architecture presentation yet again. A couple of key points really stood out to me this time around:
That combined with the principle that "any single module should be able to live on its own" got me re-thinking the way aura-example is structured.
I'm wondering if our example really isn't big enough. Everything we're doing currently centers around Todos. Sounds kind of like a single module. @addyosmani, you mentioned this sometime ago, and now I think I'm coming into agreement with you. Nicholas cites weather, stocks, and more. All very different types of data which correspond to different types of modules.
If we assume that Todos are just one Aura module, what would the mediator look like? I think pretty much the same. Same with the facade except that each module could possibly have its own instance of it. So then each AMD module within the Aura module includes the sandbox to get the functionality it needs like DOM manipulation, event handling, and AJAX. There is most likely a single entry point into that Aura module which, I think, would also be the one responsible for registering the Aura module with the core (mediator).
Just a side note: I think I get confusing because of the mixed terminology. To set the record straight, I'm proposing that we use many AMD modules to construct a single Aura module. I am using sandbox/facade interchangeably. Also I am using mediator/core interchangeably.
Seems a hell of a lot simpler to implement than my current approach. It also looks a lot like ender.js, but better because it provides an abstraction layer for all of the different APIs. Thoughts?
If i have one collection that uses many widgets, how correctly share this collection.
Sample:
Task menu (Wdiget?):
Review
In Progress
Done
Task list(Widget)
#1 Task name
#2 Task name
#3 Task name
Detailed view(Widget)
When in task list i click on "#2 Task name", i must render detailed view widget.
How correctly organize this code.
Thx.
Would you mind taking a look at this, @joelhooks ? I've been going through Travis CI trying to figure out where the build broke and everything seems to point to #71 and #72 being the culprits. Reverted those changes and applied some other minor changes but it still appears broken.
http://addyosmani.com/gyazo/0a8d.png
(It may be something very minor. I've been experiencing some npm issues this morning preventing me from running builds locally so any help would be appreciated)
@addyosmani Just wanted to see what your thoughts regarding i18n are. It's not currently needed for our Aura because we're just throwing errors, but I think it would be great to expose a single nls file through the mediator/facade.
@dustinboston when you get a chance would be great to get this idea expanded on :)
Hello,
i want to use some of this library in another project, but i need to make sure the license agreement is in alignment. What is the software license you are using to cover this project ?
Cheers,
David
'Nuf said.
One question we regularly receive is how Aura differs from extension libraries like Marionette, Lumbar, Chaplin and so on. I wonder if we should perhaps try to make this more clear in our docs.
Update HTML/CSS to use Bootstrap. @addyosmani Shall we integrate LESS and use the build step to generate the .css files? Or do you just want to stick with the pre-baked versions from le Twitter?
We currently have an outstanding TODO in mediator.js to switch over to the new error callbacks (errbacks). Below is a sample from the RequireJS docs on how this would work.
Notice that the errbacks are used from within your config and seem to best work where you know explicitly where you want to define a fallback for locally loading something, unloading the module you previously tried loading if it failed for whatever reason.
requirejs.config({
enforceDefine: true,
paths: {
jquery: 'http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min'
}
});
//Later
require(['jquery'], function ($) {
//Do something with $ here
}, function (err) {
//The errback, error callback
//The error has a list of modules that failed
var failedId = err.requireModules && err.requireModules[0],
if (failedId === 'jquery') {
//undef is function only on the global requirejs object.
//Use it to clear internal knowledge of jQuery. Any modules
//that were dependent on jQuery and in the middle of loading
//will not be loaded yet, they will wait until a valid jQuery
//does load.
requirejs.undef(failedId);
//Set the path to jQuery to local path
requirejs.config({
paths: {
jquery: 'local/jquery'
}
});
//Try again. Note that the above require callback
//with the "Do something with $ here" comment will
//be called if this new attempt to load jQuery succeeds.
require(['jquery'], function () {});
} else {
//Some other error. Maybe show message to the user.
}
});
Here is our current custom variation of this:
require.onError = function (err) {
if (err.requireType === 'timeout') {
console.warn('Could not load module ' + err.requireModules);
} else {
// If a timeout hasn't occurred and there was another module
// related error, unload the module then throw an error
var failedId = err.requireModules && err.requireModules[0];
require.undef(failedId);
throw err;
}
};
which simply unloads a module if it fails.
What benefits do we gain from switching to the proper form of errbacks? Its clearer to the user how to specify local fallbacks? Keep in mind that such fallbacks will have to be defined by widget authors.
//cc @dustinboston
Hi Addy,
First thanks for sharing this, nice work.
Just wondering, on line 130 of the aura/mediator.js file you are using a variable named 'key' without declaring it:
obj.unload = function(channel){
var contextMap = requirejs.s.contexts._.urlMap;
for (key in contextMap) {
if (contextMap.hasOwnProperty(key) && key.indexOf(channel) !== -1) {
require.undef(key);
}
}
};
Shouldn't that be ?
obj.unload = function(channel){
var contextMap = requirejs.s.contexts._.urlMap, key;
for (key in contextMap) {
if (contextMap.hasOwnProperty(key) && key.indexOf(channel) !== -1) {
require.undef(key);
}
}
};
Cheers
when the Todos is not empty, clicking "Start Todos" causes same data to be rendered .
version: Backbone Aura 0.8 Developer Preview
steps to repeat the problem:
Hi, Addy!
I tried to check if it works in Opera 11.60.. and it seems that it doesn't:-( Do you know why?
is there a good reason to use the modified source files (backbone,underscore) instead of the shim
feature of requirejs2.0?
this makes it harder for anyone to use a different version of backbone (especially master)
Coding conventions discussion based off #22, #81, #82. Making a stub for this.
We should make a wiki page to document decisions we have made throughout previous issues.
So far this includes:
var
's in variable declarationsSome things to be discussed:
'use strict';
vs "use strict";
per @sindresorhus on #82. 'use strict';
is valid and single quotes are clearer (see http://ecma262-5.com/ELS5_HTML.htm#Section_14.1).As title says... why aura is so tight bound with RequireJS and jQuery and Underscore?
For example, the pup/sub technique that mediator uses shouldn't be bound so tight with RequireJS module autoloading and DOM selection as it is now, of course these are very useful, but only for those who use jquery + requirejs but thing is that not everyone uses (at least the second...).
I prefer to keep the following as the very basic example of mediator/facade pattern with permissions intermediate:
//==== Mediator Pattern
var channels = {};
var mediator = {};
mediator.subscribe = function (channel, subscription) {
if (!channels[channel]) channels[channel] = [];
channels[channel].push(subscription);
};
mediator.publish = function (channel) {
if (!channels[channel]) return;
var args = [].slice.call(arguments, 1);
for (var i = 0, l = channels[channel].length; i < l; i++) {
channels[channel][i].apply(this, args);
}
};
//=== Permissions
var permissions = {
endContentEditing:{
todoSaver:true,
testing : true
}
};
permissions.validate = function(subscriber, channel){
var test = permissions[channel][subscriber];
return test===undefined? false: test;
};
//=== Facade Pattern
var facade = facade || {};
facade.subscribe = function(subscriber, channel, callback){
if(permissions.validate(subscriber, channel)){
mediator.subscribe( channel, callback );
}
}
facade.publish = function() {
args = [].slice.apply(arguments, [0, 2]); // first two arguments for mediator
arguments = [].slice.call(arguments, 2); // Rest for facade
mediator.publish.apply(mediator, args);
}
//=== Application Use Case
facade.subscribe('todoSaver','endContentEditing', function (context) {
try {
console.loger('todoSaver');
} catch (e) {
console.log('todoSaver f**** up!');
}
});
facade.subscribe('testing','endContentEditing', function (context) {
console.log('testing');
});
facade.publish('endContentEditing', {
test : 'test!'
});
Hi, was passing by and saw oppurtunity to make a pull, but wanted to step back and get a take on this.
Since we're using grunt configuration for handling jslint, we're set to allow for require
, define
, etc.
As a matter of consistency moving forward, should we include jslint comments at the top of backbone-aura files? It's missing on some, included on others, and outdated in other places.
Best
T
Hi there!
I was wondering about the publish function in mediator component.
This function iterates over the event's subsribers callbacks and executes them in sequence. My point is, if there are several subscribers for a single event and the first one's callback raises an error, I guess the rest of them won't be executed.
Is this so or am I missing something?
Thanks a lot for this brilliant work.
Hi,
I post here my question because I think your answers will surely help someone else one day. It is a basic workflow which could be taken as an example for beginners I guess. Let's expose my question.
Say, I've got a list of cars on my page. And when I click on a car, I see the information about the car displayed in an other part of my page and I can edit this information. Then, imagine I changed the name, so I want the row corresponding to the car being updated to display its new name.
First thought, it's easy when I update a model his own view is re-rendered! Issuer: Which architecture for this example... What I thought first was to develop two separate widgets:
The first widget listen on the click and publish a message to the second one by passing in parameters the model "Car". But, here is the problem, the second widget doesn't know the model "Car" because it is defined in the first widget. And require this model by passing a weird reference such as "../my-first-widget/models/car" isn't good, because each widget should be able to live by his own.
I don't know which good architecture I should set up in this case:
1 - Set the whole first widget as a dependence of the second one?
2 - Make two widgets having both a model Car but the first one having (for instance), the name, and the second one, the name, the year, the constructor, the owner name. And when a car is edited, tell the first widget to refresh its whole list of cars.
3 - Make only one widget, and the second widget should only be another view of Car that I should display on my page when a car is clicked.
What are your advices?
ps: I really agree with @addyosmani and @dustinboston to say that the Todo example is too simple. It is good too understand the links between the scripts and the communication intra-widget, but not enough complex to being able to set up an architecture for a big project.
We've discussed for a while the idea of renaming the project to Aura, eventually moving the Backbone-Aura extension part of it to a separate repo or just leaving it in the repo as a plugin of some sort.
As GitHub don't promise to handle redirects, renaming this repo would mean breaking links to articles pointing at backbone-aura, but otherwise should be okay. Anyone against this change?
Hi. I have just downloaded aura and tried the new version in Google Chrome (it works in IE 9). Whenever I hit Eneter to create new todo item, I'm getting an error: Uncaught TypeError: object is not a function.
Here's the stack:
Uncaught TypeError: object is not a function
c.extend._prepareModelbackbone-optamd3-min.js:224
c.extend.createbackbone-optamd3-min.js:206
facade.subscribe.donemodules.js:75
obj.publishmediator.js:18
Backbone.View.extend.createOnEnterapp.js:72
f.event.dispatchjquery-min.js:1183
f.event.add.h.handle.ijquery-min.js:1103
Thank you.
Here are 3 annotated sources generated by http://jashkenas.github.com/docco/.
Here is a blog post on it by Derick Baley, http://lostechies.com/derickbailey/2011/12/14/annotated-source-code-as-documentation-with-docco/.
TypeError: rules[channel] is undefined
In https://github.com/addyosmani/backbone-aura/blob/master/src/aura/permissions.js#L26
var test = rules[channel][subscriber];
It happens when widget name is not added to permissions config and widget tries to subscribe to any event using sandbox.subscribe method. Of course we can add empty object just to avoid issue. In my opinion this could be simplified by returning false when there is no channel declaration in permissions config.
Hi,
I just tested out Aura and I really like it. One of my minor concerns is that requirejs optimizer simply compressed all the js files instead of generating one single js file.
Is this intended or is it going to be added in the near future?
One way to make jquery more easily replaceable, is to reference jquery
with a more generic name, like dom
or something similar.
This is the approach I'm currently using in my app, to be able to switch between jquery and zepto more easily.
First the config can be changed to something like this:
require.config({
paths: {
dom: 'aura/lib/jquery'
}
});
You could then replace:
define(['jquery', 'underscore'], function ($, _) {
// ...
});
With this:
define(['dom', 'underscore'], function ($, _) {
// ...
});
Of course, this is a compile-time step, thus it's not adding the ability to change the dom-library in the runtime, which would be another feature.
Just thought I'd share the idea.
From the discussion with @dustinboston
From the last catch-up @dustinboston and I had about the project:
I'd like to integrate first class support for offline into Aura, something similar to:
https://github.com/Ask11/backbone.offline
At present, whilst offline isn't strictly a part of the originally planned architecture I strongly feel that there are advantages to giving developers the ability to use this out of the box.
To summarize: the way this works is that when a connection is no longer available we start syncing using local datastores (e.g localStorage), re-syncing with a server when a connection comes back.
What would be the best way to package a large aura project with grunt?
Any input is appreciated :-)
Hey @addyosmani!
I got some errors while trying to build aura, not sure if everyone is going through the exact same, that's why I'm not trying to patch anything.
First error was on the lint task (on events.js, app.js and todos.js). But that's just a matter of minor adjustments in the files, so I didn't worry about it โ just removed the lint task from the grunt build and ran it again. Than I got:
Error: Cannot find module 'jsdom'
So I ran npm install jsdom
in the same folder and ran grunt build
again. Than I got:
Error: Cannot find module 'xmlhttprequest'
So I ran npm install xmlhttprequest
in the same folder and ran grunt build
again. Than I got:
RequireJS optimizer started
node.js:201
throw e; // process.nextTick error, or 'error' event on first tick
^
Error: Error: ENOENT, no such file or directory '/Users/gerson/Dropbox/Sites/backbone-aura/demo-build/js/ext/mediator.js' In module tree:
The app seem to be working just fine, but I don't understand this error. I wish I could help better and give a solution, but it will take me a while until I wrap my mind around all the RequireJS thing...
I'm not sure if you are aware of that:
If you want so switch the base lib, the interfaces of the sandbox (dom, utils, events, etc.) should be abstracted. I saw some cases where you referenced to the mediator which referenced the base lib's function directly (utils.extend = $.extend, etc.). Like that your widget code might not work if you switch the base library. But this can be avoided using proper abstraction in the sandbox.
Is this enough for disposing a widget?
// Remove all modules under a widget path (e.g widgets/todos)
obj.unload("widgets/" + file);
// Empty markup associated with the module
$(el).html('');
what about unbinding events? Why is better to use
I've seen other frameworks, such as Chaplin or Marionette, having more sophisticated object disposal techniques.
Maybe I'm not understanding the full framework workflow here.
Esteban.
Previous discussion centered around using something like http://documentup.com/ for documentation.
The major confusing point to me is between module communication. My previous understanding was - there are modules, that are absolutely independent each of other. The only one way to notify about something is to publish an event. They can subscribe to / publish on through mediator object.
Say, I have 2 modules - chat and message hub. Chat is basically backbone view, that is reposible for rendering messages. Message hub is module responsible for receiving and sending events.
Previously, I thought that mediator is responsible for providing functionality of Pub / Sub in aura architecture. With the latest changes, I can see it's not.
Some very simplified code,
// Chat
define(['core'], function (core) {
core.subscribe('chat', function () {
// create view here etc..
// somewhere inside the view..
core.subscribe('message/recieved', function () { });
});
});
// Message Hub
define(['core'], function (core) {
core.subscribe('hub', function () {
// somewhere is message recieve handler
core.publish('message/recieved', { messsage: data } );
});
});
But now, Mediator (or it's wrapper Facade) publish and subscribe methods has a different meaning. Publish is starting up widget and call it's subsriber function. The code like core.publish('message/recieved' ... ) will fail, since now it is expected 'widgets/message/recieved.js' module is expected. I think publish/subscribe loosing it's previous meaning.. now it's more like start / onStarted methods, as for me.
In my opinion aura now have a drawback that it does not demonstrate inter module communication which we can see in real life.
Hi,
I think it would be very nice if we can see what module publish/listens what events. Since Aura uses facade in between module and mediator, I think it is possible to make this happen.
I know that we can simply open each js files then check source codes, but it's just painful, especially working in a team environment with a lot of modules.
I've been using my own mediator in the following way.
Mediator.getOwnerOf("event name"); // returns owner of the event
Mediator.getListenersOf("event name") // returns listeners (modules) who listerns $eventanem
Mediator.getEventMapOf("module") // returns what $module listens and publishes.
Is there a plan for such feature?
I don't know if this is an issue with aura, requirejs, or chrome - but, it appears that scripts do not delete when you stop a widget. Worse yet, when you start the widget the script is duplicated. This duplication doesn't seem to show in the "Network" section in Chrome's Developer Tools, but it does show in "Scripts." Also, if you stop/start each widget multiple times while in the "Timeline" section (with record on), you'll see the memory climb.
For reference, memory seems to be at about 10mb after everything loads, but after stopping and starting each widget about 10 times the memory goes to about 30mb.
I'm not totally sure what it all means, but in the "Timeline" section, with the "Memory" option active, it appears that "listener" events fluctuates appropriately when stopping/starting widgets, but "nodes" climb each time a widget is started. I'd guess that means that event listeners are being removed correctly, but it seems like DOM elements aren't removed (aren't scripts DOM elements?). Makes me think that require.undef either doesn't work right or it's not be fired?
I checked and require.undef(key)
is running when it should, but maybe the key
is wrong? Or require.undef()
just doesn't work like I would expect it to (I haven't it tested it on any of my projects, yet). I'll play with requirejs a bit and see if I can figure anything out.
I think it would be useful to discuss what should land in V1. We of course need docs and some more complete tests, but I'd be interested in hearing whether there are more features Aura should come with.
Some ideas:
Any other ideas or comments on the above?
@dustinboston @rudolfrck @gersongoulart
Covers Core, Sandbox and Permissions. There will be some tricky plumbing required to get this working but it should be possible.
Is there a facade.extend function example available that alters variables inside the module?
I'm not entirely sure if it is possible, but in my opinion you could respond to a channel inside a module with the facade.extend right?
I would love to start/stop/filter the module with this facade.extend but I can't seem to get this to work. A little code example would be dearly appreciated!
(im working with the new-version -- version)
I think we should rename the aura files mediator.js
to core.js
and facade.js
to sandbox.js
as well as the main objects returned within these files (just like @addyosmani was going to do inside the single-file version). The reason is that, although it's important to know and understand which Js patters are being used (and that should still be explicit through the comments within the files), auras files (and objects returned) should be named after the functionality they're providing to the framework, not the technic adopted.
It is really nice to have sub-dir READMEs in a project. When browsing on Github, it makes for an excellent supplemental narrative documentation. Aura is already aiming to be documentation heavy, but it is also appropriate to heavily document a project like this for educational purposes.
@dustinboston Do you think we should replace the existing calendar with something without a jQuery UI dependency or should it remain as a demonstration of how to pull in UI components?
e.g http://www.web-delicious.com/jquery-plugins/#calendar (http://www.web-delicious.com/jquery-plugins-demo/wdCalendar/sample.php)
The mediator has two sets of functions that do a completely different thing:
I don't think those two should have anything in common.
The way it is today, when you publish
something, say 'todos' in the example application, the application will load the todos widget. Also, sandbox.publish('todos', '#todosapp');
has the same effect as sandbox.widgets.start('todos', '#todosapp');
.
I tried to make the calendar widget publish an event to say to everyone that a new event was created and make the todo widget add a new todo when this happens and made it only by changing a lot of stuff. You can see it on my branch here. There is still one thing that is not working: if you stop a widget, all its callbacks for the events should be cleared.
I am not sure I am doing the right think here but I was unable to find a way for modules to communicate (publish events and others to receive that event).
Hi -
Just wanted to let you know that the jquery plugin is not in the git repo. Thanks for all the hard work on the tutorials and samples.
-Matt
Should the aura mediator.stop delete subscribers? - if you .start / .stop / .start the same widget it will have 2 duplicate subscribers.
obj.stop = function(channel){
var args = [].slice.call(arguments, 1),
el = args[0],
file = obj.util.decamelize(channel);
// Remove all modules under a widget path (e.g widgets/todos)
obj.unload("widgets/" + file);
// Empty markup associated with the module
$(el).html('');
};
Shouldn't all subscribers be cleared aswell?
delete channels[channel]
maybe an prettier obj.unsubscribe fn instead :-)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.