Getting Around Angular Bootstrap Popover Limitations

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 :)

blog comments powered by Disqus