Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Dynamic templateUrl in directives #1039

Closed

Description

@coli

I'd like to be able to have templateUrl be configurable

eg

<my-directive templateUrl="hard-coded-path'>

Right now, this can be done via compile()

        compile:function(tElement, tAttrs, transclude) {
            var html = $templateCache.get(tAttrs.templateUrl);
            tElement.html(html);
        },

Problem is this only works for inline template, I'd like to be able to fetch it via network if it is not available yet.

Ideally, the delayed compile function associated with templateUrl processing in Angular should be exposed to directive writers.

Very ideally, I'd like to be able to

<my-directive templateUrl="{{scope.templateUrl}}">

Activity

  1. IgorMinar commented on Jun 12, 2012

    @IgorMinar
    Contributor

    not quite what you want but this will work:

    myApp.directive('foo', function($http, $templateCache) {
      return {
        compile: function(tElement, tAtrrs) {
          $http.get(tAttrs.templateUrl, {cache: $templateCache}).success(function(html) {
            t.Element.html(html);
          });
        }
      }
    });
    

    the problem here is that the template is not compiled. to compile it you'd have to inject $compile service, compile the fetched html template. To link the compiled template with a scope, you'd have to return a linking fn from the compile fn that will keep a reference to the scope. Yeah, it's a lot of work, but it's doable. :-)

    we'll consider this RFE post 1.0

  2. dcu commented on Jul 21, 2012

    @dcu

    +1. many people is asking this feature so what are the main reasons to not implement it?

  3. casio commented on Oct 9, 2012

    @casio

    +1

  4. abdulazeezsk commented on Oct 9, 2012

    @abdulazeezsk

    +1

  5. olostan commented on Oct 31, 2012

    @olostan
    Contributor

    Did someone completed solution @IgorMinar suggested?

    I have similar problem: I have a directive, which content depends on some data, that should be loaded from resource (full problem is: I what validations/presentations of model elements loaded from backend. So I'll have kind of and type of field is loaded from backend, and only after it is loaded I can decide which template to compile...

    For not the only way how to achieve that is try to implement idea, mentioned at @IgorMinar's comment.

  6. teintinu commented on Nov 8, 2012

    @teintinu

    Hi Coli, maybe my fork help you: https://github.com/josefernandotolentino/angular.js

    Use can use parameters in route templateUrl:

    'use strict';
    
    
    // Declare app level module which depends on filters, and services
    angular.module('myApp', ['myApp.filters', 'myApp.services', 'myApp.directives']).
      config(['$routeProvider', function($routeProvider) {
    
        $routeProvider.when('/view/:id', {templateUrl: 'partials/partial{{id}}.html', controller: MyCtrl2});
    
        $routeProvider.otherwise({redirectTo: '/view/1'});
      }]);
    
  7. pkozlowski-opensource commented on Nov 8, 2012

    @pkozlowski-opensource
    Member

    @josefernandotolentino you are talking about different templateUrl - the original issue was opened for directives while you are talking about templateUrl being used in a route definition...

  8. teintinu commented on Nov 9, 2012

    @teintinu

    ok pkoz, for this reason I said "maybe".

  9. etchemkay commented on Nov 24, 2012

    @etchemkay

    Based on @IgorMinar 's comments above, I was able to quickly able to come up with a working solution. Now, all I want to know is whether this is the "right" solution, in terms of efficiency/correctness. Any "gotcha"s that I may have overlooked ?

    Off-Topic: By the way, I just went from "heard of AngularJS for the first time" to "learning AngularJS" to "single-handedly fully re-wrote a moderately complex mobile application" in 3 weeks time (which took a team of three 2.5 months to do in jQMobile & Mustache) ! That says a lot for just how fantastic AngularJS is. Big fan right here.

    Here's the code. It should be simple enough to imagine the rest of the (controller and template) code -

    // activity items can be of several types .. post-photo, status-change, etc. (think facebook activity stream)
    App.directive('activityItem', ['$compile', '$http', '$templateCache', function($compile, $http, $templateCache) {
      var templateText, templateLoader,
        baseURL = 'partial/tpl/',
        typeTemplateMapping = {
          photo:    'photo-activity-item.html',
          status: 'status-activity-item.html'
        };
    
      return {
        restrict: 'E',
        replace: true,
        transclude: true,
        scope: {
          type: '@type', title: '=', authorName: '=', avatar: '=', timeAgo: '=',
          statsLikes: '=', statsViews: '=' // some basic stats for your post
        },
        compile: function(tElement, tAttrs) {
          var tplURL = baseURL + typeTemplateMapping[tAttrs.type];
          templateLoader = $http.get(tplURL, {cache: $templateCache})
            .success(function(html) {
              tElement.html(html);
            });
    
          return function (scope, element, attrs) {
            templateLoader.then(function (templateText) {
              element.html($compile(tElement.html())(scope));
            });
          };
        }
      };
    }])
    <!-- simple bootstrap list for, say, all photo posts (hence the hardcoded @type) -->
    <ul class="nav nav-list">
      <li ng-repeat="post in posts">
        <activity-item
          type="photo"
          title="post.postTitle"
          author-name="post.creatorName"
          time-ago="post.formattedCreatedTimestamp"
          stats-likes="post.likeCount" stats-views="post.viewsCount"
          stats-comments="post.threadCount"
          avatar="post.creatorAvatarPath" >
        </activity-item>
      </li>
    </ul>
  10. tkrotoff commented on Nov 24, 2012

    @tkrotoff

    @etchemkay I use a similar method to re-use a form (so no copy-paste) for editing and creating (CRUD), in this context it is editing and creating a calendar event:

    app.directive('eventForm', function() {
      return {
        restrict: 'E',
        replace: true,
        templateUrl: '_form.html',
        scope: {
          event: '=' // event attribute is binded to the event object of the directive
        }
      };
    });

    Note the use of templateUrl inside the directive so you don't have to use $http.get.

    <!-- _form.html -->
    <input type="text" ng-model="event.title">
    <textarea ng-model="event.description"></textarea>
    [...]
    <h1>Create new calendar event</h1>
    <form>
      <event-form event="event"></event-form>
      [...]
    </form>
    <h1>Edit existing calendar event</h1>
    <form>
      <event-form event="event"></event-form>
      [...]
    </form>

    For sure the use of ng-include would be better:

    <h1>Create new calendar event</h1>
    <form>
      <ng-include src="'_form.html'"></ng-include>
      [...]
    </form>

    Ideally I would prefer to use directives for JavaScript code and re-use HTML code through ng-include.

  11. jgrund commented on Dec 14, 2012

    @jgrund
    Contributor

    +1

  12. ArturGajowy commented on Dec 17, 2012

    @ArturGajowy

    +1

  13. ryanzec commented on Dec 24, 2012

    @ryanzec

    +1

  14. vizo commented on Jan 6, 2013

    @vizo

    +1

  15. lrlopez commented on Jan 20, 2013

    @lrlopez
    Contributor

    I've proposed an implementation of dynamic templates using functions in #1849. Take a look, may be it fits your needs.

  16. 20 remaining items

  17. gregtzar commented on Aug 30, 2013

    @gregtzar

    @coli As far as I know there is still no way to bind the templateUrl to scope. Which is probably what a lot of people (including myself) were +ing it for.

  18. coli commented on Aug 30, 2013

    @coli
    Author

    Open a new issue please :) this one is long closed.

  19. willemmulder commented on Oct 3, 2013

    @willemmulder

    As a last note, the ideal declarative way that the first poster describes, is certainly possible!

    Only, instead of doing

    <div {{directiveNameInScope}}></div>

    You can do

    <div loaddirective="directiveNameInScope"></div>

    Using this simple directive here: https://github.com/willemmulder/loaddirective

    Happy coding!

  20. kumarvishal commented on Oct 30, 2013

    @kumarvishal

    +1

  21. Rush commented on Oct 31, 2013

    @Rush

    I am considering using AngularJS and would like to swap its HTTP template fetching for something else:

    function myTemplateProvider(templateUrl, locals, callback) {
       getTemplateAsynchronously(templateUrl, function(compiledTemplate) {
           callback(compiledTemplate, locals);
       });
    }
    

    Is something like this possible? How to provide my own provider for the HTML data. Sorry if it is the wrong place to ask, but it looks closest to what I could find.

  22. unluckypixie commented on Dec 19, 2013

    @unluckypixie

    No-one seems to have provided an example of the new way to do this so here is a clock directive I wrote using using an optional "template-url" attribute (uses '/app/partials/clock.html' if no attribute is set).

    This works in Angular 1.2.4:

    Directive:

    Directives.directive('clock', function() {
        return {
            restrict: 'E',
            replace: true,
            scope: {
                // Private scope so that the 1 second tick doesn't trigger all scope digest functions.
            },
            templateUrl: function(element, attr) { return attr.templateUrl ? attr.templateUrl : '/app/partials/clock.html' },
            controller: function($scope, $timeout) {
                $scope.$watch(function() { return new Date().getMinutes(); }, function (newValue, oldValue) {
                    $scope.date = Date.now();
                }, true);
    
                setInterval(function(){
                    $scope.$digest();
                }, 1000);
            }
        }
    });
    

    Example usage:

    <clock template-url="/app/partials/footer-clock.html" />
    

    The partial (/app/partials/footer-clock.html):

    <span class="footer-clock">
        <span date-time> {{date | date: "MMMM"}} </span>
        <span date-time> <span class="bold"> {{date | date: "dd"}} </span> </span>
        <span date-time> | {{date | date: "h:mm"}} </span>
        <span date-time> <span class="bold"> {{date | date : "a" | lowercase}} </span> </span> 
    </span>
    
  23. geoidesic commented on Mar 26, 2014

    @geoidesic

    Something that isn't mentioned above is that templateUrl makes an XHR request with Accept headers set to "application / json", this isn't great because it's telling the server that it wants json back when really it wants HTML back.

    Is there a way to get templateUrl to set Accept headers to "text/html"?

  24. reyraa commented on Dec 14, 2014

    @reyraa

    ngInclude implements all $compile functionality itself, I think we can use it inside directive:

    <my-directive ng-include="element.template" config="{element.config}"></my-directive>

    template is dynamic and configs come as object. I've used this method for a popup with 4 different templates in a project.

  25. shhtephe commented on Apr 14, 2015

    @shhtephe

    @TR0L, I'm a newbie trying to learn SPA stuff and you saved me so much trouble with your solution! Thank you so much!

  26. praveen1228 commented on Nov 6, 2015

    @praveen1228

    I have a Question, am new to angularJS," Here i have a directive inside having a template, in the template i was using a templateUrl for rendering the another template', Whats My issue is i Want access the form data inside the template which is called by templateUrl",..Your Answer made my day Thanks Praveen

  27. offwork commented on Jan 13, 2016

    @offwork

    Why are given continuously example of the link:! None of these examples does not work when the templateUrl: is used. The templateUrl: for the templates is a complex and long code is required.

  28. reyraa commented on Jan 17, 2016

    @reyraa

    There is a clean way for this:

    assuming app is the angular module:

    app.directive('theDirective', function () {
        return {
            templateUrl: function ($element, $attrs) {
                  return $attrs.templateUrl || 'some/path/default.html'
            },
           link: function () {
               // here is the handler
           }
        }
    });
    
  29. pankajanupam commented on Jun 23, 2016

    @pankajanupam

    +1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions