diff --git a/src/popover/docs/demo.js b/src/popover/docs/demo.js
index 6cce89812b..e4e0e2ff29 100644
--- a/src/popover/docs/demo.js
+++ b/src/popover/docs/demo.js
@@ -1,4 +1,7 @@
angular.module('ui.bootstrap.demo').controller('PopoverDemoCtrl', function ($scope) {
- $scope.dynamicPopover = 'Hello, World!';
- $scope.dynamicPopoverTitle = 'Title';
+ $scope.dynamicPopover = {
+ content: 'Hello, World!',
+ templateUrl: 'myTemplatePopover.html',
+ title: 'Title'
+ };
});
diff --git a/src/popover/docs/readme.md b/src/popover/docs/readme.md
index cbac5f3d26..dcfd6dff6f 100644
--- a/src/popover/docs/readme.md
+++ b/src/popover/docs/readme.md
@@ -4,6 +4,13 @@ directive supports multiple placements, optional transition animation, and more.
Like the Bootstrap jQuery plugin, the popover **requires** the tooltip
module.
+There are two versions of the popover: `popover` and `popover-template`:
+
+- `popover` takes text only and will escape any HTML provided for the popover
+ body.
+- `popover-template` takes text that specifies the location of a template to
+ use for the popover body.
+
The popover directives provides several optional attributes to control how it
will display:
diff --git a/src/popover/popover.js b/src/popover/popover.js
index 2bea0a3e10..dee4c94381 100644
--- a/src/popover/popover.js
+++ b/src/popover/popover.js
@@ -5,6 +5,20 @@
*/
angular.module( 'ui.bootstrap.popover', [ 'ui.bootstrap.tooltip' ] )
+.directive( 'popoverTemplatePopup', function () {
+ return {
+ restrict: 'EA',
+ replace: true,
+ scope: { title: '@', content: '@', placement: '@', animation: '&', isOpen: '&',
+ originScope: '&' },
+ templateUrl: 'template/popover/popover-template.html'
+ };
+})
+
+.directive( 'popoverTemplate', [ '$tooltip', function ( $tooltip ) {
+ return $tooltip( 'popoverTemplate', 'popoverTemplate', 'click' );
+}])
+
.directive( 'popoverPopup', function () {
return {
restrict: 'EA',
diff --git a/src/popover/test/popover-template.spec.js b/src/popover/test/popover-template.spec.js
new file mode 100644
index 0000000000..cb504c28d9
--- /dev/null
+++ b/src/popover/test/popover-template.spec.js
@@ -0,0 +1,66 @@
+describe('popover template', function() {
+ var elm,
+ elmBody,
+ scope,
+ elmScope,
+ tooltipScope;
+
+ // load the popover code
+ beforeEach(module('ui.bootstrap.popover'));
+
+ // load the template
+ beforeEach(module('template/popover/popover.html'));
+ beforeEach(module('template/popover/popover-template.html'));
+
+ beforeEach(inject(function ($templateCache) {
+ $templateCache.put('myUrl', [200, '{{ myTemplateText }}', {}]);
+ }));
+
+ beforeEach(inject(function($rootScope, $compile) {
+ elmBody = angular.element(
+ '
Selector Text
'
+ );
+
+ scope = $rootScope;
+ $compile(elmBody)(scope);
+ scope.templateUrl = 'myUrl';
+
+ scope.$digest();
+ elm = elmBody.find('span');
+ elmScope = elm.scope();
+ tooltipScope = elmScope.$$childTail;
+ }));
+
+ it('should open on click', inject(function() {
+ elm.trigger( 'click' );
+ expect( tooltipScope.isOpen ).toBe( true );
+
+ expect( elmBody.children().length ).toBe( 2 );
+ }));
+
+ it('should not open on click if templateUrl is empty', inject(function() {
+ scope.templateUrl = null;
+ scope.$digest();
+
+ elm.trigger( 'click' );
+ expect( tooltipScope.isOpen ).toBe( false );
+
+ expect( elmBody.children().length ).toBe( 1 );
+ }));
+
+ it('should show updated text', inject(function() {
+ scope.myTemplateText = 'some text';
+ scope.$digest();
+
+ elm.trigger( 'click' );
+ expect( tooltipScope.isOpen ).toBe( true );
+
+ expect( elmBody.children().eq(1).text().trim() ).toBe( 'some text' );
+
+ scope.myTemplateText = 'new text';
+ scope.$digest();
+
+ expect( elmBody.children().eq(1).text().trim() ).toBe( 'new text' );
+ }));
+});
+
diff --git a/src/tooltip/tooltip.js b/src/tooltip/tooltip.js
index 4c5dc825de..646eb58b72 100644
--- a/src/tooltip/tooltip.js
+++ b/src/tooltip/tooltip.js
@@ -103,6 +103,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
'class="'+startSym+'class'+endSym+'" '+
'animation="animation" '+
'is-open="isOpen"'+
+ 'origin-scope="origScope" '+
'>'+
'';
@@ -111,7 +112,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
compile: function (tElem, tAttrs) {
var tooltipLinker = $compile( template );
- return function link ( scope, element, attrs ) {
+ return function link ( scope, element, attrs, tooltipCtrl ) {
var tooltip;
var tooltipLinkedScope;
var transitionTimeout;
@@ -132,6 +133,9 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
tooltip.css( ttPosition );
};
+ // Set up the correct scope to allow transclusion later
+ ttScope.origScope = scope;
+
// By default, the tooltip is not open.
// TODO add ability to start tooltip opened
ttScope.isOpen = false;
@@ -197,7 +201,7 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
// And show the tooltip.
ttScope.isOpen = true;
- ttScope.$digest(); // digest required as $apply is not called
+ ttScope.$apply(); // digest required as $apply is not called
// Return positioning function as promise callback for correct
// positioning after draw.
@@ -349,6 +353,74 @@ angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position', 'ui.bootstrap
}];
})
+// This is mostly ngInclude code but with a custom scope
+.directive( 'tooltipTemplateTransclude', [
+ '$animate', '$sce', '$compile', '$templateRequest',
+function ($animate , $sce , $compile , $templateRequest) {
+ return {
+ link: function ( scope, elem, attrs ) {
+ var origScope = scope.$eval(attrs.tooltipTemplateTranscludeScope);
+
+ var changeCounter = 0,
+ currentScope,
+ previousElement,
+ currentElement;
+
+ var cleanupLastIncludeContent = function() {
+ if (previousElement) {
+ previousElement.remove();
+ previousElement = null;
+ }
+ if (currentScope) {
+ currentScope.$destroy();
+ currentScope = null;
+ }
+ if (currentElement) {
+ $animate.leave(currentElement).then(function() {
+ previousElement = null;
+ });
+ previousElement = currentElement;
+ currentElement = null;
+ }
+ };
+
+ scope.$watch($sce.parseAsResourceUrl(attrs.tooltipTemplateTransclude), function (src) {
+ var thisChangeId = ++changeCounter;
+
+ if (src) {
+ //set the 2nd param to true to ignore the template request error so that the inner
+ //contents and scope can be cleaned up.
+ $templateRequest(src, true).then(function(response) {
+ if (thisChangeId !== changeCounter) { return; }
+ var newScope = origScope.$new();
+ var template = response;
+
+ var clone = $compile(template)(newScope, function(clone) {
+ cleanupLastIncludeContent();
+ $animate.enter(clone, elem);
+ });
+
+ currentScope = newScope;
+ currentElement = clone;
+
+ currentScope.$emit('$includeContentLoaded', src);
+ }, function() {
+ if (thisChangeId === changeCounter) {
+ cleanupLastIncludeContent();
+ scope.$emit('$includeContentError', src);
+ }
+ });
+ scope.$emit('$includeContentRequested', src);
+ } else {
+ cleanupLastIncludeContent();
+ }
+ });
+
+ scope.$on('$destroy', cleanupLastIncludeContent);
+ }
+ };
+}])
+
.directive( 'tooltipPopup', function () {
return {
restrict: 'EA',
diff --git a/template/popover/popover-template.html b/template/popover/popover-template.html
new file mode 100644
index 0000000000..43731c8284
--- /dev/null
+++ b/template/popover/popover-template.html
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
From a1695114a245312878d315dfc9e369f98d573eae Mon Sep 17 00:00:00 2001
From: Chris Chua
Date: Tue, 24 Mar 2015 23:31:44 -0700
Subject: [PATCH 2/2] feat(tooltip): add tooltip-template directive
Fixes #220
---
src/tooltip/docs/demo.html | 6 ++
src/tooltip/docs/readme.md | 15 +++--
src/tooltip/test/tooltip-template.spec.js | 65 ++++++++++++++++++++
src/tooltip/tooltip.js | 17 +++++
template/tooltip/tooltip-template-popup.html | 6 ++
5 files changed, 104 insertions(+), 5 deletions(-)
create mode 100644 src/tooltip/test/tooltip-template.spec.js
create mode 100644 template/tooltip/tooltip-template-popup.html
diff --git a/src/tooltip/docs/demo.html b/src/tooltip/docs/demo.html
index 09aa0ba496..914dc91ccd 100644
--- a/src/tooltip/docs/demo.html
+++ b/src/tooltip/docs/demo.html
@@ -20,6 +20,8 @@
fading
at elementum eu, facilisis sed odio morbi quis commodo odio. In cursus
delayed turpis massa tincidunt dui ut.
+ Custom template
+ nunc sed velit dignissim sodales ut eu sem integer vitae. Turpis egestas
@@ -58,4 +60,8 @@
tooltip-enable="!inputModel" />
+
+
diff --git a/src/tooltip/docs/readme.md b/src/tooltip/docs/readme.md
index 960a7d154f..fcea6c11b0 100644
--- a/src/tooltip/docs/readme.md
+++ b/src/tooltip/docs/readme.md
@@ -1,11 +1,16 @@
A lightweight, extensible directive for fancy tooltip creation. The tooltip
directive supports multiple placements, optional transition animation, and more.
-There are two versions of the tooltip: `tooltip` and `tooltip-html-unsafe`. The
-former takes text only and will escape any HTML provided. The latter takes
-whatever HTML is provided and displays it in a tooltip; it's called "unsafe"
-because the HTML is not sanitized. *The user is responsible for ensuring the
-content is safe to put into the DOM!*
+There are three versions of the tooltip: `tooltip`, `tooltip-template`, and
+`tooltip-html-unsafe`:
+
+- `tooltip` takes text only and will escape any HTML provided.
+- `tooltip-template` takes text that specifies the location of a template to
+ use for the tooltip.
+- `tooltip-html-unsafe` takes
+ whatever HTML is provided and displays it in a tooltip; it's called "unsafe"
+ because the HTML is not sanitized. *The user is responsible for ensuring the
+ content is safe to put into the DOM!*
The tooltip directives provide several optional attributes to control how they
will display:
diff --git a/src/tooltip/test/tooltip-template.spec.js b/src/tooltip/test/tooltip-template.spec.js
new file mode 100644
index 0000000000..dab8d34863
--- /dev/null
+++ b/src/tooltip/test/tooltip-template.spec.js
@@ -0,0 +1,65 @@
+describe('tooltip template', function() {
+ var elm,
+ elmBody,
+ scope,
+ elmScope,
+ tooltipScope;
+
+ // load the popover code
+ beforeEach(module('ui.bootstrap.tooltip'));
+
+ // load the template
+ beforeEach(module('template/tooltip/tooltip-template-popup.html'));
+
+ beforeEach(inject(function ($templateCache) {
+ $templateCache.put('myUrl', [200, '{{ myTemplateText }}', {}]);
+ }));
+
+ beforeEach(inject(function($rootScope, $compile) {
+ elmBody = angular.element(
+ '