cnshenj
cnshenj

Reputation: 167

AngularJS multi-element directive

AngularJS supports multi-element directive with -start and -end postfix. The official documentation only mentions ng-repeat-start and ng-repeat-end. Do other built-in directives support this?

For example, this works fine:

<tbody>
  <tr ng-controller="myController">
    <td>{{firstName}}</td>
    <td>{{lastName}}</td>
  </tr>
</tbody>

Both {{firstName}} and {{lastName}} are replaced with their proper value.

But this only works partially:

<tbody>
  <tr ng-controller-start="myController">
    <td>{{firstName}}</td>
  </tr>
  <tr ng-controller-end>
    <td>{{lastName}}</td>
  </tr>
</tbody>

{{firstName}} is properly replaced. But {{lastName}} is empty. Since {{firstName}} works, seems ng-controller-start is recognized by AngularJS. Is it a bug, or I'm doing it wrong, that {{lastName}} is not working?

Update If ng-controller-start and ng-controller-end is not officially supported. How do I make ng-controller to span multiple elements? Can I use comment-style directive? If yes, how?

Upvotes: 5

Views: 7120

Answers (2)

Michael Bromley
Michael Bromley

Reputation: 4822

In Angular 1.2

With Angular 1.2, support for multi-element directives was introduced. It seems like the specific use-case they had in mind was ngRepeat, since it wasn't promoted to be used with any of the other built-in directives as far as I am aware. With ngRepeat, however, it solved a very particular problem such as how to repeat more than one table row per item.

In 1.2, the $compile service will detect any directive that is suffixed with -start and assume it to be one of a pair of multi-element directive attributes (source). This leads to the undesirable side-effect that you cannot name your directive something-start, as the $compile service will get confused when it does not find the multi-element counterpart.

This leads to the error: Error: [$compile:uterdir] Unterminated attribute, found 'something-start' but no matching 'something-end' found.

The fact that the $compile service is indiscriminate in treating directives as multi-element is why you are able to use ng-controller-start and ng-controller-end in Angular 1.2. However the ngController directive is not designed to deal with multiple elements and this is why it does not work as expected. It will act on the first element in the range, and ignore the rest - just as you observed.

In Angular 1.3

Angular 1.3 fixes the above problem by requiring that any multi-element directives be explicitly defined as such by using the new multiElement: true property on the directive definition object. See the docs on this

This means that ng-controller-start will not do anything in 1.3, since it will cause the compiler to look for a directive named "ngControllerStart", which does not exist. Therefore the directive attribute will simply be ignored.

As the other answer points out, you can now search the angular.js GitHub repo for "multiElement" to see the specific core directives that support this feature.

Upvotes: 4

Explosion Pills
Explosion Pills

Reputation: 191799

Whether or not a directive supports this is based on its directive definition and the multiElement property.

It doesn't seem like Angular's documentation says which built in directives are multi-element, but a Github search seems to reveal that it's only ngRepeat, ngSwitchWhen, ngSwitchDefault, ngIf, ngShow, and ngHide.

You can create your own directives with multiElement as well.

Upvotes: 4

Related Questions