diff --git a/docs/content/guide/directive.ngdoc b/docs/content/guide/directive.ngdoc index da1dc6df9ab7..c5fc5cf86a24 100644 --- a/docs/content/guide/directive.ngdoc +++ b/docs/content/guide/directive.ngdoc @@ -380,10 +380,14 @@ compiler}. The attributes are: * `template` - replace the current element with the contents of the HTML. The replacement process migrates all of the attributes / classes from the old element to the new one. See the {@link guide/directive#Components Creating Components} section below for more information. + If the template is a function, it will be called with the current elementa normalized list of + attributes (see {@link guide/directive#Attributes Attributes}). The content returned by the + call will be the template to be compiled. * `templateUrl` - Same as `template` but the template is loaded from the specified URL. Because the template loading is asynchronous the compilation/linking is suspended until the template - is loaded. + is loaded. You can also use a function that will return the URL to load. The call will include + the current element and the normalized list of attributes as a single parameter. * `replace` - if set to `true` then the template will replace the current element, rather than append the template to the element. diff --git a/src/ng/compile.js b/src/ng/compile.js index 48beb61ef51b..7beb92c77dba 100644 --- a/src/ng/compile.js +++ b/src/ng/compile.js @@ -649,6 +649,9 @@ function $CompileProvider($provide) { if ((directiveValue = directive.template)) { assertNoDuplicate('template', templateDirective, directive, $compileNode); templateDirective = directive; + if (isFunction(directive.template)) { + directiveValue = directive.template($compileNode, templateAttrs); + } directiveValue = denormalizeTemplate(directiveValue); if (directive.replace) { @@ -971,11 +974,19 @@ function $CompileProvider($provide) { // The fact that we have to copy and patch the directive seems wrong! derivedSyncDirective = extend({}, origAsyncDirective, { controller: null, templateUrl: null, transclude: null, scope: null - }); + }), + urlToLoad; $compileNode.html(''); - $http.get(origAsyncDirective.templateUrl, {cache: $templateCache}). + if (isFunction(origAsyncDirective.templateUrl)) { + urlToLoad = origAsyncDirective.templateUrl($compileNode, tAttrs); + } + else { + urlToLoad = origAsyncDirective.templateUrl; + } + + $http.get(urlToLoad, {cache: $templateCache}). success(function(content) { var compileNode, tempTemplateAttrs, $template; diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index b9ed3ff9d8e6..1291a4e01564 100644 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -647,6 +647,49 @@ describe('$compile', function() { }); + describe('template function', function() { + + beforeEach(module(function() { + directive('replace', valueFn({ + replace: true, + template: function(e,f) { + return '
Replace!
' + }, + compile: function(element, attr) { + attr.$set('compiled', 'COMPILED'); + expect(element).toBe(attr.$$element); + } + })); + + directive('replaceattr', valueFn({ + replace: true, + template: function(e, f) { + expect(e.text()).toBe('ignore'); + return '
Replace!
' + }, + compile: function(element, attr) { + expect(element.text()).toBe('Replace!'); + expect(attr.dynamic).toBe('123'); + attr.$set('dynamic', '456'); + } + })); + })); + + + it('should replace element with template returned by function', inject(function($compile, $rootScope) { + element = $compile('
ignore
')($rootScope); + expect(element.text()).toEqual('Replace!'); + expect(element.find('div').attr('compiled')).toBe('COMPILED'); + })); + + it('should pass element and attributes to template function', inject(function($compile, $rootScope) { + element = $compile('
ignore
')($rootScope); + expect(element.text()).toEqual('Replace!'); + expect(element.find('div').attr('dynamic')).toBe('456'); + })); + }); + + describe('templateUrl', function() { beforeEach(module( @@ -1214,6 +1257,59 @@ describe('$compile', function() { }); }); + describe('templateUrl function', function() { + + beforeEach(module( + function() { + directive('hello', valueFn({ + restrict: 'CAM', templateUrl: function(e,t) { + return 'hello.html'; + }, + transclude: true + })); + directive('cau', valueFn({ + restrict: 'CAM', templateUrl: function(e,t) { + expect(isElement(e)).toBeTruthy(); + return 'cau'+t.test+'.html'; + } + })); + } + )); + + it('should compile, link and flush the template inline when using functions as templateUrl', inject( + function($compile, $templateCache, $rootScope) { + $templateCache.put('hello.html', 'Hello, {{name}}!'); + $rootScope.name = 'Elvis'; + element = $compile('
')($rootScope); + + $rootScope.$digest(); + + expect(sortedHtml(element)).toBeOneOf( + '
Hello, Elvis!
', + '
Hello, Elvis!
' //ie8 + ); + } + )); + + it('should pass element and attributes to the templateUrl function', inject( + function($compile, $templateCache, $rootScope) { + $templateCache.put('cau2.html', 'Hey, {{name}}!'); + $templateCache.put('cau3.html', 'Say: Hey, {{name}}!'); + $rootScope.name = 'me'; + element = $compile('
')($rootScope); + + $rootScope.$digest(); + + expect(sortedHtml(element)).toBeOneOf( + '
Hey, me!' + + 'Say: Hey, me!
', + '
Hey, me!' + + 'Say: Hey, me!
' //ie8 + ); + } + )); + }); + describe('scope', function() { var iscope;