20

I'm trying to get an app working with angular 1.5.0-beta.2

To make a 'directive' I have the following code:

myApp.component('gridshow', {
  bindings: {
    slides: '='
  },
  controller: function() {

  },
  controllerAs: 'grid',
  template: function ($element, $attrs) {
    // access to $element and $attrs
    return [
      '<div class="slidegrid">',
      '<div ng-repeat="slide in grid.slides">',
      '{{slide.image}}',
      '</div>',
      '</div>'
    ].join('')
  }
});

I like the idea of the template that returns a function with access to $element and $attrs but how do I combine this with a templateUrl?

tuvokki
  • 720
  • 1
  • 10
  • 18
  • 3
    You don't combine them, you either use one or another. – dfsq Nov 21 '15 at 09:47
  • I'm not surprised, but a little dissapointed. Thanks dfsq. – tuvokki Nov 21 '15 at 11:40
  • 1
    why disapointed. What would you want to do with both a template and a template url exactly ? That like asking for a stair inside an elevator... They're exclusive... – Pierre Gayvallet Nov 21 '15 at 14:29
  • Ehrm ... I never thought of it that way (nice analogy), just thought it would be nice to have the opportunity to have a template in a separate file _and_ mingle with the attr and element too ... – tuvokki Nov 21 '15 at 16:37

3 Answers3

25

In 1.5.0-beta.2 templateUrl can be a function that is invoked by injector. $element and $attrs are injected into both template and templateUrl functions in component, as well as any other dependencies.

This means that

  ...
  templateUrl: function ($element, $attrs) {
    // access to $element and $attrs
    ...
    return $attrs.uninterpolatedTemplateUrl;
  }

can be done instead.

georgeawg
  • 48,608
  • 13
  • 72
  • 95
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • 1
    And how does this work whit an external template? The templateUrl normally holds a reference to the template. – tuvokki Nov 21 '15 at 19:54
  • That's right. So the decision can be made on template url based on e.g. element attributes. What were you planned to use it for? If you expected to get template response in templateUrl function, then no, you still have to process a template later in the directive (we've got no link function in component, but there is controller). – Estus Flask Nov 21 '15 at 21:57
  • I understand, estus, and I had no real objective here. Just wondering. – tuvokki Nov 22 '15 at 16:53
  • 1
    How would I pass the binding elements into a templateUrl where templateUrl is a path to an external html file? – Jon Harding Jul 28 '16 at 18:54
  • @JonHarding Not sure what you mean. Please, create a question that would explain your case. – Estus Flask Jul 28 '16 at 18:55
  • @estus http://stackoverflow.com/questions/38645945/pass-bindings-to-templateurl-in-angulars-component – Jon Harding Jul 28 '16 at 20:15
  • @estus This works for static templateUrl. Dynamic interpolation does not work at all. Can i have a solution on this? – Mohan Singh Jan 29 '17 at 10:40
  • @MohanSingh No. Components aren't supposed to do that. Use existing solution for 'dynamic directives', there's a bunch of them, the most obvious and simple one is `ng-include`. – Estus Flask Jan 29 '17 at 10:49
9

I solved this problem by following technique. This may help you.

Template

<div data-ng-repeat="field in $ctrl.fields track by $index">
  <render-field data-field-type="{{field.type}}"></render-field>
</div>

A component

/**
 * @ngdoc Component
 * @name app.component.renderField
 * @module app
 *
 * @description
 * A component to render Field by type
 *
 * @author Mohan Singh ( gmail::mslogicmaster@gmail.com, skype :: mohan.singh42 )
 */
(function () {
  'use strict';

  angular
    .module('app')
    .component('renderField', {
      bindings: {
        fieldType: '@',
      },
      template: '<div ng-include="$ctrl.templateUrl">',
      controller: [
        function () {
          var $ctrl = this;
          $ctrl.$onInit = initialization;
          $ctrl.$onDestroy = onDestroy;
          $ctrl.$onChanges = onChanges;

          /**
           * public properties
           */
          /**
           * public methods
           */
          /**
           * @function
           * @name initialization
           * @description
           * A component's lifeCycle hook which is called after all the controllers on an element have been constructed and had their bindings initialized
           */
          function initialization() {
          }

          /**
           * @function
           * @name onChanges
           * @description
           * A component's lifeCycle hook which is called when bindings are updated.
           */
          function onChanges(bindings) {
            if(bindings.fieldType && bindings.fieldType.isFirstChange()){
              //$ctrl.fieldType['text' | 'textarea' | 'select' | 'radio']
              $ctrl.templateUrl = 'partials/fields/'+$ctrl.fieldType+'.html';
            }
          }
          /**
           * @function
           * @name onDestroy
           * @description
           * A component's lifeCycle hook which is called when is called on a controller when its containing scope is destroyed. 
           * Usefull to release external resources, watches and event handlers.
           */
          function onDestroy() { }
        }]
    });
})();
Mohan Singh
  • 883
  • 8
  • 18
  • 1
    Mate, that solution is excellent! I solved by having different routes, with the configurable value hardcoded in the HTML (as the scope is not available for binding). Your solution is much nicer. Wish I could up vote this more! – Daniel Mackay Feb 17 '17 at 03:44
  • @DanielMackay glad to help you. – Mohan Singh Sep 23 '17 at 13:26
8

@estus solution worked for me until I uglified my scripts. Uglified it gave the following error:

Error: [$injector:unpr] Unknown provider: eProvider <- e

The solution that worked for me is:

['$element', '$attrs', function($element, $attrs) {
    return $attrs.uninterpolatedTemplateUrl;
}]
Community
  • 1
  • 1
Backer
  • 1,094
  • 1
  • 20
  • 33
  • 3
    **All** functions in Angular that use DI should be annotated in order to be properly minified. I.e. almost every function in Angular API, with the exception of directive functions (`link`, `template`, etc). This is often omitted in examples because this is default action. – Estus Flask Jan 29 '17 at 10:55