Create Scope Variables in the View in AngularJS

I've been working on a fairly complex, data-manipulation-centric Angular app for the past couple weeks, and recently came upon a technique that, as far as I can tell, isn't well documented anywhere. You can actually create variables in the current scope, from the model, during binding.

Let me preface all of this by pointing out that I am a relative Angular beginner. If 1 is “What is Angular?” and 10 is “I wrote most of the Angular codebase myself”, then I'm sitting somewhere around 3, maybe a 4 if we're feeling generous. All this is to say that this pattern may be well known and widespread in the Angular community. However, it's new to me, and I had a hard time finding it, so I thought I'd document it here. (I feel fairly confident, having said this, that someone will post a link to an entire 3 page description of this technique in the official online docs.)

Data Binding

The basic pattern to bind a model to the view is:

{{ varA }} // Displays the value of varA

You can go a step further and manipulate model data inside the binding:

{{ varA + varB }} // will display the results of varA+varB

But what if you wanted to display that manipulated value, and then use that same value again elsewhere:

{{ varA + varB }} // will display the results of varA+varB
{{ (varA + varB) / 2 }} // displays the average of varA and varB

That's a bit of wasted effort in this simple case. In the case of very elaborate manipulations, it could be a significant waste, and also prone to errors. Well, it turns out that we can assign a scoped variable to the result of varA + varB, and use that variable elsewhere in the view:

{{ aPlusB = varA + varB }} // will display the results of varA+varB, while also saving the results as aPlusB
{{ aPlusB / 2 }}m// displays the average of varA and varB

ng-repeat Scope

It stands to reason that what works in the parent scope will also work in an ng-repeat scope:

<ul>
  <li ng-repeat="i in items">
    {{ itemTotal = i.varA + i.varB }} // will display the results of the item's varA+varB
    {{ itemTotal / 2 }} // displays the average of the item's varA and varB
  </li>
</ul>

So this is getting pretty neat. But that “itemTotal” variable is “stuck” inside the child scope of the specific ng-repeat iteration. What if we wanted to use that variable outside the current repeat scope? Say we wanted to show the totals in one repeat, and the averages in another:

<ul>
  <li ng-repeat="i in items">
    {{ itemTotal = i.varA + i.varB }} // will display the results of the item's varA+varB
  </li>
</ul>

<ul>
  <li ng-repeat="i in items">
    {{ itemTotal / 2 }} // results in null, as itemTotal is undefined
  </li>
</ul>

We get nulls in the second ul, since itemTotal is undefined. The child scopes from the first repeat are separate from the child scopes of the second.

It turns out that we can assign the results of our addition in the first repeat to a key on the item (i) object. This will persist the result of our data manipulation (simple addition in this case) onto the item (i) object in the parent scope:

<ul>
  <li ng-repeat="i in items">
    {{ i.itemTotal = i.varA + i.varB }} // will display the results of the item's varA+varB
  </li>
</ul>

<ul>
  <li ng-repeat="i in items">
    {{ i.itemTotal / 2 }} // shows the averages of the items in the array
  </li>
</ul>

Check it out on plunkr to see it in action.

ng-repeat Filter

Another neat use for this technique is to assign a variable to the filter results in an ng-repeat. Let's say, for instance, that we wanted to include in our repeat only objects where varA is less than 6, and that we wanted to display a count of the number of objects that meat that criteria. We can do so by assigning a variable to the filter results:

<ul>
  <li ng-repeat="i in (under6Items = (items | filter:isLessThan6))">
    {{ i.itemTotal = i.varA + i.varB }} // will display the results of the item's varA+varB
    {{ under6Items.length }} // will display the number of items whose "a" key is less than 6
  </li>
</ul>

Note that “isLessThan6”³ is a function in the controller that checks the value of varA and returns true if it's less than 6. You can check it out on plunkr.

Wrap Up

What I'm doing here seems like it's treading dangerously close to mixing my view and my controller. I think I'm OK, though, as I'm only creating new scope variable for the sake of display. I suppose if I started saving those variables into a database or something in the controller, I might definitively cross the view/controller line. I welcome any feedback on this topic, but for now, I'm going to use this technique to significantly clean up my html view.