2

I am trying to use a Bootstrap Popover in an AngularJS ngRepeat directive. The problem is the title attribute isn't being interpolated. How can I resolve this issue?

View

<div ng-app="app" id="ng-app">
<ul ng-controller="Main">
    <li ng-repeat="i in items" data-toggle="popover" title="{{ i.title }}" <!-- Title isn't interpolated so Popover doesn't render a title -->
        data-content="Events: {{ day.events.length }}"
        init-popover>{{ i.data }}</li>
</ul>

Code

var app = angular.module('app', []);

app.controller('Main', ['$scope', function($scope){
    $scope.items = [
        { title: 'Title 1', data: 'lorem' },
        { title: 'Title 2', data: 'ipsum' },
    ];
}]);

app.directive('initPopover', function() {
  return function(scope, element, attrs) {
    if (scope.$last){
      $('[data-toggle="popover"]').popover({container: 'ul'});
    }
  };
});

FIDDLE

ExceptionLimeCat
  • 6,191
  • 6
  • 44
  • 77
  • 2
    Hello. I appreciate that you included a fiddle, but it isn't clear what the problem is. Can you improve your fiddle so that it says "Here is what I would like to happen: X. As you can see, Y is happening." – Code Whisperer Feb 13 '15 at 16:16
  • Sorry I wasn't clear. I updated the Fiddle. – ExceptionLimeCat Feb 13 '15 at 16:27
  • I'd suggest [ui-bootstrap](http://angular-ui.github.io/bootstrap/#/popover) here, but see my answer for a solution using the current plugin. – Nate Barbettini Feb 13 '15 at 17:14

7 Answers7

2

You can make it work but it's pretty ugly, here is my solution

var app = angular.module('app', []);

app.controller('Main', ['$scope', function($scope){
    $scope.items = [        
        { title: 'Title 1', data: 'Test in ngRepeat' },
        { title: 'Title 2', data: 'Test in ngRepeat' },
    ];
}]);

app.directive('initPopover', function() {
  return function(scope, element, attrs) {
      if (scope.$last) {
      attrs.$observe('title', function () {
          $('[data-toggle="popover"]').popover({container: 'ul'});
      });
      }
  };
});
thedoctor
  • 1,498
  • 1
  • 12
  • 12
1

You shouldn't use title="{{i.title}}", but ng-attr-title="{{i.title}}"...

MarcoS
  • 17,323
  • 24
  • 96
  • 174
  • Sorry, your fiddle isn't clear to me, too: what is your "desired result"? However, for AngularJS to interpolate title attribute, you *must* use `ng-attr-title`, and *not* `title`... – MarcoS Feb 13 '15 at 16:40
  • The desired result is for the title of the Popover to be driven a value in the scope object. It currently is not being interpolated. If I try to use `ng-attr-title`, it is not rendered in the popover. I will update the fiddle to show this. – ExceptionLimeCat Feb 13 '15 at 16:43
  • I see, sorry, I didn't understand what you meant. Sorry, I never used popovers, so can't help you here... More, I see title is interpolated by AngularJS as well as ng-attr-title... I am quite sure some time ago (and angular versions ago) it was not the case... :-( – MarcoS Feb 13 '15 at 16:52
  • Popover requires the `title` attribute to be set in order for it to render a title for the popover but it's just not interpolating. – ExceptionLimeCat Feb 13 '15 at 16:55
  • I remember, now... :-) You have to use `ng-attr-title` when you want *dynamical* titles (i.e.: titles following the model, and not set statically on page load...). – MarcoS Feb 13 '15 at 16:57
  • It looks like I'm going to have to override the `Tooltip.getTitle` method to handle `ng-attr-title`... just not sure how to do this. – ExceptionLimeCat Feb 13 '15 at 17:01
  • `ng-attr-title` is for _HTML titles_ (frequently rendered as tooltips). This is a little tricky because the Bootstrap plugin looks at the same attribute as far as I can tell. – Nate Barbettini Feb 13 '15 at 17:07
1

I had the same problem. I'm using bootstrap v3.3.6 and Angular v1.5.0

Adding attribute data-original-title="{{someTitle}}" fixed the problem.

<button type="button" class="btn btn-lg btn-danger" data-toggle="popover" title="{{someTitle}}" data-original-title="{{someTitle}}" data-content="Some Content">Click to toggle popover</button>

Also, as bootstrap requires, call .popover() in directive

module.directive('myDir', ['jQuery', function (jQuery) {
    return {
        link: function () {
            jQuery('[data-toggle="popover"]').popover();
        }
    };
}]);
Junior
  • 21
  • 2
