AngularJS offers a number of patterns for using partial templates but it’s not always obvious which one to use. Here we will discuss some of those approaches and introduce a simple directive that you can use in one common situation.
Problem: At ModernAdvisor we build a lot of long forms with repeated sets of fields. We want to DRY up our templates to shorten them and improve maintainability. Here is a somewhat contrived example.
Let’s look at some common ways to solve this in AngularJS.
- ng-include
- custom directives
- ui-router
ng-include
ng-include
is a built-in directive that allows you to specify a template to render inside of another template. You can read all about it over in the API docs.
We’ll start off by factoring out the reusable html and putting it into a partial template.
Using ng-include
, our form template will now look something like this.
But wait, now both partials refer to person
. How will the spouse
fields ever get set? We need to bind the first partial instance to person
and the second instance to spouse
. One solution is to wrap each ng-include
directive in an ng-repeat
.
This isn’t much shorter that the original form, and definitely has a certain hackiness to it. Generally, ng-include
is best for breaking views up or creating simple reusable widgets. Things get messy when you can’t simply inherit the controller scope. This is where custom directives come in.
Custom directive
If you’re an Angular veteran, the ng-include
solution above likely caused a minor stroke while you screamed write a directive!! at your monitor. Let’s see what that might look like. First we need to define our directive.
And then put it to use in our form.
Now we’re talking! No wonder this technique is so popular – look how clean and concise the form is now. There are a couple of drawbacks though.
First, directives are powerful. I mean really powerful. You can do pretty much anything you could want in the DOM with directives. The problem is that if you are reading the form template above for the first time, you need to ask what does that directive do? and then read the directive source.
The second problem is that you have to write a new directive for every partial that you want. As Bill Gates (apparently) says:
I choose a lazy person to do a hard job. Because a lazy person will find an easy way to do it.
Well Bill, if you really said that, you found your guy. Let the search continue!
ui-router
If you’re using the popular ui-router
library, there is one more weapon in your arsenal. ui-view
is a directive that allows you to nest templates inside of each other and specify a controller for each.
So far so good.
If you haven’t used ui-router
before, this is a lot to digest so I suggest you visit the docs or take a few minutes to play around with the plunk. This is another powerful technique for structuring your app and reusing templates. We prefer this approach for building layouts and combining large blocks of content into a view.
Unfortunately, none of these approaches meet our need for a simple method of including partials and attaching data within templates.
partial
You may have noticed that the underlying challenge here is creating and managing the scope of the partial. What we need is a simple way to instantiate new scopes from inside templates and attach data to it. We can accomplish this by combining ng-include
with a custom directive.
Now for the gory details.
The partial
directive requests the template at the url specified by the src
attribute much the same way as ng-include. One difference is that partial
expects a string rather than an expression so there is no need for single quotes around the url.
You pass data from the template to the partial scope using scope-[partial-variable-name]="[template-variable-name]"
. As long as the template variable is assignable (i.e. a variable name and not a constant), changes in either scope will automatically be synced to the other.
This is a major tripping point for new Angular developers so it’s worth reiterating. partial
provides two-way data binding for assignable scope attributes (including primitives).
In the example above, clicking either button would increment both number
and data
. Go ahead, give it a try.
This simple directive lets us DRY up our code without the overhead of writing extra JavaScript whenever we want to control the partial’s scope. One step closer to AngularJS enlightenment.