Question

I have this code in angular:

<span ng-mouseover="item.show_description=true" ng-mouseleave="item.show_description=false" pointer="{x: item.x, y: item.y}">
    {{item.label}}
</span>
<div class="description-popup" ng-show="!!item.description && item.show_description"
     style="left: {{item.x}}px; top: {{item.y}}px">
    <h2>{{item.label}}</h2>
    <p>{{item.description}}</p>
    <p>{{!!item.description && item.show_description}}</p>
</div>

It show popup correctly but if descritpion is null or empty string the popup still shows up. The last expression in that case show false. What I'm doing wrong here? Or maybe is there a bug there. I'm using Angular 1.0.6 (can't upgrade right now).

UPDATE:

I've create JSFiddle and it's seems that ng-show work as expected but not when I use pointer directive, that use mousemove event. The code for the directive is like this:

app.directive('pointer', function($parse) {
    function objectParser(expr) {
        var strip = expr.replace(/\s*\{\s*|\s*\}\s*/g, '');
        var pairs = strip.split(/\s*,\s*/);
        if (pairs.length) {
            var getters = {};
            var tmp;
            for (var i=pairs.length; i--;) {
                tmp = pairs[i].split(/\s*:\s*/);
                if (tmp.length != 2) {
                    throw new Error(expr + " is Invalid Object");
                }
                getters[tmp[0]] = $parse(tmp[1]);
            }
            return {
                assign: function(context, object) {
                    for (var key in object) {
                        if (object.hasOwnProperty(key)) {
                            if (getters[key]) {
                                getters[key].assign(context, object[key]);
                            }
                        }
                    }
                }
            }
        }
    }



    return {
        restrict: 'A',
        link: function(scope, element, attrs) {
            var expr = objectParser(attrs.pointer);
            element.mousemove(function(e) {
                var offest = element.offset();
                scope.$apply(function() {
                    expr.assign(scope, {
                        x: e.pageX - offest.left,
                        y: e.pageY - offest.top
                    });
                });
            });
        }
    };

});
Was it helpful?

Solution

Isolate scope

The problem is that ng-show uses the scope of the element it's on. If it's on an element that has a directive with isolate scope, then it will use that isolate scope, and not have access to the outside scope.

In this particular case, I suspect that description-popup has isolate scope, which means ng-show only has access to the scope of that directive, which probably doesn't have the item that you're trying to test against.

What is isolate scope?

Isolate scope means the directive has its own scope that does not inherit from the surrounding scope. Normal scopes inherit from the surrounding scopes, so they have access to the data on that surrounding scope. Isolate scope does not.

Why would anyone ever want to use isolate scope?!

Reuse. If a directive is intended to be reused in lots of places, in lots of different scopes with a variety of properties on them, you don't want properties on the directive's scope to collide with the properties of the surrounding scope. And since the directive is supposed to be totally independent and not use the surrounding scope at all, it's often a good idea to give it an isolate scope. There are very few disadvantages to this. Really, the only thing that's likely to go wrong is when someone wants to put an ng-show or ng-hide on that element.

Solution

Put your ng-show on an extra <div> around the description-popup:

<div ng-show="!!item.description && item.show_description">
    <div class="description-popup"
         style="left: {{item.x}}px; top: {{item.y}}px">
        <h2>{{item.label}}</h2>
        <p>{{item.description}}</p>
        <p>{{!!item.description && item.show_description}}</p>
    </div>
</div>

This assuming that the behaviour in this JSFiddle is what you want.

This is probably only true for versions of Angular < 1.2. In 1.2, the behaviour of isolate scopes seems to have been cleaned up: https://docs.angularjs.org/guide/migration#isolate-scope-only-exposed-to-directives-with-scope-property

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top