0

Consider using ui-bootstrap for native Angular directives instead of Bootstrap's jQuery version. They're a lot easier to work with in an Angular project.

Edit: If you can't do that, here's a solution:

<li ng-repeat="i in items" data-toggle="popover" data-ng-attr-popover_title="{{ i.title }}" 
            data-content="Popover Content"
            init-popover>{{ i.data }}</li>

With a small change to the directive:

app.directive('initPopover', function() {
  return function(scope, element, attrs) {
    if (scope.$last){
        $('[data-toggle="popover"]').popover({ container: 'ul', title: attrs.popoverTitle });
    }
  };

ng-attr-popup_title will create an HTML attribute called popupTitle on the element with the Angular syntax inside {{}} parsed correctly. Then, in the directive, we can just examine that attribute to set the popover title.

Fiddle: here

Nate Barbettini
  • 51,256
  • 26
  • 134
  • 147
0

Add $timeout to directive will solve your problem. $timeout will gets call when last element of ng-repeat rendered on UI & run $digest cycle.

Directive

app.directive('initPopover', function($timeout) {
    return function(scope, element, attrs) {
        if (scope.$last) {
            $timeout(function() {
                angular.element('[data-toggle="popover"]').popover({
                    container: 'ul'
                });
            });
        }
    };
});

Working Fiddle

Hope this could help you. Thanks.

Pankaj Parkar
  • 134,766
  • 23
  • 234
  • 299
0

Just to add to the knowledge base here. Here's the angular way, in case anyone needs. I based my solution on Mark Coleman's PopOver tutorial, and of course what has been contributed by Nate Barbettini.

My situation was I have a nested ng-repeat over items in items, so I need to have a title for my popup that is based on an array stored in a scope, while the content of the popup is from another array stored in the array of the scope.

I added ng-attr-title to my HTML element, like so:

 <div pop-over items='week.items' ng-attr-title="{{'Week number:'+ week.week}}">  Total sales this week : {{week.sales}}</div>

Then, in my directive, I followed mostly what Mark Coleman did.

    angular.module('vegadirectives', [])
.directive('popOver', function ($compile, $templateCache) {

var itemsTemplate = "<ul class='popper'><li ng-repeat='thing in items track by $index'>";
itemsTemplate+=" <i class='fa fa-square' ng-class=";
itemsTemplate+="{purple:thing.color==='purple','orange':thing.color==='orange','white':thing.color==='white','pink':thing.color==='pink','red':thing.color==='red','green':thing.color==='green','yellow':thing.color==='yellow'}";
itemsTemplate+="></i>  {{thing.model}} - {{thing.qty}}</li></ul>";
    var getTemplate = function () {
        var template=itemsTemplate;
        return template;
    }
    return {
        restrict: "A",
        transclude: true, 
        template: "<span ng-transclude></span>",
        link: function (scope, element, attrs) {
            var popOverContent;


            if (scope.items) {
                var html = getTemplate();
                popOverContent = $compile(html)(scope);                    
                var options = {
                    trigger:'click',
                    content: popOverContent,
                    placement: "right",
                    html: true,
                    title:scope.title


                };
                if (scope.$last) {

                }
                $(element).popover(options);
            }
        },
        scope: {
            items: '=',
           title:'@'
        }
    };
});

Explanation:

To fill up my popover with a list of items, with some fancy color square to indicate different types of items, I created a chunk of HTML code. This chunk named itemsTemplate will be used by $compile to create the popover content with scope. I think the usage is $compile(htmlstring)(scope).

Next comes the standard stuff for options settings for the popover. For the interpolation of scope with title, I specified the option title: with scope.title. Then, in the scope: option way down at the end, the two scope elements are specified with '=' and '@'.

So, the title:scope.title refers or binds to scope: {items:'=', title:'@' }, and the title:'@' is the attributes of the title in the HTML, which is ng-attrs-title={{week.week}}, where week is from the outer ng-repeat loop.

Hope it helps.

Ben Looi
  • 178
  • 2
  • 9
0

After finding this answer at https://stackoverflow.com/a/17864868/2379762, I tried data-original-title={{i.title}}, and it works.

http://jsfiddle.net/f1tjj8x5/

<li ng-repeat="i in items" data-toggle="popover" data-original-title="{{ i.title }}" 
        data-content="Popover Content"
        init-popover>{{ i.data }}</li>
Community
  • 1
  • 1
JohnMarkT
  • 36
  • 4