AngularJS directives - can "template" function be called AFTER postLink..

667 views Asked by At

It is complex to explain, but I'll try to.

http://plnkr.co/edit/PrVXRvHAC7wtgHUEU1HB?p=preview

This example works as I need, I just don't like the way I achieved this.

I'll try to explain what you see in the example.

<repeater>

  <item name="Hello item">
    <h4>Hello</h4>
    <p>Lorem ipsum dolor sit amet</p>
  </item>

  <item name="Worlditem">
    <h4>World</h4>
    <em>Mauris elementum elementum enim at suscipit.</em>
  </item>

</repeater>

<item> directive should "register" itself to the parent repeateras available_item. And it shouldn't be shown at the moment.

<repeater> directive should print menu of available items, and after clicking on menu item, it should add item's template to it's content. For example repeater template may look something like this:

<div>
  <ul>
    <li ng-repeat="item in available_items">
      <a href="javscript:;" ng-click="add2content(item)">{{item}}</a>
    </li>
  </ul>

  <div id="content">
    <div ng-repeat="item in content_items track by $index">
      <ng-include src="item"></ng-include>
    </div>
  </div>

</div>

So what don't I like in my working plnkr.co example?

I don't like that I must include <div ng-transclude></div> in my repeater.html, otherwise <item>'s won't be compiled any more. But I don't need to print items at page load - they are just templates. I have set <items> template to empty string, but in source I see empty <item> tags, and I don't like this..

Actually I am not sure what I don't like there, but it feels like everything is hard-coded there, isn't it?

If repeater's directive would be compiled in this order:

  1. controller() - add2available() function would be registered
  2. preLink()
  3. //now goes all item directives, and they could register itself to repeater
  4. postLink()
  5. template() - now repeater changes it's template where is no ng-transclude

I'm sure It's impossible to do this. And I'm not sure what I don't like in my code, but I hope you will see other ways to improve my code.

Thank you very much!

P.S. I know I can replace <item> directive with some <script type="text/ng-template" id=""> tags, but I need <item> as provided in my example.

1

There are 1 answers

1
Buu On BEST ANSWER

This plunkr does what you want to achieve, except the parts you don't like (i.e. dummy <transclude> tag and empty <item> tags). I've also cleaned up hacks like storing HTML in attribute, return empty template, pollute template cache...

The key element of this solution is using the $transclude service to compile and link directives declared in the transcluded content. The entire updated script is below.

script.js

angular.module('repeaterApp', [])
  .directive('repeater', function() {
    return {
      restrict: 'E',
      transclude: true,
      scope: {},
      templateUrl: 'repeater.html',
      controller: function($scope, $element, $transclude) {
        var available_items = $scope.available_items = [];
        var content_items = $scope.content_items = [];

        $scope.add2content = function(item) {
          content_items.push(item);
        };

        $scope.add2available = function(item) {
          available_items.push(item);
        };

        $transclude($scope);
      }
    };
  })
  .directive('item', function($templateCache, $sce) {
    return {
      restrict: 'E',
      link: function(scope, element, attrs) {
        scope.add2available({
          name: element.attr('name'),
          html: $sce.trustAsHtml(element.html())
        });
      }
    };
  });

repeater.html

<div>
  <ul>
    <li ng-repeat="item in available_items">
      <a href="javscript:;" ng-click="add2content(item)">{{item.name}}</a>
    </li>
  </ul>

  <div id="content">
    <div ng-repeat="item in content_items track by $index" 
         style="background: #f4f4f4; padding: 5px; margin: 5px; clear: both;"
         ng-bind-html="item.html">
    </div>
  </div>
</div>

index.html

<repeater>

  <item name="Hello item">
    <h4>Hello</h4>
    <p>Lorem ipsum dolor sit amet</p>
  </item>

  <item name="Worlditem">
    <h4>World</h4>
    <em>Mauris elementum elementum enim at suscipit.</em>
  </item>

</repeater>