dyelawn
dyelawn

Reputation: 760

Understanding ng-if and DOM compilation

I have two ng-ifs defined in the same template that call functions of the same controller:

<div ng-controller="UserController">
    <!-- WORKS :) -->
    <p>{{ user.name }}</p>

    <!--WORKS :)-->
    <div ng-if="showThingOne()">
        <h2>Thing One</h2>
    </div>

    <!--DOESN'T WORK :( ; TypeError: Cannot read property 'role' of undefined -->
    <div ng-if="showThingTwo(user)">
        <h2>Thing Two</h2>
    </div>
</div>

angular.module('MyApp')
    .controller('UserController', ['$rootScope', '$scope', 'User', function ($rootScope, $scope, User) {
        var getCurrentUser = function () {
            User.current(function (user) {
               $scope.user = user;
               $rootScope.currentUser = user;
            });
        };
        getCurrentUser();

        $scope.showThingOne = function () {
            return $scope.user.role === 'Thing One Seer';  
        };

        $scope.showThingTwo = function (user) {
            return user.role === 'Thing Two Seer';  
        };
    }]);

The second conforms with a bunch of things I've read about unit testing, while the first is always marked "Don't do it this way", so I'd like to write my functions the second way. Also, I'd like to understand why the first works and the second doesn't; I'm guessing it has something to do with how the DOM is compiled during $digest, but that's just a random guess with no real support in the Angular source or docs.

Upvotes: 3

Views: 182

Answers (2)

Alborz
Alborz

Reputation: 6903

The User.current is an async function so the result get ready after the dom compiled.

showThingOne has no argument to be evaluated at compile time.

Solution

Add

 $scope.user = {};

as the first line of user controller.

   angular.module('MyApp')
        .controller('UserController', ['$rootScope', '$scope', 'User', function ($rootScope, $scope, User) {
$scope.user = {};
            var getCurrentUser = function () {
                User.current(function (user) {
                   $scope.user = user;
                   $rootScope.currentUser = user;
                });
            };
            getCurrentUser();

            $scope.showThingOne = function () {
                return $scope.user.role === 'Thing One Seer';  
            };

            $scope.showThingTwo = function (user) {
                return user.role === 'Thing Two Seer';  
            };
        }]);

Upvotes: 1

Davin Tryon
Davin Tryon

Reputation: 67316

This doesn't work because user is never being used:

$scope.showThingTwo = function (user) {
    return $scope.user.role === 'Thing Two Seer';  
};

Only $scope.user.role is being used (but not the user parameter) and it is still set to the 'Thing One Seer' value.

If you use it like this it might work this way you are intenting:

$scope.showThingTwo = function (user) {
    if (user) {
        return user.role === 'Thing Two Seer';  
    }
    return false;
};

Upvotes: 0

Related Questions