44

我想创建一个带有包含美化 JSON 对象的 pre 标记的引导弹出框。天真的实现,

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

在将内容插入弹出窗口之前对其进行转义。用 html 内容指定弹出框的最佳方法是什么?

4

8 回答 8

72

更新

从这里可以看出,您现在应该能够在不覆盖默认模板的情况下执行此操作。

原来的:

截至 angular 1.2+ng-bind-html-unsafe已被删除。您应该使用$sce服务Reference

这是一个用于创建可信 HTML 的过滤器。

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

这是使用此过滤器的覆盖的Angular Bootstrap 0.11.2模板

// 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" +
      "");
}]);

编辑:这是一个Plunker实现。

编辑2:由于这个答案不断受到欢迎,我会尽我所能保持更新。作为参考这里是来自 angular-ui bootstrap repo 的模板。如果这种情况发生变化,覆盖模板将需要匹配更新并添加ng-bind-html=\"title | unsafe\"ng-bind-html=\"content | unsafe\"属性才能继续工作。

有关更新的对话,请在此处查看问题

于 2014-02-24T05:04:06.550 回答
25

使用popover-template指令

如果您使用的angular-ui版本等于或高于0.13.0,则最好的选择是使用该popover-template指令。以下是如何使用它:

<button popover-template="'popover.html'">My HTML popover</button>

<script type="text/ng-template" id="popover.html">
    <div>
        Popover content
    </div>
</script>

注意:不要忘记popover-template="'popover.html'".

演示 plunker


附带说明一下,可以将弹出框模板外部化到专用的 html 文件中,而不是<script type="text/ng-template>像上面那样在元素中声明它。

第二个演示 plunker

于 2015-09-26T22:15:04.167 回答
9

我在 github 项目上发布了一个解决方案:https ://github.com/angular-ui/bootstrap/issues/520

我想将此功能添加到您的项目中,这是一个补丁。

添加这些指令:

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");
    }]);

并添加模板:

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

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

在 plunkr 上查看:http ://plnkr.co/edit/VhYAD04ETQsJ2dY3Uum3?p=preview

于 2014-07-24T09:49:15.350 回答
6

对于所有传统的 Bootstrappopover需求,您可以使用以下 angular 指令。它消除了 HTML 模板中的杂乱,并且非常易于使用。

您可以配置弹出框的title, content, placement, 淡入/淡出delay,trigger事件以及内容是否应被视为html. 它还可以防止内容溢出和剪辑。

相关 plunker 与所有 teh 代码在这里http://plnkr.co/edit/MOqhJi

屏幕截图

图片

用法

<!-- 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 });
    }
  };
});
于 2015-01-28T10:17:05.430 回答
6

您需要更改默认弹出框模板以指定您要允许 Html 内容。看看popover-contentdiv,它现在已经绑定到content允许不安全 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>");
    }]);
于 2013-12-20T00:56:41.857 回答
3

请参阅https://github.com/jbruni/bootstrap-bower-jbruni,它允许使用 popover-template

于 2014-07-04T10:08:56.133 回答
1

在我的特定情况下,以下 CSS 样式似乎已经完成了我想要的:

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

一般问题仍然悬而未决。

于 2013-05-24T19:11:05.183 回答
0

这是我的解决方案的一个小提琴

  • 可访问(您可以使用 Tab 键激活/停用)。
  • 允许用户悬停弹出框并使弹出框保持打开状态。
  • 允许页面上的多个弹出框,但在任何给定时间只能激活一个弹出框。
  • 不依赖任何第三方,尽管已经借用了引导弹出框样式。

它的工作方式是我们实例化我们将在popover数组中的页面上拥有的许多弹出框(请参阅评论中的 TODO 以了解如何连接它)。

然后,只要用户在一个应该触发弹出框的元素中选择或悬停,我们就会在popover数组中激活该特定的弹出框。当用户不再悬停元素时,我们为popover数组中的特定元素设置超时。如果超时已经过去,它会快速检查用户是否重新悬停或重新聚焦(通过选项卡)元素。如果是这样,那么我们保持弹出框活着。如果不是,我们隐藏弹出框。

对于 CSS,我不想依赖使用 bootstrap,所以我直接从 bootstrap 借用了样式。如果您尝试使用引导程序的弹出框样式,您可能会遇到一些奇怪的行为,引导程序在我们不想要的自定义弹出框上运行它自己的脚本。

