├── .gitignore ├── .travis.yml ├── Gruntfile.js ├── LICENSE ├── README.md ├── backbone.controller.js ├── bower.json ├── package.json └── tests ├── config.js ├── spec ├── basic.js ├── router.js └── routerCancel.js └── vendor ├── Q.js ├── backbone.js ├── chai.js ├── jquery.js ├── sinon.js └── underscore.js /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | node_modules 16 | 17 | .DS_Store 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.10 4 | before_install: 5 | - export DISPLAY=:99.0 6 | - sh -e /etc/init.d/xvfb start 7 | - sleep 3 -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | grunt.initConfig({ 3 | jshint: { 4 | options: { 5 | browser: true, 6 | globals: { 7 | console: true 8 | } 9 | }, 10 | all: [ 11 | 'Gruntfile.js', 12 | 'backbone.controller.js', 13 | 'tests/**/*.js', 14 | '!tests/vendor/*.js' 15 | ] 16 | }, 17 | 18 | karma: { 19 | test: { 20 | configFile: 'tests/config.js', 21 | singleRun: true, 22 | browsers: ['PhantomJS'] 23 | } 24 | } 25 | }); 26 | 27 | grunt.loadNpmTasks('grunt-contrib-jshint'); 28 | grunt.loadNpmTasks('grunt-karma'); 29 | 30 | grunt.registerTask('test', ['jshint', 'karma']); 31 | grunt.registerTask('default', ['test']); 32 | }; 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Artyom Trityak 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Backbone.Controller 2 | =================== 3 | 4 | [![Build Status](https://travis-ci.org/artyomtrityak/backbone.controller.png)](https://travis-ci.org/artyomtrityak/backbone.controller) 5 | 6 | 7 | 8 | 9 | Controller for Backbone MV* 10 | 11 | Keep controller logic separated, split your routes to controllers. 12 | 13 | ## Usage 14 | 15 | DEMO: [Just Test It](https://github.com/artyomtrityak/just-test-it) 16 | 17 | Usage examples: 18 | 19 | ### Basic 20 | 21 | ```js 22 | var Controller = Backbone.Controller.extend({ 23 | initialize: function() { 24 | // do init stuff 25 | }, 26 | 27 | search: function(query, page) { 28 | // create search model and view 29 | } 30 | }); 31 | 32 | var searchController = new Controller(); 33 | ``` 34 | 35 | ### Controller supports default Backbone events 36 | 37 | ```js 38 | var Controller = Backbone.Controller.extend({ 39 | initialize: function() { 40 | this.model = new Backbone.Model(); 41 | this.listenTo(this.model, 'add', this._onAdd); 42 | }, 43 | 44 | _onAdd: function(model) { 45 | // show notification view 46 | } 47 | }); 48 | 49 | var catsController = new Controller(); 50 | ``` 51 | 52 | ### Controller has remove method for cleanup 53 | 54 | Remove method should do correct remove for all controller views and models, stop listening controller events and clear state. 55 | 56 | ```js 57 | var Controller = Backbone.Controller.extend({ 58 | initialize: function() { 59 | this.model = new Backbone.Model(); 60 | this.listenTo(this.model, 'add', this._onAdd); 61 | }, 62 | 63 | _onAdd: function(model) { 64 | // show notification view 65 | } 66 | }); 67 | 68 | var catsController = new Controller(); 69 | //... 70 | catsController.remove(); 71 | ``` 72 | 73 | Also remove method is calling automatically when user goes from one controller to another. See routing section for details. 74 | 75 | ### Controller supports declarative routes definition. 76 | 77 | It's little more complex than previous examples but can be used to keep all routes separately 78 | which is good idea for any size app. 79 | 80 | 81 | ```js 82 | var CatsController = Backbone.Controller.extend({ 83 | routes: { 84 | 'cats': 'list', 85 | 'cats/:id': 'showCat' 86 | }, 87 | 88 | initialize: function() { 89 | // do some init stuff 90 | }, 91 | 92 | list: function() { 93 | // show cats list 94 | }, 95 | 96 | showCat: function(catId) { 97 | // show cat view 98 | } 99 | }); 100 | 101 | var DogsController = Backbone.Controller.extend({ 102 | routes: { 103 | '': 'list', 104 | 'dogs': 'list', 105 | 'dogs/:id': 'showDog' 106 | }, 107 | 108 | initialize: function() { 109 | // do some init stuff 110 | }, 111 | 112 | list: function() { 113 | // show dogs list 114 | }, 115 | 116 | showDog: function(catId) { 117 | // show cat view 118 | }, 119 | 120 | remove: functin() { 121 | // cleanup 122 | } 123 | }); 124 | 125 | var Application = Backbone.Router.extend({ 126 | controllers: {}, 127 | 128 | initialize: function() { 129 | this.controllers.cats = new CatsController({router: this}); 130 | this.controllers.dogs = new DogsController({router: this}); 131 | 132 | Backbone.history.start(); 133 | } 134 | }); 135 | ``` 136 | 137 | The main idea - pass `{router: routerInstance}` as controller option. 138 | This allows to define controller specific routes in separated controllers. 139 | 140 | When url changes from `#dogs` / `#dogs/:id` to any route which defined in another controller, remove method is calling automatically. 141 | 142 | This case controller should clear state, remove controller specific views and models. 143 | 144 | ### Controller can automatically add router without creating Backbone.Router instance 145 | 146 | ```js 147 | var CatsController = Backbone.Controller.extend({ 148 | routes: { 149 | 'cats': 'list', 150 | 'cats/:id': 'showCat' 151 | }, 152 | 153 | initialize: function() { 154 | // do some init stuff 155 | }, 156 | 157 | list: function() { 158 | // show cats list 159 | }, 160 | 161 | showCat: function(catId) { 162 | // show cat view 163 | } 164 | }); 165 | 166 | var DogsController = Backbone.Controller.extend({ 167 | routes: { 168 | '': 'list', 169 | 'dogs': 'list', 170 | 'dogs/:id': 'showDog' 171 | }, 172 | 173 | initialize: function() { 174 | // do some init stuff 175 | }, 176 | 177 | list: function() { 178 | // show dogs list 179 | }, 180 | 181 | showDog: function(catId) { 182 | // show cat view 183 | } 184 | }); 185 | 186 | var cats = new CatsController({router: true}); 187 | var dogs = new DogsController({router: true}); 188 | ``` 189 | 190 | ### Before / after routing 191 | 192 | Controller automatically calls `onBeforeRoute` / `onAfterRoute` functions when processing routes. 193 | 194 | ```js 195 | var DogsController = Backbone.Controller.extend({ 196 | routes: { 197 | '': 'list', 198 | 'dogs': 'list' 199 | }, 200 | 201 | initialize: function() { 202 | // do some init stuff 203 | }, 204 | 205 | onBeforeRoute: function(url, param1, param2, ...) { 206 | // called before `#dogs` / `#` routes 207 | // Set some state variables, create controller layout etc 208 | }, 209 | 210 | onAfterRoute: function(url, param1, param2, ...) { 211 | // called after `#dogs` / `#` routes 212 | }, 213 | 214 | list: function() { 215 | // show dogs list 216 | } 217 | }); 218 | 219 | var dogs = new DogsController({router: true}); 220 | 221 | 222 | //Cancel route 223 | var DogsController = Backbone.Controller.extend({ 224 | routes: { 225 | 'dogs': 'list', 226 | 'dogs/:id': 'showDog' 227 | }, 228 | 229 | initialize: function() { 230 | // do some init stuff 231 | }, 232 | 233 | list: function() { 234 | // show dogs list 235 | }, 236 | 237 | showDog: function(catId) { 238 | // show cat view 239 | }, 240 | onBeforeRoute : function(url) { 241 | console.log('before route'); 242 | var deferred = Q.defer(); 243 | 244 | setTimeout(function() { 245 | deferred.resolve('ggg'); 246 | }, 2000); 247 | 248 | return deferred.promise; 249 | //return false; 250 | }, 251 | onAfterRoute : function() { 252 | console.log('afterRoute'); 253 | } 254 | }); 255 | 256 | var dogs = new DogsController({router : true}); 257 | Backbone.history.start(); 258 | ``` 259 | 260 | ### Redirecting to another route 261 | 262 | If declarative routing has been used in project, you don't have access directly to Router instance. 263 | Backbone Controller provides Controller.navigate method as proxy for Backbone.Router.navigate method. 264 | 265 | ```js 266 | var DogsController = Backbone.Controller.extend({ 267 | routes: { 268 | 'dogs': 'list' 269 | }, 270 | 271 | list: function() { 272 | // show dogs list 273 | // if something 274 | this.navigate('cats/', {trigger: true}); 275 | } 276 | }); 277 | 278 | var dogs = new DogsController({router: true}); 279 | ``` 280 | 281 | ## Dependencies loading 282 | 283 | ### Require.js AMD 284 | 285 | ```js 286 | requirejs.config({ 287 | baseUrl: 'static/', 288 | urlArgs: 'bust=' + Date.now(), 289 | paths: { 290 | jquery: 'assets/js/jquery', 291 | underscore: 'assets/js/underscore', 292 | backbone: 'assets/js/backbone', 293 | controller: 'assets/js/backbone.controller' 294 | }, 295 | 296 | shim: { 297 | backbone: { 298 | deps: ['underscore', 'jquery'], 299 | exports: 'Backbone' 300 | }, 301 | controller: { 302 | deps: ['underscore', 'backbone'] 303 | }, 304 | app: ['controller'] 305 | } 306 | }); 307 | ``` 308 | 309 | ### CommonJS 310 | 311 | ```js 312 | var Controller = require('controller'); 313 | // or require Backbone, both fine 314 | 315 | var HomeController = Controller.extend({ 316 | ... 317 | }); 318 | ``` 319 | 320 | ### Old style 321 | 322 | ```html 323 |