AngularJS Includes

In modern web development, maintaining a single, massive HTML file is a nightmare for scalability. The ng-include directive in AngularJS solves this by allowing you to break your interface into smaller, manageable chunks. Think of it as the "Don't Repeat Yourself" (DRY) principle applied to your HTML. It dynamically fetches and embeds external HTML templates into your main view, making your code modular and much easier to debug.

Developer Tip: Use ng-include for elements that appear on multiple pages, like sidebars, navigation bars, or footers. This way, you only have to update the code in one place.

Key Concepts of AngularJS Includes

  1. ng-include: A built-in directive that acts as a placeholder where an external HTML file will be injected.
  2. Template URL: This is the path to your partial HTML file. Crucially, AngularJS treats this value as an expression, meaning it expects a string (wrapped in quotes) or a variable.
  3. Scope Sharing: By default, ng-include creates a new "child scope" that inherits from the parent controller’s scope, allowing the included file to access data from the main page.

 

How to Use ng-include

Basic Usage

To include a static file, you provide the file path as a string. Because ng-include evaluates the attribute value as an expression, you must wrap the filename in single quotes inside the double quotes.

Example:

<!-- Notice the single quotes inside the double quotes -->
<div ng-include="'header.html'"></div>

In this example, AngularJS makes an AJAX request to fetch header.html and renders its content directly inside the <div> tag.

Common Mistake: Forgetting the single quotes: ng-include="header.html". Without quotes, AngularJS looks for a variable named header.html in your controller instead of the actual file.

Using ng-include with a Dynamic URL

You aren't limited to hardcoded filenames. You can point the directive to a variable in your controller, allowing you to swap out parts of the UI on the fly.

Example:

<div ng-include="currentView"></div>

In your controller, you can change the view based on user actions:

app.controller('myCtrl', function($scope) {
  // Set the initial view
  $scope.currentView = 'dashboard-summary.html'; 

  // Function to switch views
  $scope.showSettings = function() {
    $scope.currentView = 'settings-panel.html';
  };
});
Best Practice: When using dynamic URLs, ensure your backend or file structure is organized. Grouping your templates in a /partials or /templates folder makes the paths easier to manage.

ng-include with Expressions

Since the directive accepts expressions, you can use logic like string concatenation to build paths dynamically. This is very common in dashboard applications where different widgets might be loaded from a specific directory.

Example:

<div ng-include="'views/modules/' + userRole + '-nav.html'"></div>

If userRole is 'admin', AngularJS will look for views/modules/admin-nav.html.

 

Conditional Template Inclusion

You can use ternary operators (conditional expressions) directly within the HTML attribute to decide which template to show based on a boolean value.

Example:

<div ng-include="isReadOnly ? 'viewMode.html' : 'editMode.html'"></div>

This approach is much cleaner than using multiple ng-if blocks with repeated ng-include directives.

Watch Out: Each ng-include triggers an HTTP request. If you have many includes on a single page, it can slow down the initial load. Consider using a tool like ng-template or a build task to cache these templates into the $templateCache.

 

Sharing Scope Between Parent and Included Template

One of the most powerful features is that the included HTML "lives" within the scope of the parent controller. You don't need to pass data manually; the template can see everything the parent sees.

Example:

Main Template:

<div ng-controller="myCtrl">
    <div ng-include="'header.html'"></div>
</div>

header.html Template:

<header>
    <h1>{{ pageTitle }}</h1>
    <p>Welcome, {{ user.name }}</p>
</header>

Controller:

app.controller('myCtrl', function($scope) {
  $scope.pageTitle = "Dashboard";
  $scope.user = { name: "Alex" };
});
Developer Tip: While ng-include shares the parent scope, it actually creates a child scope. If you try to update a primitive (like a string) inside the template using ng-model, it might not update the parent. Always use objects (e.g., data.name instead of just name) to ensure two-way binding works correctly.

 

Using ng-include with ng-repeat

You can combine ng-include with ng-repeat to render complex lists where each item requires its own template structure.

Example:

<div ng-repeat="contact in contactList">
  <div ng-include="'contactCard.html'"></div>
</div>

Inside contactCard.html, you can simply use {{ contact.name }} to display data for that specific iteration.

 

Handling Events with ng-include

Sometimes you need to know when a template has finished loading or if it failed (e.g., a 404 error). While ng-include has an onload attribute for success, errors are typically handled via AngularJS events.

Example:

<div ng-include="'header.html'" onload="finishedLoading()"></div>

To handle errors globally or specifically, you can listen for the $includeContentError event in your controller:

$scope.$on('$includeContentError', function(event, src) {
    console.log('Failed to load template at:', src);
    $scope.errorMessage = "We couldn't load the content. Please refresh.";
});

 

Summary

The ng-include directive is a fundamental tool for keeping your AngularJS projects organized. By separating your UI into reusable HTML files, you make your application easier to maintain and test. Whether you are using static files for a layout or dynamic expressions for a complex dashboard, ng-include provides a seamless way to inject content while maintaining data connectivity through scope inheritance. Just remember to watch your file paths and consider performance caching as your app grows!