Getting Around Angular Bootstrap Popover Limitations October 10, 2014
Using Bootstrap is nice, it's got some good default behaviors and components set up for quickly building a decent looking web application. However, the Angular Bootstrap tools are a little bit behind.
For instance, I was using the popover in my latest project. There is no way that I could find to bind HTML to the body part of the code, like so:
<div popover="Title" popover-content="<strong>Content</strong>"> ... </div>
So, not wanting to change how the popover looks, but at the same time not liking how it was working as an Angular directive, I rolled my own. For now, I just wanted to get it to work and also include HTML in the body, so I hard coded some things, like the fact that for now, I only expect it to show up on the right side of the thing I'm popping it over.
module.directive("infobox", function(){
return {
restrict: "E",
transclude: true,
scope: { title: "=", content: "=" },
template: "<div ng-transclude class='infobox popover right' style='position: absolute; display: none'></div>",
controller: function($scope){
},
link: function(scope, element, attrs){
var parentWidth = element.parent().outerWidth();
var infobox = element.find(".infobox");
infobox.append("<div class='arrow'></div>")
element.parent()
.on("mouseover", function(){
var t = angular.element(this);
var offset = t.offset(); offset.left += parentWidth;
var h = t.outerHeight() / 2;
offset.top = offset.top - (infobox.outerHeight() / 2) + h;
t.find("div.infobox").show().offset(offset);
})
.on("mouseout", function(){
var t = angular.element(this);
t.find(".infobox").hide();
}); }
}
}); module.directive("infoboxTitle", function(){
return {
restrict: "E",
transclude: true,
require: "^infobox",
template: "<div ng-transclude class='popover-title'></div>"
}
})
module.directive("infoboxBody", function(){
return {
restrict: "E",
transclude: true,
require: "^infobox",
template: "<div ng-transclude class='popover-content'></div>"
}
})
Then I use it like this:
<infobox>
<infobox-title>{{obj.name}}</infobox-title>
<infobox-body>
<p>{{obj.description}}</p>
<div ng-repeat="(key, val) in obj.attributes">
<strong>{{lookupAttributeName(key)}}</strong>: {{val}}
</div>
</infobox-body>
</infobox>
It generates markup that looks like this, and that works
<infobox class="ng-isolate-scope"><div ng-transclude="" class="infobox popover right" style="position: absolute; display: none; top: -73.5px; left: 241px;">
<infobox-title class="ng-scope"><div ng-transclude="" class="popover-title"><span class="ng-binding ng-scope">Medium Truck</span></div></infobox-title>
<infobox-body class="ng-scope"><div ng-transclude="" class="popover-content">
<p class="ng-binding ng-scope">The medium truck</p>
<!-- ngRepeat: (key, val) in obj.attributes --><div ng-repeat="(key, val) in obj.attributes" class="ng-binding ng-scope">
<strong class="ng-binding">Capacity</strong>: 60
</div><!-- end ngRepeat: (key, val) in obj.attributes --><div ng-repeat="(key, val) in obj.attributes" class="ng-binding ng-scope">
<strong class="ng-binding">Employees Maximum</strong>: 2
</div><!-- end ngRepeat: (key, val) in obj.attributes --><div ng-repeat="(key, val) in obj.attributes" class="ng-binding ng-scope">
<strong class="ng-binding">Employees Minimum</strong>: 1
</div><!-- end ngRepeat: (key, val) in obj.attributes --><div ng-repeat="(key, val) in obj.attributes" class="ng-binding ng-scope">
<strong class="ng-binding">N/A</strong>: 15
</div><!-- end ngRepeat: (key, val) in obj.attributes --><div ng-repeat="(key, val) in obj.attributes" class="ng-binding ng-scope">
<strong class="ng-binding">Miles Per Gallon</strong>: 14
</div><!-- end ngRepeat: (key, val) in obj.attributes -->
</div></infobox-body>
<div class="arrow"></div></div></infobox>
Don't read too much into what my latest project is :)