32

I've seen this and this but it seems like there might be a simpler way.

In my view I have several menu options that are controlled through permissioning - i.e., not everyone can see a "Dashboard" view. So in my menu option in my view I have something like the following:

<li ng-show="validatePermission('Dashboard')">Dashboard</li>

In my controller I have a validatePermission method defined where it is looking at the permissions of the current user. For example:

  $scope.validatePermission = function(objectName) {
    if $scope.allPermissions......

Also in my controller I'm loading those permissions via an $http call:

  $http.get('permissions/' + userid + '.json').success(function(data) {  
    $scope.allPermissions = data;....

The issue is that $scope.allPermissions doesn't get loaded before the view makes the call to validatePermission. How can I wait for allPermissions to be loaded before the view renders?

Community
  • 1
  • 1
Arthur Frankel
  • 4,695
  • 6
  • 35
  • 56

5 Answers5

21

You ask:

How can I wait for allPermissions to be loaded before the view renders?

To prevent the entire view from rendering, you must use resolve. You don't have to use the promise library though, since $http returns a promise:

var app = angular.module('app');

app.config(function ($routeProvider) { 
  $routeProvider
    .when('/', {
        templateUrl : 'template.html',
        controller : 'MyCtrl',
        resolve : MyCtrl.resolve
  });
});

function MyCtrl ($scope, myHttpResponse) {
   // controller logic
}

MyCtrl.resolve = {
  myHttpResponse : function($http) {
    return $http({
        method: 'GET',
        url: 'http://example.com'
    })
    .success(function(data, status) {
        // Probably no need to do anything here.
    })
    .error(function(data, status){
        // Maybe add an error message to a service here.
        // In this case your $http promise was rejected automatically and the view won't render.
    });
  }
}

But if you simply want to hide the dashboard <li>, then do as Joe Gauterin suggested. Here's a very simple example plunkr if you need it.

Rick Jolly
  • 2,949
  • 2
  • 23
  • 31
11

Have the validatedPermission function return false when allPermissions hasn't been loaded. That way the element with your ng-show won't be displayed until allPermissions has been loaded.

Alternatively, put an ng-show="allPermissions" on the enclosing <ul> or <ol>.

JoeG
  • 12,994
  • 1
  • 38
  • 63
  • Thank you. Does that mean that the validatePermissions function will be called multiple times? i.e., maybe initially when allPermissions isn't loaded, then would it really just call it again after it's loaded. How does it know to call it again (after it's loaded)? Wouldn't it just be called once? – Arthur Frankel May 18 '13 at 02:39
  • I selected this because it seems the simplest and works - although I'm not 100% sure why. I guess Angular initially calls the validatePermissions function on displaying the view and even though allPermissions isn't loaded (and I return false) it will call the function again when allPermissions is loaded - somehow knows to call this function again. – Arthur Frankel May 18 '13 at 02:55
  • @ArthurFrankel - Yes, the expression in the `ng-watch` will be evaluated once per cycle. Angular as a whole works like this - `ng-show` isn't "show if the expression is true", it's "show *while* the expression is true". The other options are good if you really need to dealy until permissions are loaded but, as far as I can tell, you don't here. – JoeG May 18 '13 at 18:15
  • Re. 'how does it know to call it again' - the ng-show directive watches the value of the expression, and the $http service causes all relevant watches to be evaluated after a response to an http request is received. It's really too complicated a topic to go into detail in a comment. – JoeG May 18 '13 at 18:22
  • Oh man... I've been working on this problem for *hours*. This is elegant, and simple; which is probably why I didn't solve it on my own. =] – kneeki Jul 13 '17 at 18:19
6

You can also specify on your routecontroller a resolve object that will wait for that object to resolve prior to rendering that route.

From the angular docs: https://docs.angularjs.org/api/ngRoute/provider/$routeProvider

resolve - {Object.=} - An optional map of dependencies which should be injected into the controller. If any of these dependencies are promises, they will be resolved and converted to a value before the controller is instantiated and the $routeChangeSuccess event is fired. The map object is:

key – {string}: a name of a dependency to be injected into the controller. factory - {string|function}: If string then it is an alias for a service. Otherwise if function, then it is injected and the return value is treated as the dependency. If the result is a promise, it is resolved before its value is injected into the controller.

A google group reference as well: https://groups.google.com/forum/#!topic/angular/QtO8QoxSjYw

Eric C
  • 3,886
  • 3
  • 30
  • 26
lucuma
  • 18,247
  • 4
  • 66
  • 91
4

I encountered an similar situation, you might also want to take a quick look at

http://docs.angularjs.org/api/ng/directive/ngCloak

if you're still seeing a "flicker" effect.

As per the angularjs documentation:

The ngCloak directive is used to prevent the Angular html template from being briefly displayed by the browser in its raw (uncompiled) form while your application is loading. Use this directive to avoid the undesirable flicker effect caused by the html template display.

NatraxYN
  • 99
  • 4
  • Welcome to Stack Overflow! Whilst this may theoretically answer the question, [it would be preferable](http://meta.stackexchange.com/q/8259) to include the essential parts of the answer here, and provide the link for reference. – user13500 Mar 10 '14 at 23:41
  • `ng-cloak` has helped me in the past before with the exact same situation – Robert Johnstone Feb 01 '16 at 13:45
2

Wrapping the code in ng-if fixed the issue for me:

<div ng-if="dependentObject">
  <!-- code for dependentObject goes here -->
</div>
WiredIn
  • 4,157
  • 4
  • 27
  • 23