How to pass a template via bindings to an AngularJS Component

3k Views Asked by At

I'd like to pass a custom template, via bindings, into an AngularJS Component and render it using his scope. Something like this (pseudo-code, this is not working):

angular
  .module('myApp', [])
  .controller('mainController', ($scope) => {

    $scope.getTemplate = () => (`
      <div>
        <span>{{$ctrl.name}}</span>
      </div>
    `)
  })
  .component('myComponent', {
    controller: ($scope, $compile) => {
      const $ctrl = $scope.$ctrl;

      $ctrl.$onInit = () => {
        $ctrl.name = 'Hello World!';
      };

      $ctrl.compileTemplate = () => $compile($ctrl.template())($scope);
    },
    bindings: {
      template: '&'
    },
    template: `
      <div>
        My dynamic content: {{$ctrl.compileTemplate()}}
      </div>
  `
  });

Usage:

<div ng-controller="mainController as $ctrl">
  <my-component template="$ctrl.getTemplate()"></my-component>
</div>

Expected Result:

<div>
  My custom content:
  <div>
    <span>Hello World!</span>
  </div>
</div>

Is there any way to do it?

2

There are 2 best solutions below

7
On

In case you want dynamic template, You can utilize the fact that you can pass a function to component template, in components that function is injectable, I refer you to this question for more info, but here is the main idea:

angular
  .module('myApp', [])
  .factory('tempalteFactory', {
      return getTemplate() {
          retrun '<b> yep </b>';
      }
  })
  .component('myComponent', {
    controller: ($scope, $compile) => {
      const $ctrl = $scope.$ctrl;

      $ctrl.$onInit = () => {
        $ctrl.name = 'Hello World!';
      };   

    },
    template: function($element, $attrs, templateFactory) {
          'ngInject';

          return templateFactory();
    }    
  });
4
On

You can use the $compile service to create a directive to handle the DOM manipulation involved.

The following working snippet implements an attribute directive compile which compiles the input value of the attribute in the controller scope. It basically take your template and add it to the inner content of the element to which the directive is attached and finally compiles it.

angular.module('app', [])
  .run(($rootScope) => {  
    $rootScope.name = 'world';    
    $rootScope.template = `
      <label>Name</label>
      <input type="text" ng-model="name">
      <h2>Hello {{ name }}!</h2>
    `;
  })
  .directive('compile', ($compile) => {
    return (scope, element, attrs) => {
      scope.$watch(
        scope => scope.$eval(attrs.compile),
        value => {
          element.html(value);
          $compile(element.contents())(scope);
        }
      );
    };
  })
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.5/angular.js"></script>
<div ng-app="app">
  <div compile="template"></div>
</div>