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

2

6

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.

Mirabel answered 12/11, 2014 at 2:7 Comment(1)
Followup: adding a timeout does not help, and this issue also affects any tooltips that are generated on a page I visit after my first pageload (ie: load page at /company/id and the first 3 tooltips work, change state to /company/id2 and none will work).Mirabel
M
0

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.

Mirabel answered 14/11, 2014 at 0:41 Comment(0)
B
0

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.

Broomfield answered 13/11, 2014 at 8:16 Comment(1)
This was a pretty good thought, but unfortunately didn't change anything. I really expected this one to work, seems like a lot of angular issues are solved with a clever timeout.Mirabel
M
0

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.

Mirabel answered 14/11, 2014 at 0:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.