Jen
Jen

Reputation: 2004

angularjs ng-show not displaying as expected

I'm probably getting confused with mvc and angularjs and trying to set a boolean to control a scope variable to hide a div.

I have a list html page that includes this:

 <tbody>{{isAuthorised}}
            <tr ng-repeat="calendarEvent in items" id="event_{{calendarEvent.Id}}">
                <td><strong>{{calendarEvent.EventTitle}}</strong><br/>{{calendarEvent.EventDescription}}</td>
                <td>{{calendarEvent.EventDate | date:mediumDate}}</td>
                <td><img src="{{calendarEvent.ThumbnailUrl}}" alt="" width="100" /></td>
                <td>
                    <div ng-show="isAuthorised"> 
                        <a href="#/edit/{{calendarEvent.Id}}"><i class="glyphicon glyphicon-edit"></i></a>
                        <a ng-click="delete()"><i class="glyphicon glyphicon-remove"></i></a>
                    </div>
                </td>
            </tr>
        </tbody>

I'm outputting the value currently to try to figure out what is going on. So if I hit this page with setting the value the div shows my edit and delete buttons which I don't want. The value of the scope variable displays as {}.

I have this app.js code:

 var ListCtrl = function ($scope, $location, CalendarEvent, SharedService) {

    ** lots of stuff removed as irrelevant **

        $scope.isAuthorised = SharedService.get();
    };

My login controller via a separate html content section that is setting the value (in the shared service)

var LoginCtrl = function ($scope, $location, $http, SharedService) {

    $scope.login = function () {
        $http.get("/AuthorisedUser/IsValidUser/" + $scope.item.ValidEmailAddress + "/")
        .success(function (result) {
            var isAuthorised = result.toLowerCase();
            if (isAuthorised) {
                SharedService.set(isAuthorised);
                $location.path('/');
            } else {
                alert('you do not have the power!');
            }

        })
        .error(function() {
             alert('Email could not be Validated at this time');
        });

    }
};

the result is an MVC method returning a bool type. I thought maybe I needed to convert the bool to lower case because javascript would like it better, but maybe that's doing some implicit conversion to a string or something?! I'm not sure what I need to change in my list html to properly show that div only when the value is true. I'm coming from a .NET background with limited AngularJS understanding.

The value seems to being set, because if I put in a valid email address I'm seeing

true

in the html page where the scope variable is.

It seemed to work once in Chrome - but now that's not working, and just showing the stuff that should be hidden.

Sorry forgot to include the shared service:

EventsCalendarApp.factory('SharedService', function() {
    var savedData = {}

    function set(data) {
        savedData = data;
    }

    function get() {
        return savedData;
    }

    return {
        set: set,
        get: get
    }
});

Upvotes: 1

Views: 182

Answers (3)

Michael Kang
Michael Kang

Reputation: 52867

I think everything would be simplified in your controller, service, and UI if your service dealt with object references rather than a Boolean value (which is a primitive).

Your service:

EventsCalendarApp.factory('SharedService', function() {
    var savedData = { isAuthorised: false }

    function set(data) {
        // overwrites savedData properties with data's properties, 
        // but preserves the reference
        angular.copy(data, savedData);
    }
    function setAuthorised(authorised) {
        savedData.isAuthorised = authorised;
    }

    function get() {
        return savedData;
    }

    return {
        set: set,
        get: get,
        setAuthorised: setAuthorised
    }
});

Your Login controller:

var LoginCtrl = function ($scope, $location, $http, SharedService) {

    // helper function to determine if str contains 'true'
    function parseBoolean(str) {
          return /^true$/i.test(str);
    }        
    $scope.login = function () {
        $http.get("/AuthorisedUser/IsValidUser/" + $scope.item.ValidEmailAddress + "/")
        .success(function (result) {
            var isAuthorised = parseBoolean(result);
            if (isAuthorised) {
                SharedService.set({ isAuthorised: isAuthorised });
                // OR
                SharedService.setAuthorised(isAuthorised);

                $location.path('/');
            } else {
                alert('you do not have the power!');
            }

        })
        .error(function() {
             alert('Email could not be Validated at this time');
        });

    }
};

Your List Controller:

 var ListCtrl = function ($scope, $location, CalendarEvent, SharedService) {

    ** lots of stuff removed as irrelevant **

        $scope.savedData = SharedService.get();
    };

HTML:

<tbody>{{savedData.isAuthorised}}
            <tr ng-repeat="calendarEvent in items" id="event_{{calendarEvent.Id}}">
                <td><strong>{{calendarEvent.EventTitle}}</strong><br/>{{calendarEvent.EventDescription}}</td>
                <td>{{calendarEvent.EventDate | date:mediumDate}}</td>
                <td><img ng-src="{{calendarEvent.ThumbnailUrl}}" alt="" width="100" /></td>
                <td>
                    <div ng-show="savedData.isAuthorised"> 
                        <a href="#/edit/{{calendarEvent.Id}}"><i class="glyphicon glyphicon-edit"></i></a>
                        <a ng-click="delete()"><i class="glyphicon glyphicon-remove"></i></a>
                    </div>
                </td>
            </tr>
        </tbody>

When you use object references, then any changes to the reference from within your service is automatically propagated to the views; as do any changes to the reference that happen inside a controller. There is no real magic behind this - they are automatically updated because they are the same reference. In contrast, when you use primitives, then a copy of the value is passed around, and it becomes more challenging to keep them all in synch.

NOTE: on an unrelated note, you should use ng-src for image URLs that are binding expressions. This ensures that the image URL is only downloaded by the browser after the expression is evaluated and rendered.

Upvotes: 1

Jen
Jen

Reputation: 2004

So the answer was to update the ListCtrl to have this logic:

var ListCtrl = function ($scope, $location, CalendarEvent, SharedService) {
    var authorised = SharedService.get();
    if (authorised != "true")
        $scope.isAuthorised = false;
    else
        $scope.isAuthorised = SharedService.get();
};

It now seems to be working! I'm still confused about the handling of booleans in javascript as I seem to have a mix of boolean and string going on in the various methods.

Upvotes: 0

micronyks
micronyks

Reputation: 55443

var LoginCtrl = function ($scope, $location, $http, SharedService) {

$scope.login = function () {
    $http.get("/AuthorisedUser/IsValidUser/" + $scope.item.ValidEmailAddress + "/")
    .success(function (result) {

        $scope.isAuthorised = result.toLowerCase();

    })
    .error(function() {
         alert('Email could not be Validated at this time');
    });

}

};

Keep one thing in mind you $scope works as a bridge between controller and view. if your controller update $scope, your view gets changed. Don't use sharedservice here. its useless for what you want to do. try my above snippet.

Upvotes: 0

Related Questions