Extending Marionette.Application with module definitions - backbone.js

I've been writing an application of mine using Marionette, but I feel a bit confused that application extensions are failing.
There seems to be no way to do this sort of thing in MarionetteJS:
var SuperApplication = Backbone.Marionette.Application.extend({
'modules': {
'foo': function (module, app) {
// module definition
},
'bar': function (module, app) {
// module definition
}
}
});
var app = new SuperApplication();
While I have been developing, it has become important to occasionally create a new Application instance and call .module numerous times before the application object becomes useful. It negatively affects testing reliability of each Application object created, because there are no guarantees that they are of the same prototype.
The following method, ugly as it is, cannot properly attach a module definition to an Application as a prototype:
SecondApplication = Backbone.Marionette.Application.extend({
'foo': function () {
var args = slice(arguments);
args.unshift(this);
args.push(function (module, app) {
// module definition
});
return Backbone.Marionette.Module.create.apply(
Backbone.Marionette.Module, args);
}
});
Am I supposed to do this some other way?

EDIT: Marionette modules are now deprecated and will be removed in the next major release
Use ES6/CommonJS/AMD modules instead. Capability sharing can be accomplished in vanilla JavaScript using prototypical inheritance or composition.
Original answer (Marionette pre 2.0)
You're right, you can't create modules like that; you have to use the App.module() pattern.
It's unusual that you're creating multiple application instances. Your typical setup has a single Application instance with Modules and subModules used to partition the site into logical areas, like so:
var MyApp = new Marionette.Application();
MyApp.module('forums', function(mod, app) {});
MyApp.module('gallery', function(mod, app) {});
MyApp.module('gallery.kitchen', function(mod, app) {});
MyApp.module('gallery.diningroom', function(mod, app) {});
MyApp.start();
If you want numerous modules to have similar capability (or to have the same initial definition), the module method can be called multiple times with the same module name to change the functionality. This can be used to create a base module of sorts that can be extended:
var MyApp = new Marionette.Application();
// Create your shared definition:
var ModuleDefaults = { definition: function(mod, app) {
mod.type = 'gallery';
mod.addInitializer(function() { /* do work */ });
}};
// create your modules (quickly!)
_.each(['forums', 'gallery', 'gallery.kitchen', 'gallery.master'],
function(moduleName) {
MyApp.module(moduleName, ModuleDefaults.definition);
});
// override whatever your want:
MyApp.module('forums', function(mod, app) {
mod.type = 'forum';
mod.addFinalizer(function() { /* take out the papers and the trash */ });
});
MyApp.start();

Related

Why anglar.mock.module does not $provide values like it $provides constants

