Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I want to create a bootstrap popover with a pre tag containing a prettified JSON object. The naive implementation,

<span popover='<pre>{[ some_obj | json:"  " ]}</pre>'
      popover-trigger='mouseenter'>

escapes the content before inserting it into the popup. What's the best way of specifying a popover body with html content?

share|improve this question
    
My angular popover directive inspector-gadget lets you use HTML in popover content a bit more seemlessly. Might be a better solution if you don't mind the dependency. –  Doug T. Oct 4 '14 at 13:50

6 Answers 6

up vote 2 down vote accepted

For all your conventional Bootstrap popover needs you could utilize the following angular directive. It removes clutter from the HTML template and is very easy to use.

You can configure popover's title, content, placement, fade in/out delay, trigger event and whether content should be treated as html. It also prevents content overflow & clipping.

Related plunker with all teh codes here http://plnkr.co/edit/MOqhJi

Screencap

imgur

Usage

<!-- HTML -->
<div ng-model="popup.content" popup="popup.options">Some element</div>

/* JavaScript */
this.popup = {
  content: 'Popup content here',
  options: {
    title: null,
    placement: 'right', 
    delay: { show: 800, hide: 100 }
  }
}; 

JavaScript

/**
 * Popup, a Bootstrap popover wrapper.
 *
 * Usage: 
 *  <div ng-model="model" popup="options"></div>
 * 
 * Remarks: 
 *  To prevent content overflow and clipping, use CSS
 *  .popover { word-wrap: break-word; }
 *  Popup without title and content will not be shown.
 *
 * @param {String}  ngModel           popup content
 * @param {Object}  options           popup options
 * @param {String}  options.title     title
 * @param {Boolean} options.html      content should be treated as html markup
 * @param {String}  options.placement placement (top, bottom, left or right)
 * @param {String}  options.trigger   trigger event, default is hover
 * @param {Object}  options.delay     milliseconds or { show:<ms>, hide:<ms> }
 */
app.directive('popup', function() {
  return {
    restrict: 'A',
    require: 'ngModel',
    scope: {
      ngModel: '=',
      options: '=popup'
    },
    link: function(scope, element) {
      scope.$watch('ngModel', function(val) {
        element.attr('data-content', val);
      });

      var options = scope.options || {} ; 

      var title = options.title || null;
      var placement = options.placement || 'right';
      var html = options.html || false;
      var delay = options.delay ? angular.toJson(options.delay) : null;
      var trigger = options.trigger || 'hover';

      element.attr('title', title);
      element.attr('data-placement', placement);
      element.attr('data-html', html);
      element.attr('data-delay', delay);
      element.popover({ trigger: trigger });
    }
  };
});
share|improve this answer
    
This isn't using angular UI bootstrap like OP asked, and instead relies on jQuery and bootstrap.js not sure how conventional that is. Other wise good answer. –  Matthew.Lothian Mar 2 at 0:54

As of angular 1.2+ ng-bind-html-unsafe has been removed. you should be using the $sce service. Reference.

Here is a filter for creating trusted html.

MyApp.filter('unsafe', ['$sce', function ($sce) {
    return function (val) {
        return $sce.trustAsHtml(val);
    };
}]);

Here is the overwritten Angular Bootstrap 0.11.2 template making use of this filter

// update popover template for binding unsafe html
angular.module("template/popover/popover.html", []).run(["$templateCache", function ($templateCache) {
    $templateCache.put("template/popover/popover.html",
      "<div class=\"popover {{placement}}\" ng-class=\"{ in: isOpen(), fade: animation() }\">\n" +
      "  <div class=\"arrow\"></div>\n" +
      "\n" +
      "  <div class=\"popover-inner\">\n" +
      "      <h3 class=\"popover-title\" ng-bind-html=\"title | unsafe\" ng-show=\"title\"></h3>\n" +
      "      <div class=\"popover-content\"ng-bind-html=\"content | unsafe\"></div>\n" +
      "  </div>\n" +
      "</div>\n" +
      "");
}]);

EDIT: Here is a Plunker implementation.

EDIT 2: As this answer keeps getting hits, i'll keep it updated as best i can. As a reference Here is the template from the angular-ui bootstrap repo. If this changes, the override template will require matching updates and the addition of the ng-bind-html=\"title | unsafe\" and ng-bind-html=\"content | unsafe\" attributes to continue working.

For updated conversation check the issue here.

share|improve this answer
    
where do I have to put this code? –  zoidbergi Mar 19 '14 at 15:16
    
@zoidbergi The filter should be added to a module, or any modules used by you main app module. The template override should be included after Angular and Bootstrap UI so it isn't redefined by bootstrap. I'll add an example to make it easier to see it working. –  Matthew.Lothian Mar 20 '14 at 2:24
1  
@Andrey Fedorov as this has as many upvotes as the current (now incorrect) answer, could you help future viewers by setting the above to the marked answer? –  Matthew.Lothian Mar 31 '14 at 2:00

You need to alter the default popover template to specify you want to allow Html content. Look the popover-content div, it now has its binding done to the content property allowing unsafe html:

 angular.module("template/popover/popover.html", []).run(["$templateCache", function ($templateCache) {
        $templateCache.put("template/popover/popover.html",
            "<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' ng-bind-html-unsafe='content'></div>" +
            "<button class='btn btn-cancel' ng-click='manualHide()'>Cancel</button>" +
            "<button class='btn btn-apply' ng-click='manualHide()'>Apply</button></div></div>");
    }]);
share|improve this answer
8  
This is no longer correct as of angular 1.2+ as ng-bind-html-unsafe has been removed. –  Matthew.Lothian Feb 24 '14 at 4:55
1  
See the correct answer for Angular UI 1.2, below. stackoverflow.com/a/21979258/48082 –  Cheeso Sep 3 '14 at 0:03

The following CSS styling seems to have done what I wanted in my specific case:

.popover-content {
  white-space: pre;
  font-family: monospace;
}

The general question still remains open.

share|improve this answer

See https://github.com/jbruni/bootstrap-bower-jbruni, which allow to use a popover-template

share|improve this answer

I post a solution on the github project: https://github.com/angular-ui/bootstrap/issues/520

I you want to add this functionality to your project, here is a patch.

Add those directives:

angular.module("XXX")
    .directive("popoverHtmlUnsafePopup", function () {
      return {
        restrict: "EA",
        replace: true,
        scope: { title: "@", content: "@", placement: "@", animation: "&", isOpen: "&" },
        templateUrl: "template/popover/popover-html-unsafe-popup.html"
      };
    })

    .directive("popoverHtmlUnsafe", [ "$tooltip", function ($tooltip) {
      return $tooltip("popoverHtmlUnsafe", "popover", "click");
    }]);

And add the template:

<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" bind-html-unsafe="content"></div>
  </div>
</div>

Usage: <button popover-placement="top" popover-html-unsafe="On the <b>Top!</b>" class="btn btn-default">Top</button>

View it on plunkr: http://plnkr.co/edit/VhYAD04ETQsJ2dY3Uum3?p=preview

share|improve this answer
    
Since the template used is defined directly in directive,is there any way we can choose template directly from HTML ? Since we may need different template at different times. –  Aniket Sinha Jul 30 '14 at 6:39

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.