Skip to content
This repository has been archived by the owner on May 29, 2019. It is now read-only.

Commit

Permalink
feat(popover): add popover-template directive
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisirhc committed Mar 28, 2015
1 parent bf4b5ec commit 7e3179a
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 8 deletions.
21 changes: 17 additions & 4 deletions src/popover/docs/demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,27 @@
<h4>Dynamic</h4>
<div class="form-group">
<label>Popup Text:</label>
<input type="text" ng-model="dynamicPopover" class="form-control">
<input type="text" ng-model="dynamicPopover.content" class="form-control">
</div>
<div class="form-group">
<label>Popup Title:</label>
<input type="text" ng-model="dynamicPopoverTitle" class="form-control">
<input type="text" ng-model="dynamicPopover.title" class="form-control">
</div>
<button popover="{{dynamicPopover}}" popover-title="{{dynamicPopoverTitle}}" class="btn btn-default">Dynamic Popover</button>

<div class="form-group">
<label>Popup Template:</label>
<input type="text" ng-model="dynamicPopover.templateUrl" class="form-control">
</div>
<button popover="{{dynamicPopover.content}}" popover-title="{{dynamicPopover.title}}" class="btn btn-default">Dynamic Popover</button>

<button popover-template="{{dynamicPopover.templateUrl}}" popover-template-title="{{dynamicPopover.title}}" class="btn btn-default">Popover With Template</button>

<script type="text/ng-template" id="myPopoverTemplate.html">
<div>{{dynamicPopover.content}}</div>
<div class="form-group">
<label>Popup Title:</label>
<input type="text" ng-model="dynamicPopover.title" class="form-control">
</div>
</script>
<hr />
<h4>Positional</h4>
<button popover-placement="top" popover="On the Top!" class="btn btn-default">Top</button>
Expand Down
7 changes: 5 additions & 2 deletions src/popover/docs/demo.js
Original file line number Diff line number Diff line change
@@ -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'
};
});
7 changes: 7 additions & 0 deletions src/popover/docs/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
14 changes: 14 additions & 0 deletions src/popover/popover.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
66 changes: 66 additions & 0 deletions src/popover/test/popover-template.spec.js
Original file line number Diff line number Diff line change
@@ -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, '<span>{{ myTemplateText }}</span>', {}]);
}));

beforeEach(inject(function($rootScope, $compile) {
elmBody = angular.element(
'<div><span popover-template="{{ templateUrl }}">Selector Text</span></div>'
);

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' );
}));
});

76 changes: 74 additions & 2 deletions src/tooltip/tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -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" '+
'>'+
'</div>';

Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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',
Expand Down
10 changes: 10 additions & 0 deletions template/popover/popover-template.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<div class="popover {{placement}}" ng-class="{ in: isOpen(), fade: animation() }">
<div class="arrow"></div>

<div class="popover-inner">
<h3 class="popover-title" ng-bind="title" ng-show="title"></h3>
<div class="popover-content"
tooltip-template-transclude="content"
tooltip-template-transclude-scope="originScope()"></div>
</div>
</div>

0 comments on commit 7e3179a

Please sign in to comment.