I've looked at the documentation for angular.mock.module and a couple of examples of others using it but I seem to be running into an issue in my use-case that I don't understand.
I'm running Jasmine (2.4.1) tests with angular (1.4.9) and I have my angular app separated into multiple modules. When I attempt to mock out certain parts of my app for unit testing I want to mock out entire modules (or providers) so that I only expose the pieces I use.
Here is a very simple app that has a main module plunker which depends on plunker.service. plunker.service depends on plunker.constant.
var app = angular.module('plunker', ['plunker.service']);
app.controller('MainCtrl', function($scope, valueService, appService) {
$scope.init = function() {
$scope.appValue = valueService.getValue();
$scope.appIsRunning = appService.getStatus();
};
});
angular.module('plunker.service', ['plunker.constant'])
.service('appService', function(appSettings) {
var vm = this;
vm.getStatus = function () {
if (appSettings.isRunning) {
return true;
} else {
return false;
}
};
})
.service('valueService', function(valueSettings) {
var vm = this;
vm.getValue = function () {
return valueSettings.value;
}
});
angular.module('plunker.constant', [])
.constant('appSettings', { isRunning: true })
.constant('valueSettings', { value: 10 });
In my Jasmine tests I have a beforeEach() that registers my modules using module (aka angular.mock.module).
I have seen 3 ways of using module
string
function with $provide
object
You can see below that I use the module('plunker') (string) to register my main module and I have 3 ways of mocking out my appSettings constant (A, B, C). You will notice that the function with $provide.constant works fine but function with $provide.value does not and object does not.
beforeEach(function() {
module('plunker');
function useFunction(typeofProvider) {
module(function($provide) {
$provide[typeofProvider]('appSettings', { isRunning: false });
});
}
function useObject() {
module({
appSettings: { isRunning: false }
});
}
// A. THIS WORKS! //
useFunction('constant');
// B. THIS DOES NOT //
// useFunction('value');
// C. THIS ALSO DOES NOT!! //
// useObject();
inject(function($rootScope, $controller) {
$scope = $rootScope.$new();
ctrl = $controller('MainCtrl', {
$scope: $scope
});
});
});
I have also seen people use the following syntax...
beforeEach(function() {
var mockService = function () {
var mockValue = 10;
this.value = mockValue;
};
// D.
module('a.module.name', function newProviders($provide){
$provide.service('realService', mockService);
});
});
My questions
In my test code, why does A. work but B. and C. do not?
Is D. equivalent to calling module('a.module.name'); followed by module(function newProviders($provide) { ... });? Does placing both in the same module() call have any special effects on how things are registered or is it just a shorthand? (based on the documentation it should be a shorthand)
Related to Jasmine, specifically, do all beforeEach() calls run in the same top-to-bottom order with every execution?
Here is my plunker for the above app and jasmine code
Thanks
This happens because of how Angular injector works. In fact, there are two different injectors in Angular. The one (available as $injector in config blocks) deals with service providers. Another one (available as $injector anywhere else) deals with service instances. Providers and instances are cached and stored internally.
$provide.constant('service') creates both provider and instance of name 'service' at call time.
All other types of services are lazily instantiated. They create 'serviceProvider' provider at call time, but 'service' instance is created on the first injection.
Since Angular service instance is a singleton, it refers to instance cache before the instantiation. If the instance is in the cache, it is reused and not instantiated. constant service instance is eagerly instantiated, so only another constant can override the instance.
Object properties in angular.mock.module are shortcuts for $provide.value, and useObject() equals to useFunction('value') in this example.
As long as module order stays the same,
module('a.module.name', function ($provide) { ... });
is indeed a shortcut for
module('a.module.name');
module(function ($provide) { ... });
Due to the fact that appSettings object isn't used in config blocks (the primary use of constant service), it is more convenient to make it value.

Can service and factory be used interchangeably?

