Controllers are dying. The migration path doesn’t even make a reference of them. Once controllers were a cornerstone of Angular. Now we’re all trying to sweep them under the carpet.
The question that bothers me is what can you do today to make your life easier once Angular 2 is out. Last time I discussed a first step – making controllers smaller and cleaner.
But I’ve been toying with not writinga controllers at all. I’ve seen several people already do this and my experience so far is nice. We’re all still learning this together. I figured I’d share how I made it work for me.
This preparation step will, hopefully, make your transition to Angular 2 easier and smoother. Especially once
ng-upgrade is out and you’ll be able to use all your code units.
The mechanism: component directives
We achieve controller annihilation by using directives everywhere you’d use a controller. That means your app’s code is now either in a directive or in a factory (service).
It’s important to make a mind shift. Stop thinking about directives as a building block for reusable code. It’s a building block, period.
Isolated directives are self-contained components that I find easy to reason about and maintain.
I prefer to use directives with a
controller function and not use
link. That’s mostly because
controller means I almost never need to inject
$scope. Also, because using controllers means I can then
require them in child directives.
A basic example: controller turned directive
Here’s a very basic controller that we’ll turn into a directive (plunk):
1 2 3 4 5 6 7 8 9
And its template is:
1 2 3 4 5 6 7 8
(Did I mention it was basic?)
Here’s the after picture (plunk):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
What have we got here?
- Notice that the template didn’t need any changes.
- Same goes for the actual controller function itself.
- We made sure to define the directive with
scope:. Just the right incantation.
This resulted in a little more boilerplate but we got rid of the controller. Along the way we also earned an isolated scope, win! And, we have a true component. You can take a look at this .js file and you see all you need to know – the inputs, the template, the controller.
Interesting notes and caveats
Do DOM manipulation in the directive controller: This might feel wrong. You’ve heard the mantra “Don’t do DOM manipulation in controllers” a thousand times. But this isn’t a controller anymore. It’s a component. Angular even let’s you inject
$element inside these directives. Take use of it!
Sometimes you still need
link functions: The controller function executes before the element has rendered. If, for example, you want to register an event handler on some child element you’ll have to do it in the
link function. Another is when you want to use
require and get access to that controller.
Defining routes: You no longer need to supply a controller in your ui-router or ng-route configuration. Just pass a simple template such as
Some widgets love controllers: If you’re using widgets such as ui-bootstrap’s modals you will see they love controllers. It’s still possible to use them without controllers. The above workaround works.
The way forward
As I said, I’m still figuring this out along with you. But so far I’ve found this to be simpler. For a long time I’ve avoided creating controllers except for routing endpoints. Now I just don’t use that as well. Having everything be a directive means less mental overload and a simpler file structure.
If you’ve been toying with this too I’d love to hear your thoughts and techniques.