njfife
njfife

Reputation: 3575

Is there a difference between Element and Attribute directives when using transclusion?

I am attempting to make a table directive that will have some more advanced functionality but when I was writing the basic version to build from I came across something I don't understand.

I have a directive named "njTable"

When I use it as an Attribute it works:

<body ng-app="tableTest">
<div ng-controller="mainCtrl as mc">
  <div></div>
  <table nj-table>
    <nj-head>
      <tr>
        <th>Name</th>
        <th>Age</th>
        <th>State</th>
      </tr>
    </nj-head>
    <nj-body>
      <tr ng-repeat="person in mc.asyncList">
        <td>{{person.name}}</td>
        <td>{{person.age}}</td>
        <td>{{person.state}}</td>
      </tr>
    </nj-body>
  </table>
</div>

However, when I used the exact same directive as an element it breaks:

<body ng-app="tableTest">
<div ng-controller="mainCtrl as mc">
  <div></div>
  <nj-table>
    <nj-head>
      <tr>
        <th>Name</th>
        <th>Age</th>
        <th>State</th>
      </tr>
    </nj-head>
    <nj-body>
      <tr ng-repeat="person in mc.asyncList">
        <td>{{person.name}}</td>
        <td>{{person.age}}</td>
        <td>{{person.state}}</td>
      </tr>
    </nj-body>
  </nj-table>
</div>

Here is the broken Plunker: http://plnkr.co/edit/zkpPcJG1ZZ5XjORJOoy6?p=preview

Here is the functional Plunker: http://plnkr.co/edit/9W56YRREyuR4ew2rgVUc?p=preview

Maybe I don't fully understand the difference between using a directive as an attribute vs using it as an element?

Upvotes: 2

Views: 871

Answers (2)

SoEzPz
SoEzPz

Reputation: 15912

When I looked at your (broken) plunker link chrome console was throwing this error:

Error: error:tplrt
Invalid Template Root
Template for directive 'njTable' must have exactly one root element.

which linked to this angularjs page:

Angularjs Error Link

In the initial description, the angularjs link states:

When a directive is declared with template (or templateUrl) and replace mode on, 
the template must have exactly one root element.

Perhaps the description could be more explicit and state "exactly one HTML root element." As Enzey states, in your case, that root element MUST be a table element as IT is the parent of th, td, and tr in ANY HTML DOM, which is why the below HTML works as well as , it says the same thing:

<table class="table table-hover">
  <nj-head>
    <tr>
      <th>Name</th>
      <th>Age</th>
      <th>State</th>
    </tr>
  </nj-head>
  <nj-body>
    <tr ng-repeat="person in mc.asyncList">
      <td>{{person.name}}</td>
      <td>{{person.age}}</td>
      <td>{{person.state}}</td>
    </tr>
  </nj-body>
</table>

Your idea works, just not with the HTML Table element as it is ALWAYS a parent of of it's nested siblings. Here is your same idea working, but with Divs and Spans.

Note: I used some CSS to mimic the table look that was originally provided by Tbootstrap.

<nj-table>
  <nj-head>
    <div>
      <span class="tableHeader">Name</span>
      <span class="tableHeader">Age</span>
      <span class="tableHeader">State</span>
    </div>
  </nj-head>
  <nj-body>
    <div ng-repeat="person in mc.asyncList">
      <div class="tableItem">{{person.name}}</div>
      <div class="tableItem">{{person.age}}</div>
      <div class="tableItem">{{person.state}}</div>
    </div>
  </nj-body>
</nj-table>

Here is the working plunker link: Working Plunker Link

To complete your answer - it's not an Angularjs transclude issue, it's just the way HTML is designed.

Upvotes: 0

Enzey
Enzey

Reputation: 5254

The reason your directive does not function as an element is because the browser renders the HTML before Angular runs and purges any invalidly placed elements.

In your example you have:

<nj-body>
    <tr>

The above is invalid HTML because only a TABLE, THEAD, TFOOT, or TBODY element are a valid parent elements of a TR. This comes from the HTML5 spec as can be seen here: http://www.w3.org/TR/html-markup/tr.html

When the browser gets this mark up it completely removes the TR tag because it is not used in a valid way. That then cascades to the TD below it and so on.

Upvotes: 2

Related Questions