Difficulty with rendering speed when reusing a directive in AngularJS

Here is a basic directive called "base":

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 like this:

 .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);
      }
    }
  });

However, when I create a list of my custom directives, there is a slight delay before they render. You can see this behavior in the Plunker example. (The list cells initially appear empty before displaying the directives)

The idea behind this approach is to reuse the template of the base directive and only pass in the necessary display data. While $scope.data suffices in this case, there may be instances where some preprocessing is required. Instead of burdening the data-querying controller with this task, I prefer to delegate it to the directive, for cleaner separation of concerns.

This raises two questions:

  1. Is there a way to speed up directive rendering and eliminate the flickering as seen in the example?
  2. Is this methodology considered a best practice for reusing directives in Angular?

Answer №1

The issue of flickering on the page is a result of the asynchronous http request to retrieve the "base.html" file. When Angular needs to load the HTML for the base directive from the server, there will be a brief period where no content is displayed.

Angular goes through 3 stages to render the data:

  1. Fetching the HTML file/template from the server (no content displayed during this stage)
  2. Compiling the HTML template (updating the DOM without linking to the scope yet)
  3. Linking the scope to the DOM/template (displaying the expected data)

Option 1 - Utilize the template attribute

To eliminate the flickering effect, replace templateUrl: "base.html" with 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 observe that there is no flickering this time (see this example).

Option 2 - Pre-load template files

Angular provides a built-in template cache ($templateCache) that can store previously fetched HTML templates. By populating this cache while the app loads, Angular can directly read the template instead of fetching it again to render the directive.

You can utilize Angular's $templateRequest (for the latest beta version) or $templateCache (for any version). The difference lies in $templateRequest automatically performing the HTTP GET request and storing the result in $templateCache, while with the other option, you need to manually do so:

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

Note that this approach requires your app to have a distinct "loading phase".


In terms of best practices for reusing directives, it seems like you are following the right path. Given the simplicity of the example provided, it is hard to offer specific advice... However, refer to the "Best practice" guidelines mentioned in this Angular "Creating Custom Directives" guide.

Edit

(my personal preferences regarding template vs templateUrl)

If the primary aim is performance (faster page rendering), then using template appears to be the optimal choice. Fetching one file is always faster than fetching two... However, as the app expands, having a sound structure becomes essential, and template files align well with this principle.

Typically, I adhere to these principles:

  1. For small chunks of HTML in the template, opt for template
  2. If the HTML template remains relatively static over time (e.g., post structure, contact details), use template; if dynamic (e.g., banners/ads), prefer templateUrl
  3. When the app includes a "loading phase", leverage templateUrl with caching

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Display the closing tag depending on specific conditions to simulate the behavior of a calendar

Is it possible to conditionally render a closing tag using v-if? If so, what is the correct way to do it? Initially, I tried the following: <template v-for="day in days"> <td class="days">{{day.number}}</td> <template v-if ...

Every time I refresh a Next.js page, I find myself back on the index.js

I find it confusing that whenever I refresh a page in my Next.js app, it always redirects me back to the index page. My e-commerce single-page application (SPA) has a catalogue page in index.js and the products are displayed using the dynamic [name].js pag ...

Create a polling feature using a Grease Monkey script

