Search code examples
angularjsangularjs-directiveangularjs-serviceangularjs-controllerangularjs-injector

Can one inject a directive $compiler into a service?


I'm trying to output a directive as a javascript string so that I can place the html in a code display window. I'm interested in the actual code rather than the browser rendered version because I'm display code to a power user. So I would like to be able to inject a directive function into a service or possibly a page controller.

This would be similar to using $compile or perhaps even $interpolate, but for a specific directive. I figure I've already defined the directive, there's a decent chance I can access the html generation function somehow. I know you can define a controller inside the directive definition, but I'm looking for a solution that I use within a service or page controller.

So, as an example, say I have a directive defined within a module

mod.directive("superThing", function() {
        return {
            templateUrl: "/superThing.html",
            scope: {
               variableA: "="
            }
        };
    });

Ex service:

 mod.service("applicationService", [ "$rootScope", "superThing",
        function ($rootScope, superThing) {
          $rootScope.result = superThing($rootScope);
       }
]);

(I know using $rootScope like this is weird but I'm just trying to come up with a short succient example.)

Example page template:

<fieldset>
  <legend>Preview:</legend>
  <div data-super-thing data-variable-a="false">
  </div>
</fieldset>
<fieldset>
   <legend>Code output:</legend>
   <textarea rows="4" cols="50" data-code-mirror="{{result}}">
   </textarea>
</fieldset>

Is there someway to inject the internal $compiled version of a directive, or similar, into a service?


Solution

  • Depending on what you need, I can offer 4 options:

    • Creating directives manually using the service.
    • Creating a directive, which will record your html in variable.
    • Creating a common directive, which will report via the event $emit report their contents html.
    • Creating a common directive, which will record your html in variable (This is the preferred solution, I think.)

    Live example on jsfiddle.

    angular.module('ExampleApp', [])
      .controller('ExampleController', function($scope, compileDirective) {
        $scope.valueShow = 1234567;
        $scope.array = [1, 23, 4, 5, 9, 6];
        
        $scope.addInArray = function(){
          $scope.array.push(Math.random());
        }
        
        var elem = compileDirective.get(myDirective, {
          val: 1234
        });
        console.log('Compiled by Service', elem);
    
    
        $scope.$on('show-compile.html-changed', function(event, value) {
          console.log('html from show-cimpile', value);
        });
    
    
      })
      .service('compileDirective', function($compile, $rootScope) {
        return {
          get: function(directiveFn, scope) {
            var directive = myDirective();
            directive.scope = $rootScope.$new(false);
            for (var k in scope)
              directive.scope[k] = scope[k];
            return $compile(directive.template)(directive.scope);
          }
        };
      })
      .directive('myDirective', myDirective)
      .directive('showCompileEvent', function() {
        return {
          restrict: "A",
          link: function(scope, elem) {
            scope.$watch(function() {
              return elem.html();
            }, function(val) {
              scope.$emit('show-compile.html-changed', val);
            });
          }
        };
      }) 
      .directive('showCompile', function() {
        return {
          restrict: "A",
          scope:{showCompile:"="},
          link: function(scope, elem) {
            scope.$watch(function() {
              return elem.html();
            }, function(val) {
              scope.showCompile = val;
            });
          }
        };
      })
      .directive('myDirectives', function() {
        return {
          restrict: "EA",
          replace: true,
          scope: {
            val: "=",
            htmlVal:"="
          },
          template: "<div><b>{{val}}</b></div>",
          link: function(scope, elem) {
            scope.$watch(function() {
              return elem.html();
            }, function(val) {
              scope.$emit('my-directive.html-changed', val);
              scope.htmlVal = val;
            });
          }
        };
      });
    
    function myDirective() {
      return {
        restrict: "EA",
        replace: true,
        scope: {
          val: "="
        },
        template: "<div>{{val}}</div>",
      };
    
    }
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
    <div ng-app="ExampleApp">
      <div ng-controller="ExampleController">
        <my-directive val="valueShow"></my-directive>
        <my-directives val="valueShow" html-val="selfHTML"></my-directives>
        Html for my-directives <pre>{{selfHTML}}</pre>
        <my-directive show-compile-event val="valueShow"></my-directive>
        <button ng-click="addInArray()">
         add in array
        </button>
        <div show-compile="htmlFromNgRepeat">
          <div ng-repeat="a in array">
            {{a}}
          </div>
        </div>
        <pre>{{htmlFromNgRepeat}}</pre>
      </div>
    </div>