I am using Backbone/RequireJS to provide my application with modularization and structure. One thing I am coming up against, and would greatly appreciate some advice in this area.
When a user visits the page, the first thing that happens is some JSON that populates a couple of models. I would like these models to be available where-ever I am in the app, as they contain the data and support for the program. Is it permissible to use window.modelName, or do you recommend another/better way of accomplishing this?
Using your suggested solution defies the whole purpose of using AMD in the first place.
Define a module (let's call it globals) as such:
define(function (require) {
var globals = function () {
return {};
};
return globals();
});
Now, when you init you can add values to it:
globals = require('globals');
globals.mymodel = new MyModel();
mymodel.fetch();
Later, and from any other module, you can access your globals module:
globals = require('globals');
console.log(globals.mymodel.get('myattr');
Related
I have a singleton that creates classes for me. Each class is in a separate file.
// singleton.js
angular.module('mymodule')
.service('singleton', SingletonClass)
// someclass1.js, someclass2.js, ...
// Multiple files with same layout. Each web page has different # of these files.
// Therefore, it's hard to inject them statically
// (ie, via aggregator service/factory).
angular.module('mymodule')
.??('someClass', function(singleton) {
classOptions = {}; // details here
// creates a class with options and adds it to `singleton`s cache
singleton.addClass(classOptions);
})
// yet another file
angular.module('mymodule')
.controller('myController', function(singleton) {
singleton.getClasses(); // gets all classes added by #addClass method
});
Basically, I want each individual "class" to be self-contained in its own file similar to how modules are self-contained. That means I don't want want singleton or any aggregator service/factory to know of the existence of these classes in its creation.
Edit:
I'd like to clarify: I'm using singleton instead of individually injecting the created classes because I don't yet know which classes will be created yet. The web page will dynamically load certain js files. Each file will inject the singleton object and use the addClass method to create a new class and store it inside the singleton object itself. The controllers using singleton.getClasses() won't know how many or which classes it's getting, but they have a uniform interface which allows it to use them in a predictable fashion.
You could try setting up providers for each class (per my comment above), though that seems . . . time consuming. Maybe you just don't need it? Make each class a separate file in a closure (like you use to need to do with jquery), and grab the singleton object from the injector:
// file 1 - Add other files for other classes.
;(function(window, $) {
$(function() {
var classOptions = {};
$('body').injector().get('singleton').addClass(classOptions);
});
})(window, jQuery);
This is not exactly the angular way, and angular purists will call for your head, but I think it will achieve what you're after if you don't want to mess with providers.
Using ngAudio, I cannot seem to get my code to work when I load an ngAudioObject within the controller. I am able to use the directives to work with my wav file directly in the view (so I know I have linked everything correctly) but not in the controller. I'm working with a number of modules and wondering if there is a conflict...
Please let me know if there is something obviously wrong.
var myApp = angular.module('screenApp', ['firebase','ngAudio']);
myApp.controller('screenController', ['$scope','$http','$firebaseArray','$interval', function($scope,$http,$firebaseArray,$interval,ngAudio) {
$scope.audio = ngAudio.load('sounds/dingding.wav');
...
See: https://docs.angularjs.org/guide/di#inline-array-annotation
When using this type of annotation, take care to keep the annotation array in sync with the parameters in the function declaration.
Your parameters and annotation array are out of sync. Add 'ngAudio' to the annotation array.
myApp.controller('screenController', ['$scope','$http','$firebaseArray','$interval', 'ngAudio', function($scope,$http,$firebaseArray,$interval,ngAudio) {
$scope.audio = ngAudio.load('sounds/dingding.wav');
I am trying to have external modules change my $translateProvider.translation on the main module. see this as a "tranlation plugin" for my app.
it seems like changing translations from the $translate service is not possible.
mymodule.service('MyService', function ($translateProvider) {
var lib = function () {
//EDITED FOR BREVITY
this._registerTranslations = function (ctrl) {
if (!ctrl.i18n) return;
for (var name in ctrl.i18n) {
/////////////////////////////
// THIS IS THE PLACE, OBVIOUSLY PROVIDER IS NOT AVAILABLE!!!!
$translateProvider.translations(name, ctrl.i18n[name]);
//////////////////////////////
}
};
//EDITED FOR BREVITY
};
return new lib();
});
anyone with a bright idea?
So, to answer your question: there's no way to extend existing translations during runtime with $translate service without using asynchronous loading. I wonder why you want to do that anyway, because adding translations in such a way means that they are already there (otherwise you would obviously use asynchronous loading).
Have a look at the Asynchronous loading page. You can create a factory that will load a translation from wherever you want.
I created an Angular constant to hold new translations. If I want to add a new translation, I add it to the constant. Then in my custom loader, I first check the constant to see if the translation exists (either a new one, or an updated one). If so, I load it from the constant. If not, I load it from a .json file (or wherever you load your initial translations from). Use $translate.refresh() to force translations to be reloaded and reevaluated.
Demo here
The demo is pretty simple. You would need to do a little more work if you wanted to just change a subset of the translations, but you get the general idea.
From the AngularJS docs (https://docs.angularjs.org/guide/providers):
You should use the Provider recipe only when you want to expose an API for application-wide configuration that must be made before the application starts. This is usually interesting only for reusable services whose behavior might need to vary slightly between applications.
Providers are to be used with the application's .config function. $translateProvider for configuration, $translate for other services and controllers.
The plugin model in Backbone.js is really nice, but one thing I'm wondering about is whether it's possible to use multiple plugins without modifying any of the plugin source.
For example, say I've written two plugins for the Collections:
MyBetterCollection = Backbone.Collection.extend({
coolNewFeature: function () {
console.log('This feature is great.');
}
});
MyWayBetterCollection = Backbone.Collection.extend({
wayCoolerNewFeature: function () {
console.log('This feature is even better.');
}
});
I can see some potential issues already, if, for example, both plugins override something like the add method. But having to modify third-party plugins would be a bummer:
MyWayBetterCollection = MyBetterCollection.extend({
...
});
Is there a good approach to handling this situation?
There isn't an easy way to do this. Since there is no traditional inheritance in JavaScript, it's difficult to provide this facility.
You'd have to find plug ins designed to work with each other or fork them and make them compatible.
You could also provide your own extend method that would use the interceptor pattern or some type of monkey patching to provide access to overwritten methods from previous prototypes that were overwritten.
The two collection you've defined above are totally separate from each other. They simply inherit the methods from Backbone.Controller, and if you defined methods with the similar name, then you overwrite those.
Think about Backbone as a class inheritance in other programming languages. Basically, you extend Backbone.Collection as you would do with other languages.
As a result, you can call the superclass, like this
var MyCollection = Backbone.Collection({
toJSON: function() {
var toJSON = this.constructor.__super__.toJSON.call(this);
toJSON.extra = 'my extra value';
return toJSON;
});
Even if you extend your own collection, the logic remains.
I'm trying to write my first mixin for use across different backbone projects, and I'm just wondering how to namespace them.
In short, window. seems intrusive, I can't namespace them under the project because the project name changes, and I can't offer no namespace, because they only work if inside the same file.
So, in short, how should I organise/namespace my mixins so that they're callable throughout a project?
Cheers...
The standard way would be to define a clean AMD-compliant module and then load it in your Backbone projects using a tool like Require.js. This approach doesn't pollute the global namespace. If you don't want to go all AMD, you can use the module pattern as explained in Douglas Crockford's book "Javascript, The Good Parts". It uses a self-invoked function to define your module, and returns the exposed object (or function). It does require a global namespace, though. A nice documentation of the pattern is here. In its simplest form it looks like this:
var MODULE = (function () {
var my = {},
privateVariable = 1;
function privateMethod() {
// ...
}
my.moduleProperty = 1;
my.moduleMethod = function () {
// ...
};
return my;
}());