2

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?

pridegsu
  • 1,108
  • 1
  • 12
  • 27

2 Answers2

1

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();
    }    
  });
Rathma
  • 1,196
  • 2
  • 33
  • 65
  • Thanks but I want to inject the directive's scope into the template – pridegsu Sep 23 '17 at 13:25
  • What do you mean inject scope into template? template is compiled against a scope, so based on the scope it is evaluated against your binding my or may not work. – Rathma Sep 23 '17 at 14:04
  • I mean that in your example you have the template passed by a binding compiled with the scope that I want in the var compileTemplate but what you are returning is the templateFactory – pridegsu Sep 23 '17 at 14:08
  • not sure why you need this, if you elaborate more on your problem maybe I can help you, but I guess you can turn that generic template into another directive and use `ng-switch` or `ng-if` in your component ... – Rathma Sep 23 '17 at 14:17
  • I need to build a component that receives a collection of items and a template. It'll iterate through all the items (with an ng-repeat) and it'll render the custom template for each one – pridegsu Sep 23 '17 at 14:21
  • if you change your original example to use `ng-bind-html` or its unsafe variant I guess you are good to go with your solution, but what comes to my mind is to have an item directive that render differently based on the template given to it. – Rathma Sep 23 '17 at 14:33
  • Could you share a working example? Because mine is not working – pridegsu Sep 23 '17 at 18:55
0

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>
lenilsondc
  • 9,590
  • 2
  • 25
  • 40
  • I want to inject into the template the directive's scope. You can see it in my example – pridegsu Sep 23 '17 at 13:22
  • @mattias ok, you'd just have to add the directive on your template `
    `
    – lenilsondc Sep 23 '17 at 20:51
  • I want to do it using a component instead of a directive – pridegsu Sep 23 '17 at 23:07
  • you can do whatever components do with directives, components are a more restrict type of directive, therefore you can't do compile with components so directives are the way to go. What exactly you want to do with components? – lenilsondc Sep 25 '17 at 04:33