angularjs-directiveangularjs-ng-show

How do I get the ng-show to work inside an AngularJS directive?


I'm having trouble getting ng-show to work inside my directive template. I'm using AngularJS 1.6.4. The Chrome debugger shows the successful change from ng-show="true" to ng-show="false" in the final DOM. But the element stays hidden when set to true. It appears this is because AngularJS adds a ng-hide class to the list in the element, but does not remove this when ng-show changes to true. Maybe AngularJS does not evaluate at this stage? How do I get this to show/hide properly?

I've been playing around with this for a while and have tried may different approaches including using the loading parameter directly instead of using the scoped showspinner. I've also tried omitting the mustaches (AngularJS expression) in the directive template like so: ng-show="showspinner" but this makes it worse by just rendering to ng-show="showspinner" instead of ng-show="false".

Following is my code.

The $ctrl.result is loaded asynchronously while the $ctrl.resultsLoading is set to true, when the results are done loading it's set to false:

<report-tile
     title="My Report Item"
     value="{{$ctrl.result.count|number:0}}"
     loading="{{$ctrl.resultsLoading}}">
</report-tile>

This is my ReportTileDirective.js

(function(angular) {
"use strict";

angular
    .module("app")
    .directive(
        "reportTile", 
        ["$templateCache", "$compile" ,function($templateCache, $compile) {
            return {
                restrict: "EA",
                scope: {
                    title: "@",
                    value: "@",
                    loading: "@"
                },
                link: function (scope, element, attribues) {
                    scope.showspinner = false;
                    scope.$watch("loading",
                        function () {
                            scope.showspinner = attribues.loading;
                            console.log("watching::loading::" + attribues.loading);

                        });
                },
                templateUrl: "app/directives/ReportTileDirective.html"
            };
        }]);
}(window.angular));

This is my ReportTileDirective.html

<div class="col-sm-4 col-md-3 col-lg-2 col-padding">
<div class="panel panel-default">
    <div class="panel-heading tile-title">
        <strong>{{title === '' ? 'Loading' : title}}</strong>
    </div>
    <div class="panel-body" style="text-align: right">
        <strong>
            <i ng-show="{{showspinner}}" class="fa fa-refresh fa-spin"></i>
            {{value === '' ? 0 : value}}
        </strong>
    </div>
</div>

Finally this is the rendered DOM (as shown in the Chrome debugger Elements tab) when loading is done and it switches to true, the ng-hide is not removed:

<i ng-show="true" class="fa fa-refresh fa-spin ng-hide"></i>

Please help! Thank you!


Solution

  • I found my question is a duplicate of this one and thanks to @CodeWarrior's answer I was able to fix this. I can remove the whole link: section and use the loading parameter directly if: I bind it with = instead of @ and then get rid of the expression syntax, so that this is evaluated in the directive rather than beforehand.

    So my directive usage changes to:

    <report-tile
        title="My Report Item"
        value="{{$ctrl.result.count|number:0}}"
        loading="$ctrl.resultsLoading"> <!-- notice no mustaches here -->
    </report-tile>
    

    and my directive JavaScript file changes to:

    (function(angular) {
    "use strict";
    
    angular
        .module("app")
        .directive(
            "reportTile", 
            ["$templateCache", "$compile" ,function($templateCache, $compile) {
                return {
                    restrict: "EA",
                    scope: {
                        title: "@",
                        value: "@",
                        loading: "=" /* notice the = binding */
                    },
                    templateUrl: "app/directives/ReportTileDirective.html"
                };
            }]);
    }(window.angular));
    

    Then finally my directive template changes to:

    <div class="col-sm-4 col-md-3 col-lg-2 col-padding">
    <div class="panel panel-default">
        <div class="panel-heading tile-title">
            <strong>{{title === '' ? 'Loading' : title}}</strong>
        </div>
        <div class="panel-body" style="text-align: right">
            <strong>
                <i ng-show="loading" class="fa fa-refresh fa-spin"></i> <!-- notice to mustaches here -->
                {{value === '' ? 0 : value}}
            </strong>
        </div>
    </div>