2

I have the following simple base directive:

angular.module("base", [])
  .directive("base", function() {
    return {
      restrict: "A",
      scope: true,
      controller: function($scope) {
        this.setHeader = function(header) {
          $scope.header = header;
        }
        this.setBody = function(body) {
          $scope.body = body;
        }
        this.setFooter = function(footer) {
          $scope.footer = footer;
        }
      },
      templateUrl: "base.html"
    }
  });

I am passing data to this directive in the following way:

 .directive("custom", function() {
    return {
      restrict: "E",
      require: "^base",
      scope: {
        ngModel: "="
      },
      link: function($scope, $element, $attrs, baseCtrl) {
        //Do something with the data or not...
        baseCtrl.setHeader($scope.ngModel.header);
        baseCtrl.setBody($scope.ngModel.body);
        baseCtrl.setFooter($scope.ngModel.footer);
      }
    }
  });

When I create a list of my custom directives, I notice the custom directives aren't rendering immediately. I have made a Plunker demonstrating this behavior. (You will see the list cells empty for a split second, then the directives will appear)

My goal behind this design is to reuse the template of the base directive and only pass in the data needed for display. In this simple example, $scope.data is exactly what I need to pass in but it may be the case some rules or manipulation need to happen first. Rather than have the controller which queried the data handle this, I wanted to pass it off into the directive, separating the concerns.

So my questions are:

  1. Is there any way to make the directives render faster and avoid the flickering shown in the Plunker?
  2. Is this a best practice for reusing directives with Angular?

1 Answer 1

4

The flickering is being caused by the async http request to the "base.html" file. Since the HTML for the base directive has to be loaded from the server, there will be a fraction of time where no content will be displayed.

In order to render the data, Angular will go though these 3 stages:

  1. Fetch the HTML file/template from the server (no content will be displayed)
  2. Compile the HTML template (the DOM is updated but the scope isn't yet linked)
  3. Link the scope to the DOM/template (expected data is displayed)

Option 1 - Use the template attribute

Just replace the templateUrl: "base.html" for the direct HTML content:

//templateUrl: "base.html"
template: '<div class="base"><div class="header bottom-border"><h2>{{header}}</h2><div><div class="body bottom-border"><p>{{body}}</p></div><div class="footer">{{footer}}</div></div>',

You will notice that there won't be any flickering this time (check this plunker).

Option 2 - Pre-load template files

Angular has a built-in template cache ($templateCache) that it uses to check if any HTML template file/content has already been fetched from the server or not. If you populate that cache while the app is loading then Angular will not need to fetch the template to render the directive, it will read it directly from the cache.

You can use Angular's $templateRequest (if you are using the latest beta version of Angular) or $templateCache (for any version). The difference is that $templateRequest automatically makes the HTTP GET request and stores the result in the $templateCache. On the other one you will have to do it manually, like this:

loadTemplate = function(tpl) {
  $http.get(tpl, { cache : $templateCache })
    .then(function(response) {
      $templateCache.put(tpl, html);
      return html;
    });
};
loadTemplate('base.html');

Note however that this approach needs your app to have a "loading phase".


Regarding the best practices for reusing directives, you seem to be on the right path. The example is to simple to give any advices... Nevertheless, check the "Best practice" notes in this Angular "Creating Custom Directives" guide.

Edit

(my personal preferences regarding template vs templateUrl)

If the main goal is solely performance (i.e. page render speed) then template seems to be the best choice. Fetching one file will always be faster than fetching two... However, as the app grows, the need for a good structure is mandatory, and template files are one of the best practices when it comes to that.

Normally I follow this "rules":

  1. If there are only a few lines of HTML in the template, then just use the template
  2. If the HTML template will not be constantly changing over time (i.e. post structure, contact details, etc...), use template, otherwise (i.e. templates containing banners/ads) use templateUrl
  3. If the app has a loading phase, use templateUrl with cache
Sign up to request clarification or add additional context in comments.

4 Comments

Understood. I appreciate your time and the excellent answer. I took for granted using templateUrl was an async call since usually, it never creates a problem. Does this mean it's best to always use template to prevent this from ever happening? What approach do you personally use?
Edited my answer with my personal preferences. Hope it helps :)
As well you can use a build time task to compile your html templates github.com/karlgoldstein/grunt-html2js
@Martin that's also a good idea. Ultimately it will end up being the same as using template and storing the content on another js file.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.