- exploring ways to extend controller
- disclaimer: it's in CoffeeScript
In Angular, it's always good practice to split up and refactor non-view related logic into Services and break up re-usable view-related logic into components. But once in a while, it can still get out of control when a view has convoluted logic.
This is exactly the case that I'm running into. Roughly half of my controller is based on how the user navigates while the other half is based on server reponse when resources change. However, at the same time, I also have a different view for read-only user, which means, I have to duplicate the same navigation code in two places in our codebase.
I started a quest to explore a better way to extend controller in Angular. My goal is to have the navigation related code only in one parent controller and allow the code to be reused in multiple places.
This is what I ended up doing:
- use factory to return a base controller class
- in main controller, extend the base controller with customization
- pass in scope/vm into customized class
The scaffolding app is generated with generator-gulp-angular and you can take a look at the repo here to compare.
Here's what my
MainController looks like:
angular.module 'coffeeBaseCtrlTest' .controller 'MainController', (BaseController) -> 'ngInject' vm = this class MainController extends BaseController activate: -> console.log 'activate from MainController' super mainCtrl = new MainController(vm) mainCtrl.activate()
BaseController, it has all the methods defined in BaseController, and this is advantageous because it allows you to call
super and use the methods from
Here's what the
BaseController factory looks like:
angular.module 'coffeeBaseCtrlTest' .factory 'BaseController', ($timeout, webDevTec, toastr) -> 'ngInject' class BaseController constructor: (vm) -> @vm = vm @vm.awesomeThings ?=  @vm.classAnimation ?= '' @vm.creationDate ?= 1453658664761 @vm.showToastr = @showToastr activate: -> console.log 'activate from BaseController' @getWebDevTec() $timeout (=> @vm.classAnimation = 'rubberBand' return ), 4000 return getWebDevTec: -> @vm.awesomeThings = webDevTec.getTec() angular.forEach @vm.awesomeThings, (awesomeThing) -> awesomeThing.rank = Math.random() return return showToastr: => toastr.info 'Fork <a href="https://github.com/Swiip/generator-gulp-angular" target="_blank"><b>generator-gulp-angular</b></a>' @vm.classAnimation = '' return BaseController
The advantages of doing it this way are:
- reduces code by the use of
- it's easy to see in the constructor what's really binding to the vm
I'm sure there's a better way out there to write this, but right now this seems to be an easier approach without modifying too much of the codebase. After all, in my experience, it's not that common to have a need to extend from a parent controller.
Here's a different approach to extract base controller in CoffeeScript:
Thanks for reading and suggestions are welcome : )