Angular JS conceptual overview. View-independent business logic: Services (heading).
The description says - moving view-independent logic from the controller into a service, yet the code says factory. What am I missing here?
angular.module('finance2', [])
.factory('currencyConverter', function() {
var currencies = ['USD', 'EUR', 'CNY'];
Link to the resource
The factory method ('recipe') is a way of creating a 'Service'.
You can also create 'Services' with the service, constant, value, and provider recipes ('methods').
However you do it, you'll end up instantiating an object that is conceptually a 'Service'.
It's been acknowledged widely that this is a confusing aspect of Angular. See this classic Stackoverlow question.
The developer guide does a good job of clarifying these concepts too:
Each web application you build is composed of objects that collaborate to get stuff done. These objects need to be instantiated and wired together for the app to work. In Angular apps most of these objects are instantiated and wired together automatically by the injector service.
The injector creates two types of objects, services and specialized objects.
Services are objects whose API is defined by the developer writing the service.
Specialized objects conform to a specific Angular framework API. These objects are one of controllers, directives, filters or animations.
The injector needs to know how to create these objects. You tell it by registering a "recipe" for creating your object with the injector. There are five recipe types.
The most verbose, but also the most comprehensive one is a Provider recipe. The remaining four recipe types — Value, Factory, Service and Constant — are just syntactic sugar on top of a provider recipe.
Coming from a Java background, I really like the concept of angular factories; we get to mimic POJOs here to an extent. I get to attach meaningful methods and execute logic that's all self-contained within the model itself. Whereas for services, I tend to treat those as I'd treat a service on the server-side, simply for fetching data.
For instance, if we were building a Twitter clone of some sort, for the tweet stream, I'd have a TweetSteamFactory that internally fetches data using TweetService to get the latest tweets. Maybe my factory has a getNextPage() method, which is bound to an ngClick somewhere - when fired, it of course makes its call with TweetService.
At any rate, I do see a pretty clear distinction between services and factories, although my understanding could be misguided.
http://plnkr.co/edit/SqPf212nE5GrSPcZdo5K
Controller
app.controller('MyController', function(FoobarFactory) {
FoobarFactory()
done(function(factory) {
var factory = factory;
$scope.data = factory.getData();
$scope.baz = factory.getBaz();
})
});
Factory
app.factory('FoobarFactory', ['MyService', function(MyService) {
function Foobar() {}; // Empty constructor
angular.extend(Foobar.prototype, {
_init: function() {
var deferred = $.Deferred();
var foobar = this;
this.baz = true;
this.data = undefined;
MyService.getData()
.done(function(data) {
foobar.data = data;
deferred.resolve(foobar);
})
deferred.resolve();
return deferred.promise();
},
getBaz: function() {
return this.baz;
},
getData: function() {
return this.data;
}
});
return function () {
var deferred = $.Deferred();
var foobar = new Foobar();
foobar._init()
.done(function() {
deferred.resolve(foobar);
})
.fail(function(error) {
deferred.reject(error);
});
return deferred.promise();
};
}]);

Factory methods in multiple files Angular JS

What is the best practice when you have a Factory with like 4 related methods, each of those is really long (200+ lines of code) and you want to avoid having a huge file of 800+ lines of code?
One solution is to create 4 factories under the same module , each one exposing a single method and in its own file. Then inject all of them in the controller that requires them.
Is there a better solution? I'd like to create the Factory once, then add methods to it like I was doing module augmentation using the module pattern. Then I just need to inject the Factory once and have all its methods available.
I'd like to create the Factory once, then add methods to it like I was doing module augmentation using the module pattern. Then I just need to inject the Factory once and have all its methods available.
Yes, that will work:
// In your main module file.
angular.module('myModule', []);
// file1.js
angular.module('myModule').factory('BigService1', function(){
// Your actual implementation here.
console.log('I am BigService1');
});
// file2.js
angular.module('myModule').factory('BigService2', function(){
// Your actual implementation here.
console.log('I am BigService2');
});
// ... additional files
// Separate file/service to aggregate your big factory services.
angular.module('myModule').service('AggregateService', [
// Add dependencies
'BigService1','BigService2',
function(BigService1, BigService2){
// Return an object that exposes the factory interfaces.
return {
service1: BigService1,
service2: BigService2
};
}]);
You could also arrange your code the old vanilla js style and then access those libraries in your services like this:
var Mirko = { };
Mirko.FunService = {
getAllSomething : function (p1, p2) {
},
...
};
angular.module('myModule').factory('BigService', function(){
return {
methodOne : Mirko.getAllSomething,
...
};
});
You will end up with one object Mirko that you can access outside the scope of your angular app, but it will in no way differ from other externals api's (not written for angular) you would want to use in your app. The way you handle your own 'external' api can be done the oldschool fashion way, one file per 'class' e.g. 'FunService'.
It might not be the prettiest solution but it will be an easy abstraction.
Just saying...
Maybe segment your methods through other factories, which can be injected to your "main" factory :
// file 1
angular.module('foo').factory('segment1', function () {
return {
method: function () {
// ... super long method
}
};
});
// file 2
angular.module('foo').factory('segment2', function () {
return {
method: function () {
// ... super long method
}
};
});
// your main factory file
angular.module('foo').factory('myFactory', function (segment1, segment2) {
return {
method1: segment1.method,
method2: segment2.method
};
}

How to handle multiple JS libraries with different loading times in Angular?

I am just learning Angular and I have some questions regarding the architecture of my app.
The project I will be working on will be using allot of external libraries: jQuery, jQuery.ui, jsPlumb and so on, with different loading times.
I know that each code related to these libraries will have to be handled inside directives.
I worked with Backbone which uses Require JS effectively - on each view, I could set what libraries I need and the view would be loaded as soon as those libraries are available.
Now, on angular side - what would be the correct way of handling this issue?
From the top of my head, I am thinking of:
Place checks inside the router - checking if the desired libraries for a certain route are loaded.
Place checks inside each directive - for example if one directive uses jsPlumb, place a check inside and return the directives content when that library is loaded - I believe this could generate problems when interacting with other directives on the same view, which require multiple libraries with different loading times.
Load angular only after every other library is loaded - that would lead to long loading times.
What's the best way to handle all those issues?
You can create a factory to load the external library you need. Return a deferred object for the library's script after it loads. Here is one I used for d3 library:
var d3js = angular.module('d3', []);
d3js.factory('d3Service', ['$document', '$q', '$rootScope', '$window',
function($document, $q, $rootScope, $window) {
var d = $q.defer();
function onScriptLoad() {
// Load client in the browser
$rootScope.$apply(function() { d.resolve($window.d3); });
}
// Create a script tag with d3 as the source
// and call our onScriptLoad callback when it
// has been loaded
var scriptTag = $document[0].createElement('script');
scriptTag.type = 'text/javascript';
scriptTag.async = true;
scriptTag.src = 'lib/d3.v3.js';
scriptTag.onreadystatechange = function () {
if (this.readyState == 'complete') onScriptLoad();
}
scriptTag.onload = onScriptLoad;
var s = $document[0].getElementsByTagName('body')[0];
s.appendChild(scriptTag);
return {
d3: function() { return d.promise; }
};
}]);
then in your directive, use then function of the deferred to wait until it's ready
d3Service.d3().then(function(d3) {
// place code using d3 library here
}
If your directive is needing access to multiple libraries, you can chain the deferreds.
d3Service.d3().then(function(d3) {
someOtherLibSvc.getLib().then(function(lib){
// place code using d3 library and someOtherLibrary here
}
}
To avoid this chaining check out bluebird and use Promise.join, Angular comes with $q automatically so I just used that here.
Note: if you just load JQuery before angular, then angular.element will already reference JQuery. So you don't need to do this for JQuery, just load it in your main html page before Angular
In short go
http://slides.com/thomasburleson/using-requirejs-with-angularjs#/
route.
However now suffer having to put the defines and requires everywhere. For a large application this may get messy if not planned carefully. Use grunt requirejs plugin for builds because without this things would be wash.
I'm not sure this is the "correct" way to do this, but here is perhaps a simpler way to handle external libraries (in this case d3.js) within Angular 1.x code.
This is the same basic idea as #aarosil's answer (use a factory), but using fewer dependencies on other services - for what that's worth.
var app = angular.module('SomeApp', []);
app.factory('LibraryFactory', function () {
var factory = {};
factory.getD3 = function(callback) {
if(!window.d3) {
var script = document.createElement("script");
script.src = "https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.9/d3.min.js";
document.head.appendChild(script);
script.onload = function() {
callback(window.d3);
};
} else {
callback(window.d3);
}
};
//factory.getSomeOtherLibrary = ...ditto
return factory;
});
And to use the factory (eg, in a controller):
app.controller('SomeTrialWithD3Ctrl', function ($scope, LibraryFactory) {
LibraryFactory.getD3(function main(d3) {
// place code using d3 library here
});
});
And of course, the callbacks can be chained if you need multiple libraries at once.

How to organize my JavaScript MVC code into AMD modules?

I'm building a web application with various visualization components, made up of Backbone.js Models and Views:
The "PopulationVisualization component" might for example have:
a main model - storing the state of the component
several Backbone views (timesliderView, legendView etc.) - listening to changes on the model
All of these components depend on external dataManagers and dataSource objects but otherwise they are supposed to be decoupled.
On a given page, I'd like to instantiate an instance of the PopulationVisualization component. I'd also like to listen for changes in the main model of that component so that I could serialize its state in the URL.
1) What would this look like if I tried adopting the AMD module pattern?
2) Would I make one module of the PopulationVisualization component or several?
3) Would I expose module-level methods as an API or would I provide direct manipulation of the inner Models and Views?
Thanks.
To answer you questions, here's my advice, answering all three:
Modules should be as small as possible, so I would create a new module for each view, one for the module, and one for the serialization logic. Then I would create one module that ties all this together so that outside code doesn't have to deal with models, views, or serialization.
Here is my first stab at something like this:
// components/populationVisualization/Model.js
define(function (require, exports, module) {
return Backbone.Model.extend({ /* ... */});
});
// components/populationVisualization/views/Timeslider.js
define(function (require, exports, module) {
return Backbone.View.extend({ /* ... */});
});
// components/populationVisualization/views/Legend.js
define(function (require, exports, module) {
return Backbone.View.extend({ /* ... */});
});
// components/populationVisualization/serializer.js
define(function (require, exports, module) {
exports.setupSerialization = function (model) {
// ...
};
});
// components/populationVisualization.js
define(function (require, exports, module) {
var Model = require("./populationVisualization/Model");
var TimesliderView = require("./populationVisualization/views/Timeslider");
var LegendView = require("./populationVisualization/views/Legend");
var serializer = require("./populationVisualization/serializer");
exports.createAndRender = function (modelParams, $el) {
var model = new Model(modelParams);
serializer.setupSerialization(model);
var timesliderView = new TimesliderView({ model: model });
var legendView = new LegendView({ model: model });
$el.append(timesliderView.el);
$el.append(legendView.el);
};
});
Elsewhere in the app you would only require("components/populationVisualization") and call that module's createAndRender method with the appropriate parameters.

Resources