Getting an error with context in jQuery when using ng-repeat and limit-to and a tooltip from tether.js

2.1k Views Asked by At

First off, I know that's a heck of a title.

Ive recently taken over angular-tooltip and am attempting to build a custom tooltip for my main work project.

In my project, I have an ng-repeat directive that simply says

<div class="row-submenu-white" ng-repeat="merge in potentialMerges | limitTo: numPotentialMergesVisible" company-profile-tooltip="merge.preview"></div>

Using the instructions for the library, I defined a custom tooltip directive:

myApp.directive('companyProfileTooltip', ['$tooltip', ($tooltip) => {
    return {
        restrict: 'EA',
        scope: { profile: '@companyProfileTooltip' },
        link: (scope: ng.IScope, elem) => {
            var tooltip = $tooltip({
                target: elem,
                scope: scope,
                templateUrl: "/App/Private/Content/Common/company.profile.html",
                tether: {
                    attachment: 'middle right',
                    targetAttachment: 'middle left',
                    offset: '0 10px'
                }
            });

            $(elem).hover(() => {
               tooltip.open();
           }, () => {
                tooltip.close();
           });
        }
    };
}]);

Company.profile.html is simply:

<div>Hello, world!</div>

Now, if you notice, in the ng-repeat I have a limitTo filter. For each of those (inititally 3) merges work perfectly, where a <div>Hello, world!</div> tooltip is properly added.

Then I trigger the limitTo to limit to a greater number. Each repeated element after the initial 3 gives me the following error:

TypeError: context is undefined


if ( ( context.ownerDocument || context ) !== document ) {

The error is in jquery-2.1.1.js, which debugging appears to be hopelessly over my head.

What I can tell you is that the function being called for that line is

Sizzle.contains = function( context, elem ) {
    // Set document vars if needed
    if ( ( context.ownerDocument || context ) !== document ) {
        setDocument( context );
    }
    return contains( context, elem );
};

With a call stack of

Sizzle</Sizzle.contains(context=Document 046f7364-fa8d-4e95-e131-fa26ae78d108, elem=div)jquery-2.1.1.js (line 1409)
.buildFragment(elems=["<div>\r\n Hello, world!\r\n</div>"], context=Document 046f7364-fa8d-4e95-e131-fa26ae78d108, scripts=false, selection=undefined)jquery-2.1.1.js (line 5123)
jQuery.parseHTML(data="<div>\r\n Hello, world!\r\n</div>", context=Document 046f7364-fa8d-4e95-e131-fa26ae78d108, keepScripts=true)jquery-2.1.1.js (line 8810)
jQuery.fn.init(selector="<div>\r\n Hello, world!\r\n</div>\r\n", context=undefined, rootjQuery=undefined)jquery-....2.1.js (line 221)
jQuery(selector="<div>\r\n Hello, world!\r\n</div>\r\n", context=undefined)jquery-2.1.1.js (line 76)
compile($compileNodes="<div>\r\n Hello, world!\r\n</div>\r\n", transcludeFn=undefined, maxPriority=undefined, ignoreDirective=undefined, previousCompileContext=undefined)angular.js (line 6812)
m()angular....min.js (line 2)
.link/<()app.js (line 262)
jQuery.event.special[orig].handle(event=Object { originalEvent=Event mouseover, type="mouseenter", timeStamp=0, more...})jquery-2.1.1.js (line 4739)
jQuery.event.dispatch(event=Object { originalEvent=Event mouseover, type="mouseenter", timeStamp=0, more...})jquery-2.1.1.js (line 4408)
jQuery.event.add/elemData.handle(e=mouseover clientX=980, clientY=403)jquery-2.1.1.js (line 4095)

App.js line 262 being the link function of the directive supplied above.

For the life of me, I cannot figure out what makes the context undefined in repeated elements that come after the initial limitTo is increased. What I can verify is that removing the limitTo filter causes the behavior to be fine in each element throughout. What I can also verify is that the initial 3 elements do not work if I set the initial limitTo value to 0 and increase it after.

Looking at the source code for limitTo leads me to believe that a new array is constructed each time the amount you're limiting to changes. As to my understanding, this should cause angular to remove all the DOM elements and then change them, but I cannot tell if that change would change affect this in any way.

I know that there's not much to work off of, but I am lost as to how to debug this and could appreciate any help, or if there's any behavior in ng-repeat that I'm not aware of that could explain this.

2

There are 2 best solutions below

0
On BEST ANSWER

Apparently, the issue was that the wrong data was being cached in the angular-tooltip library. The entire request for the template was being parsed, rather than just the content of it. The issue had nothing to do with ngRepeat; the first n-items before the limitTo would fire off a GET request because the template data had not yet populated the templateCache, but later on they would be trying to access the content of the entire request.

1
On

I would guess that elem isn't added to the dom "fast enough" when you update numPotentialMergesVisible.

Try the following:

myApp.directive('companyProfileTooltip', ['$tooltip', ($tooltip) => {
    return {
        restrict: 'EA',
        scope: { profile: '@companyProfileTooltip' },
        link: (scope: ng.IScope, elem) => {
            var tooltip = $tooltip({
                target: elem,
                scope: scope,
                templateUrl: "/App/Private/Content/Common/company.profile.html",
                tether: {
                    attachment: 'middle right',
                    targetAttachment: 'middle left',
                    offset: '0 10px'
                }
            });
             $timeout(()=> {
                $(elem).hover(() => {
                   tooltip.open();
               }, () => {
                    tooltip.close();
               });
           },1);
        }
    };
}]);

This way the hover setup method will be executed after the $scope variable value change has been handled.