Cleaner class-based controllers for AngularJS
Structure
As your Angular controller grows, it can start to get unwieldy and difficult to structure.
Classy uses a class-based approach and steals some neat ideas from AngularDart to make controllers a little nicer.
No more annotating your dependencies
This is a big-ee. Angular veterans will know that if you want your Angular code to work with minifiers then you have to annotate your dependencies (i.e. list your dependencies twice), like so (without Classy):
app.controller('AppCtrl',
['$scope', '$location', '$http',
function($scope, $location, $http) {
// ...
}]);
Annotating your dependencies is annoying and also it's just not very DRY. If you want to add/remove a dependancy then you need to remember to do it in two places.
In Classy you don't need to do that, it works with minifiers and your code remains DRY. Here's what it looks like with Classy:
app.classy.controller({
name: 'AppCtrl',
inject: ['$scope', '$location', '$http'],
// ...
});
$scope
convenience
Functions are automatically added to the controller's $scope
Most of the time when you add a function to a controller, you want it available on the $scope
. This is so that you can easily call it in your html using directives like ng-click
. Here's how it used to look without Classy:
$scope.editTodo = function(todo) {
//...
}
and now with Classy:
editTodo: function(todo) {
//...
}
If you don't want the function to be on the $scope
then just prefix it with an underscore character (_
).
Handy shortcut for $scope
To access the $scope
You can simply write this.$.foo = 'bar';
instead of this.$scope.foo = 'bar';
. Although you can use still use this.$scope
if you prefer.
Special object for $watch
listeners
Instead of polluting your init
method with lots of calls to $scope.$watch
, you can put them in a watch object instead:
watch: {
'location.path()': function(newValue, oldValue) {
// ...
},
'{object}todos': function (newValue, oldValue) {
// ...
}
}
Notice the {object}
keyword in the second listener above. This allows you to easily specify the type of watcher to use. This is much more explicit than Angular's approach. Here is a table of the available keywords:
Keyword | $watch Type |
---|---|
{collection} or {shallow}
|
$watchCollection(..)
|
{object} or {deep}
|
$watch(.., objectEquality = true)
|
Only 1KB (gzipped and minified)
it's super tiny so you don't have to worry about it adding weight to your application.
Bonus beta features
Named dependencies
Classy allows you to name dependencies whatever you like, simply pass in an object instead of an array. Use a '.' value if you wish to use the original name for a dependency.
app.classy.controller({
name: 'MyCtrl',
inject: {
$scope: '$',
filterFilter: 'filter',
$location: '.'
},
init: function() {
// Check if dependencies are defined
console.log(this.$); // ✔ ChildScope {}
console.log(this.$scope); // ✘ undefined
console.log(this.$location); // ✔ LocationHashbangUrl {}
// Use a dependency
console.log(this.filter(
this.$.todos,
{ completed: true }
)); // [{"title":"Learn Angular","completed":true}]
}
});
Reverse-reference controllers
This feature is stolen from AngularDart (you may also be familiar with it from Backbone and other MVC frameworks). It's best explained with code.
This is how you would typically bind a controller to a view with Angular Classy:
<!-- In your HTML -->
<div id="footer" ng-controller="FooterCtrl"></div>
// In your JS
app.classy.controller({
name: 'FooterCtrl',
//...
});
If you want to use reverse-reference controllers then you simply give your controller an element selector reference instead of a name, like this:
<!-- In your HTML -->
<div id="footer"></div>
// In your JS
app.classy.controller({
el: '#footer',
//...
});
Classy will use jQuery as it's selector engine if available, otherwise it will fallback to document.querySelectorAll
. There is currently no test coverage for this feature so it's probably best not to use it in production (unless you're a badass).
FAQs
Click the questions below to expand the answers.
How do I use Classy Controllers in a directive?
How do I reference a Classy controller in a route?
How do I use classy with the `TodoCtrl as todo` syntax?
How do I ask a question that isn't answered here?
Add ‘classy’ to your app modules
var app = angular.module('app', ['classy']);
Registers your controller and inject your dependencies.
Injecting dependencies with Classy plays nice with minifiers, you don’t need to annotate
your dependencies (i.e. list dependencies twice) and your code remains DRY.
By the way you can use the shortcut app.cC instead of app.classy.controller if you prefer.
app.classy.controller({
name: 'TodoCtrl',
inject: ['$scope', '$location', 'todoStorage', 'filterFilter'],
An init method for your initialization code. Who’d have thunk it?
You can access your dependencies using the class-wide this symbol.
The $scope is available using this.$ (or you can use this.$scope if you prefer).
init: function() {
this.todos = this.$.todos = this.todoStorage.get(); //!
this.$.newTodo = ''; //!
this.$.location = this.$location; //!
}, //!
Instead of polluting your init method with lots of calls to $scope.$watch,
you can put your watchers in the watch object instead.
If you want to watch an object or collection just use the {object} or {collection} keyword.
watch: {
'location.path()': function(path) {
this.$.statusFilter = (path === '/active') ? //!
{ completed: false } : (path === '/completed') ? //!
{ completed: true }; //!
}, //!
'{object}todos': '_onTodoChange'
}, //!
Most of the time when you add a function to a controller, you want it available on the $scope
.
Classy automatically puts the function in your $scope
so you can easily access it using directives like ng-click.
addTodo: function() {
var newTodo = this.$.newTodo.trim(); //!
this.todos.push({ //!
title: newTodo, //!
completed: false //!
}); //!
}, //!
Prefix the function name with an underscore and Classy wont add it to the $scope.
_onTodoChange: function(newValue, oldValue) {
this.$.remainingCount = //!
this.filterFilter(this.todos, { completed: false }).length; //!
} //!
}); //!
Installation
-
Or install with bower:
bower install angular-classy
-
Reference Classy after the reference to Angular
<script src="bower_components/angular/angular.js"></script> <script src="bower_components/angular-classy/angular-classy.min.js"></script>
-
Add Classy to your application module
var app = angular.module('app', ['classy']);
- That's it, you can create a
classy.controller
like so:app.classy.controller({ name: 'MyCtrl', inject: ['$scope'], init: function() { } });
More Info
- Version: 0.4.2 (Changelog)
- Github Repo
- Test status:
- License: MIT
- Author: Dave Jeffery
- Find Angular Classy useful? Share it on twitter!