HTML:

 <section>
    <a href="#" 
       ng-mouseover="showPopover(i)" 
       ng-mouseleave="hidePopover(i)"
       ng-focus="showPopover(i)"
       ng-blur="hidePopover(i)">
        I trigger a popover - {{i}}
    </a>
    <popover popover-show="popover[i].popoverTracker">
      <div class="arrow"></div>
      <div class="custom-popover-content"
           ng-mouseover="showPopover(i)" 
           ng-mouseleave="hidePopover(i)"
           ng-focus="showPopover(i)"
           ng-blur="hidePopover(i)">
        <a href="#"
           ng-focus="showPopover(i)"
           ng-blur="hidePopover(i)">You can tab into me, I'm accessible!</a>
        <br/>
        <a href="#" 
           ng-focus="showPopover(i)"
           ng-blur="hidePopover(i)">You can tab into me, I'm accessible!</a>
      </div>
    </popover>
  </section> 

角度控制器和指令:

angular.module('controllers', []);
angular.module('directives', []);
angular.module('myApp', ['ngAnimate', 'controllers', 'directives']);

angular.module('controllers')
    .controller('MainCtrl', function ($scope, $timeout) {

    $scope.popover = [];

    (function init() {
        // TODO: Make this dynamic so that we can pass it a value and it will generate the right amount
        // Initializing the array of popovers on startup
      createPopoverTrackers(20);
        })();

    // Creating an array of popovers equal to the number of popovers on the page
    function createPopoverTrackers(count) {
        for(var i = 0; i < count; i++) {
        $scope.popover.push({
            popoverTracker: false,
          popoverKeepAlive: false,
          timer: null
        })
      }
    }

    // A user has focused on an element that has an associated popover
    $scope.queueOpenPopover = function(index) {
    // Show our specified tracker
    $scope.popover[index].popoverTracker = true;

     // Hide the rest
     Object.keys($scope.popover)
        .filter(function(trackerIndex) {
          return trackerIndex != index
       })
      .forEach(function(trackerIndex) {
          $scope.popover[trackerIndex].popoverTracker = false;
          $scope.popover[trackerIndex].popoverKeepAlive = false;

          const timer = $scope.popover[trackerIndex].timer;
          if(timer) {
             $timeout.cancel(timer);
             $scope.popover[trackerIndex].timer = null;
          }
      })
        };

    // Queuing up the demise of the popover
    $scope.queueKillPopover = function(index) {
      $scope.popover[index].timer = $timeout(function() {
             if (!$scope.popover[index].popoverKeepAlive) {
          // Popover or the popover trigger were not hovered within the time limit, kill it!
          $scope.popover[index].popoverTracker = false;
        }
        }, 700);
    };

    // When a user focuses into the actual popover itself or it's trigger,  we need to keep it alive
    $scope.showPopover = function(index) {
        $scope.popover[index].popoverKeepAlive = true;
      $scope.queueOpenPopover(index);
    };

    // The user has removed focus from the popover or it's trigger, set this to false so the timer knows to kill it
    $scope.hidePopover = function(index) {
        $scope.popover[index].popoverKeepAlive = false;
      $scope.queueKillPopover(index);
    };
});

angular.module('directives')
    .directive('popover', function () {
    return {
        restrict: 'E',
        replace: true,
        transclude: true,
        scope: {
            'popoverShow': '='
        },
        template: '<div class="custom-popover bottom" ng-show="popoverShow" ng-transclude></div>'
    };
});

从引导程序借来的 CSS:

.custom-popover {
    position: absolute;
    z-index: 1010;
    max-width: 276px;
    padding: 1px;
    text-align: left;
    white-space: normal;
    background-color: #fff;
    border: 1px solid rgba(0,0,0,0.2);
    border-radius: 6px;
    box-shadow: 0 5px 10px rgba(0,0,0,0.2);
    background-clip: padding-box;
}

.custom-popover .arrow,
.custom-popover .arrow:after {
  position: absolute;
  display: block;
  width: 0;
  height: 0;
  border-color: transparent;
  border-style: solid;
}

.custom-popover .arrow {
  border-width: 11px;
}

.custom-popover .arrow:after {
  border-width: 10px;
  content: "";
}

.custom-popover.bottom {
  margin-top: 10px;
}

.custom-popover.bottom .arrow {
  top: -11px;
  left: 50%;
  margin-left: -11px;
  border-bottom-color: rgba(0, 0, 0, 0.25);
  border-top-width: 0;
}

.custom-popover.bottom .arrow:after {
  top: 1px;
  margin-left: -10px;
  border-bottom-color: #ffffff;
  border-top-width: 0;
  content: " ";
}

.custom-popover-content {
  padding: 9px 14px;
}
于 2020-03-13T17:15:21.740 回答