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;
}());
Related
I'm doing Angular JS tutorials. I've no clue why this notation have been used. Can anyone explain what is happening here?
Code in the controller.js file:
var vm = this;
vm.openSideBar = openSideBar; // Did not understand this line
function openSideBar() {
$mdSidenav('left').open();
};
The code that you post is purely stylistic and takes advantage of hoisting to enable the bindable functions to be displayed at the top of the file, and the details (implementations) for those to be kept separate and specified further down the file.
Benefits
The benefits of this approach are more apparent as the number of bindable functions in the controller increases:
Easier to read (what's important). Since the purpose of the controller is to support the view and supply it with bindings, the what is more important than the how i.e. What the view can bind to is more important than how the controller goes about it's work. You can see immediately on opening the file what the controller can do for the view.
Easier to find (the details). Having the bindable functions at the top of the file acts as like a contents page for the file. Most IDE's will highlight matching variables if you place the cursor over them and that helps find the implementation details further down the file.
Encourages single responsibility. Your controller should be supplying bindings for a view, and only one view. You can see at the top of your file if you start to build up lots of unrelated bindable functions that serve different purposes that you might be trying to do too much in one controller.
Encourages named functions. This is particularly useful for debugging as it means that you will have less anonymous functions in your application, which in turn makes reading call stacks in developer tools easier.
Further Reading
Check out John Papa's Angular 1 Style Guide which has been approved by the Angular team. You can read all about the thought behind this approach under the heading Bindable Members Up Top in the Controllers section (although it can be applied to factories/services/directives also).
openSideBar was add to view model. it was aviable to run this func from view like <someElement ng-click="$ctrl.openSideBar()"></someElement>
The VM model or way of doing is quite old... before ES6 came along.
Nowadays, I would suggest you learn ES6, take a look at Todd Motto's AngularJS style guide https://github.com/toddmotto/angularjs-styleguide.
But here an answer to your question:
// OLD WAY
function MyAppController() {
var vm = this;
vm.colors = ['red', 'blue', 'green'];
vm.getBlue() {
return vm.colors[1];
}
vm.handleClickEvent(e) {
// this keyword is now the dom element, not your controller
// this is why the VM pattern came to life.
this.classList.add(vm.getBlue());
}
}
// NEW WAY
class MyAppController {
$onInit() {
this.colors = ['red', 'blue', 'green'];
}
getBlue() {
return this.colors[1];
}
handleClickEvent(e) {
// Here this keyword is still your controller, NOT the dom
// element (provided you used angular's ng-click directive
const elem = e.target;
elem.classList.add(this.getBlue);
}
}
With ES6, it is rare that you have to same a reference to this since you can use arrow functions. You should also get familiar with bind(), call(), and apply() methods to control which context you want to apply to functions.
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.
I'm new to backbone, but have watched several tutorial screencasts on it, both with and without requirejs.
My question involves the setup structure (both file structure if using require, and/or variable/object structure).
Most of the tutorials I have watched, seem to prefer a App.Models, App.Collections, and App.Views approach, and each item inside has the name of the module: ie,
App.Models.todo = Backbone.Model.extend({...});
App.Collections.todos = Backbone.Collection.extend({...});
App.Views.todo = Backbone.View.extend({...});
After a little research, trying to find someone that uses the same style as I would like to use, I finally found: File structure for a web app using requirejs and backbone. They seem to prefer more of a App.[Module Name] method: ie,
App.Todo.Model = Backbone.Model.extend({...});
App.Todo.Collection = Backbone.Collection.extend({...});
App.Todo.Views = Backbone.View.extend({...});
I personally prefer the App.[Module Name] structure over having my modules split up, but would like to know the benefits, if any, of having the different structures.
Which structure do you use, and how has it helped you over a different structure you may have seen or used in the past?
I like the approach described in this blog:
http://weblog.bocoup.com/organizing-your-backbone-js-application-with-modules/
If you are using requireJS you don't need/want to attach the models/views to a global namespace object attached to the window (no App.Views, App.Models). One of the nice things about using requireJS or a different AMD module loader is that you can avoid globals.
You can define a model like this:
define(['underscore', 'backbone'],
function(_, Backbone) {
var MyModel = Backbone.Model.extend({});
return MyModel;
});
Then you define a view:
define(['underscore', 'backbone', 'tpl!templates/someTemplate.html'],
function(_, Backbone, template) {
var MyView = Backbone.View.extend({});
return MyView;
});
Now you have a model and a view with no globals. Then if some other module needs to create one of these (maybe your App module), you add it to the define() array and you have it.
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 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');