Skip to content

Signal Through the Noise

Honest takes on code, AI, and what actually works

Menu
  • Home
  • My Story
  • Experience
  • Services
  • Contacts
Menu

AngularJS ngInclude directive and scope inheritance

Posted on November 26, 2014 by ivan.turkovic

ngInclude directive and scope

There are many times when you want to include a html snippet code from another file but preserve the scope of it. It is usually when you have different form fields for the various objects and you want to have a global controller that oversees the updating of different forms. So if you want to take the quickest route and use ngInclude directive you would be surprised that it is not properly linking to your controller and you cannot access the form instance.

This is due to ngInclude internals and how they work. ngInclude creates for each use as a new child scope so overwriting anything inside the new included HTML file content will be written into child scope and not in the one you’ve anticipated to be. So there are few workaround around this as creating a new object inside the scope for example

$scope.data = {}

inside the controlling controller and then in the imported html file set values inside the

<input type="text" ng-model="data.name"/>

This works if you don’t have a problem with static value being inserted into all html files, but if you want maximum flexibility then this is not the perfect solution. So after inspecting the source code inside ngInclude.js, I have seen a room for improvement and created a similar directive to ngInclude called ngInsert, which instead of making new child scope it inherits the current scope and continue using it inside. You can pick up the whole source code at this gist. You can use it in the same manner as existing ngInclude.

<ng-insert src="groupForm.html"></ng-insert>

I am also appending here the full source code for further reading.

(function(){
  'use strict';
 
  angular
    .module('app', [])
    .directive('ngInsert', ngInsert)
    .directive('ngInsert', ngInsertFillContentDirective);
 
  ngInsert = ['$templateRequest', '$anchorScroll', '$animate', '$sce'];
  function ngInsert($templateRequest, $anchorScroll, $animate, $sce) {
    return {
      restrict: 'ECA',
      priority: 400,
      terminal: true,
      transclude: 'element',
      controller: angular.noop,
      compile: function(element, attr) {
        var srcExp = attr.ngInsert || attr.src,
            onloadExp = attr.onload || '',
            preserveScope = attr.preserveScope || true,
            autoScrollExp = attr.autoscroll;
 
        return function(scope, $element, $attr, ctrl, $transclude) {
          var changeCounter = 0,
              currentScope,
              previousElement,
              currentElement;
 
          var cleanupLastInsertContent = function() {
            if (previousElement) {
              previousElement.remove();
              previousElement = null;
            }
            if (currentScope) {
              currentScope.$destroy();
              currentScope = null;
            }
            if (currentElement) {
              $animate.leave(currentElement).then(function() {
                previousElement = null;
              });
              previousElement = currentElement;
              currentElement = null;
            }
          };
 
          scope.$watch($sce.parseAsResourceUrl(srcExp), function ngIncludeWatchAction(src) {
            var afterAnimation = function() {
              if (angular.isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
                $anchorScroll();
              }
            };
            var thisChangeId = ++changeCounter;
 
            if (src) {
              $templateRequest(src, true).then(function(response) {
                if (thisChangeId !== changeCounter) return;
 
                var newScope = scope.$parent;
                if (!preserveScope)
                  newScope = scope.$new();
 
                ctrl.template = response;
 
                var clone = $transclude(newScope, function(clone) {
                  cleanupLastInsertContent();
                  $animate.enter(clone, null, $element).then(afterAnimation);
                });
 
                currentScope = newScope;
                currentElement = clone;
 
                currentScope.$emit('$insertContentLoaded', src);
                scope.$eval(onloadExp);
              }, function() {
                if (thisChangeId === changeCounter) {
                  cleanupLastInsertContent();
                  scope.$emit('$insertContentError', src);
                }
              });
              scope.$emit('$insertContentRequested', src);
            } else {
              cleanupLastInsertContent();
              ctrl.template = null;
            }
          });
        };
      }
    };
  }
 
  ngInsertFillContentDirective = ['$compile'];
  function ngInsertFillContentDirective($compile) {
    return {
      restrict: 'ECA',
      priority: -400,
      require: 'ngInsert',
      link: function(scope, $element, $attr, ctrl) {
        if (/SVG/.test($element[0].toString())) {
          // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
          // support innerHTML, so detect this here and try to generate the contents
          // specially.
          $element.empty();
          $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,
              function namespaceAdaptedClone(clone) {
            $element.append(clone);
          }, {futureParentElement: $element});
          return;
        }
 
        $element.html(ctrl.template);
        $compile($element.contents())(scope);
      }
    };
  }
 
})();

Recent Posts

  • The “Build It Yourself” Trap: How AI Enthusiasm Is Quietly Killing Core Product Development
  • When ADD Is Wrong: Recognizing the Limits
  • Software Companies Are Dead. Just Nobody Told Them.
  • ADD Meets TDD, BDD, and Agile: Combining Methodologies
  • ADD in Context: Greenfield, Legacy, Refactoring, and Testing

TOP 3% TALENT

Vetted by Hire me
  • Instagram
  • Facebook
  • GitHub
  • LinkedIn

Recent Comments

  • Prompt Patterns Catalog: Iteration, Verification, and Persona on Prompt Patterns Catalog: Decomposition, Exemplar, Constraint
  • Top AI Code Bugs: Semantic Errors, API Misuse, and Security Risks Unveiled – Trevor Hinson on Code Is for Humans, Not Machines: Why AI Will Not Make Syntax Obsolete
  • ADD: AI-Driven Development Methodology for Modern Engineers on The Future Engineer: What Software Development Looks Like When AI Handles the Code
  • The Future Engineer: Skills for AI-Era Software Development on Contact Me
  • A CTO Would Be Bored by Tuesday - Signal Through the Noise on Contact Me

Archives

  • February 2026
  • January 2026
  • December 2025
  • November 2025
  • October 2025
  • September 2025
  • August 2025
  • July 2025
  • May 2025
  • April 2025
  • March 2025
  • January 2021
  • April 2015
  • November 2014
  • October 2014
  • June 2014
  • April 2013
  • March 2013
  • February 2013
  • January 2013
  • April 2012
  • October 2011
  • September 2011
  • June 2011
  • December 2010

Categories

  • ADD Methodology
  • AI
  • AI development
  • AI-Driven Development
  • AngularJS
  • Artificial Intelligence
  • Behavior-Driven Development
  • Best Practices
  • blockchain
  • Business Strategy
  • Career Development
  • Code Integration
  • Code Review
  • Critical Thinking
  • development
  • Development Methodology
  • ebook
  • Introduction
  • leadership
  • Legacy Code
  • mac os
  • personal
  • personal development
  • presentation
  • productivity
  • Quality Assurance
  • Refactoring
  • Requirements
  • ruby
  • ruby on rails
  • sinatra
  • Software Development
  • Software Engineering
  • Software Testing
  • Specification
  • start
  • startup
  • success
  • Uncategorized

Meta

  • Log in
  • Entries feed
  • Comments feed
  • WordPress.org
© 2026 Signal Through the Noise | Powered by Superbs Personal Blog theme