Many Directives, One Controller in Angular

Recently, I was implementing a feature that could toggle between related data in a minimized, detail, and list view.  It looked something like this:

multiple directives

Simplifying the Process

Going into implementation, I knew I wanted to use directives (which is what AngularJS v1 calls components). Components are the best way to group views and logic in front-end applications, and ReactJS, AngularJS v2, and EmberJS v2 all build their view logic entirely out of components. During this project, I found a neat way to simplify the process.

Keeping It Bite-Sized

Since Angular’s templates contain a lot of logic, I like to keep my directive templates bite-sized and easy to read. HTML markup is so busy that when templates get big, it’s hard to see the Angular logic. There was enough markup in each view I wanted to justify creating separate templates, but because the interaction between them was simple and strongly tied together, I really didn’t need to manage binding of data and behavior for each piece individually.

My Solution

My solution? Create different directives for each state of the view, along with a super directive to tie them all together.  All three directives share the same controller, so they can easily share view state and interactions. The ng-show logic in the super controller can be cleanly read, and the behavior is easy to understand.  

You can try it out here: http://jsfiddle.net/jeanettehead/5r6cxgu4/

The template code is hard to read inline in the JSFiddle, but here’s what it looks like with a tiny bit of formatting:

<minimized-directive ng-show="viewState === VIEW_STATES.minimized" ng-click="showDetail()"></minimized-directive>
<detail-directive ng-show="viewState === VIEW_STATES.detail" ng-click="showList()"></detail-directive>
<list-directive ng-show="viewState === VIEW_STATES.list" ng-click="showMinimized()"></list-directive>

One key piece of this solution is that only the super directive can be passed data on scope.  If the children are passed data, they end up having their own scope, not a shared scope.  Also, because the directive will be initialized multiple times, data should be passed in or fetched in a managed way–not fetched on load, which will cause it to be fetched once for every directive using the controller.

Best Practice

I would not recommend this solution if there were complex view logic for each view.  In my situation, which was like the demo above, I simply wanted multiple ways to display what was essentially the same data.  For this scenario, it was a great way to keep my logic together and keep my Angular templates short and easy to read.