I am looking for a way to run a Tamper Monkey script on a Facebook page that regularly checks a database for new data and performs certain actions. I have attempted to implement polling using AJAX, and below is the code I used: (function poll() { setT ...

What is the best way to isolate particular components of an argument and store them in separate variables?

Currently, I am facing a challenge in extracting the name and id of an emoji from a discord argument using discord.js. The input provided to me is <:hack_wump:670702611627769876>, and my goal is to retrieve var id = '670702611627769876' alo ...

Creating a dynamic image binding feature in Angular 8

I am working with an object array that requires me to dynamically add an image icon based on the type of credit card. Typescript file icon: any; savedCreditCard = [ { cardExpiryDateFormat: "05/xx", cardNumberLast: "00xx", cardId: "x ...

How can I create a jumping animation effect for my <li> element in JQuery?

I have a horizontal navigation bar with a "JOIN" option. My goal is to make the #jump item continuously jump up and down after the page has finished loading. Currently, I have this code which only triggers the jump effect once when hovering over the eleme ...

Ways to include multiple pieces of data in a JQuery Mobile List view

Obtaining JSON data (list of available Hotels within a certain distance) and extracting the following information in JSON format: Name of Hotels, Distance of Hotel from our current location, number of rooms. There might be multiple Hotels within the specif ...

In strict mode, duplicate data properties are not allowed in object literals when using grunt-connect-proxy

I recently started following a tutorial on AngularJS titled "Hands on Angularjs" by Tutsplus. In the tutorial, the instructor uses the grunt-connect-proxy package to redirect ajax requests to a Rails server running on localhost:3000. However, I believe the ...

Updating CSS when input field value changes using jQuery

var emailFooter = $('footer#footer input.email'); var emailFooterLength = emailFooter.val().length; var hiddenCaptcha = $("footer#footer .captcha-hide"); emailFooter.focus( function() { hiddenCaptcha.css("disp ...

The Magic of Javascript Routing with Regex Implementation

I'm currently developing a Javascript Router similar to Backbone, Sammy, and Spin. However, my specific requirements are rather straightforward. I need the ability to define a series of routes along with their corresponding callbacks, and I want to be ...

Transform nested properties of an object into a new data type

I created a versatile function that recursively converts nested property values into numbers: type CastToNumber<T> = T extends string ? number : { [K in keyof T]: CastToNumber<T[K]> }; type StringMap = { [key: string]: any }; const castOb ...

Implemented a deletion feature in the list, however, certain items that have been checked are not being removed

I am currently participating in the Wes Boros JS 30 challenge, where we created a list of our favorite foods. As part of the challenge, we were tasked with implementing a 'select all' function, an 'unselect all' function, and a 'de ...

ESLint detecting error with returning values in async arrow functions

Currently facing a minor inconvenience instead of a major problem. Here is the code snippet causing the issue: export const getLoginSession = async (req: NextApiRequest): Promise<undefined | User> => { const token = getTokenCookie(req) if (!t ...

What sets apart .create from .save in mongoose?

A while ago, I completed a bootcamp course on Express and Mongoose on Udemy. In the course, we learned how to add new fields to data by writing code like this: var playground = require("../models/playground.js"); route.post("/", middleware.isLoggedIn,fun ...

Sending data from frontend to backend in a Node.js Express application

My Node.js Express project in the WebStorm IDE is structured like this: https://i.sstatic.net/pQKwb.jpg I'm trying to find a way to pass a variable from index.js to app.js so that I can write it to data.json at the end. As a beginner in Node.js, I& ...

What sets apart importing crypto from 'crypto' as opposed to simply using crypto in Node.js?

For my upcoming Node project, I'm considering using crypto to obtain a UUID. After some research, I discovered that I can achieve this by utilizing crypto and that it is already a declared variable without the need for importing it. So there are two o ...

Automating the Process of File Downloads Using Ajax and PHP

I am currently working on a form that requires users to enter a secret key. Once the key is submitted, it is verified against a database. If the key is found, the relevant file associated with the key should be downloaded automatically. I have successfully ...

Utilizing JS Underscore for combining and organizing arrays with common keys

I am facing a scenario where I have two arrays: "array_one": [ { "id": 1, "name": One }, { "id": 2, "name": Two } ] "array_two": [ { "id": 1, "name": Uno }, { "id": 3, "name": Three ...

Show or hide a div in Vuejs based on checkbox selection

I've been attempting to toggle the visibility of a container div using Vuejs with no success. I've tried two different methods, but neither seem to work for me. Here is Method 1: <body> <div class="checkbox" id = "selector"& ...

What is the best time to fetch the height of an element containing an image?

I am working on my web app and I want to implement a popup element that contains an <img> element. Typically, the image source is larger than necessary, so I resize it using CSS. However, before displaying the popup, I need to determine its outerHeig ...