reacting on my parent controller to be done with loading

Issue

I have a MainController that loads an array of Objects via RestAngular

controllers.controller('MainController', $scope, Restangular) {
    $scope.colors = {};
    Restangular.all('colors').getList().then(function(colors) {
        angular.forEach(colors, function(c) {
            c.brightness = getBrightness(c);
            $scope.colors[c.id] = c;
        });
    });
};

And I have a routing to have a sub-page for dealing with colors.

$stateProvider
   .state('picture', {
           abstract: true,
           url: "/picture/{pictureId:[0-9]{1,6}}",
           templateUrl: "partials/picture.html",
           controller: 'MainController'
          }
   )
   .state('picture.colors', {
           url: "/colors",
           templateUrl: "partials/picture_colors.html",
           controller: 'PictureColorsController'
           }
   );

Here, I want to have a drop-down menu to multi-select colors.

<multiselect ng-model="selector.colors"
             options="c as c.name for c in colors| orderBy:'brightness'"
             data-multiple="true"
             header="Colors">
</multiselect>

This works well. But when the page is loaded, I want all colors to be selected. So what I want is this:

controllers.controller('PictureColorsController', $scope) {
    $scope.selector = {colors:[]};
    var selectAll = function() {
        $scope.selector.colors.splice(0, $scope.selector.colors.length);
        angular.forEach($scope.colors, function(c) {
            $scope.selector.colors.push(c);
        });
    };
    selectAll();
};

But at the time the the child controller ‘PictureColorController’ is executed, the colors aren’t loaded yet. So the only solution I can think of is following, but I don’t really like it. It feels like I’m doing something wrong.

controllers.controller('MainController', $scope, Restangular) {
    $scope.colors = {};
    var colorsCallback = undefined;
    $scope.registerColorsCallbackFn = function(func) {
        colorsCallback = func;
    };
    Restangular.all('colors').getList().then(function(colors) {
        angular.forEach(colors, function(c) {
            c.brightness = getBrightness(c);
            $scope.colors[c.id] = c;
        });
        if (colorsCallback) {
            colorsCallback();
        }
    });
};

and

controllers.controller('PictureColorsController', $scope) {
    $scope.selector = {colors:[]};
    var selectAll = function() {
        $scope.selector.colors.splice(0, $scope.selector.colors.length);
        angular.forEach($scope.colors, function(c) {
            $scope.selector.colors.push(c);
        });
    };
    selectAll();
    $scope.registerColorsCallbackFn(selectAll);
};

Is there a cleaner way to do this? There are several lists of data that I want to load in my MainController that all need to be loaded before I want to execute any of the child controllers. Is there a good mechanism for that?

Solution

You can use ui-router’s resolve property on the state:

.state('picture.colors', {
           url: "/colors",
           templateUrl: "partials/picture_colors.html",
           controller: 'PictureColorsController'
           },
           resolve: {
             colors: function(Restangular) {
               return Restangular.all('colors').getList();
             }
           }
   )

ui-router will resolve every promise in the resolve object before executing your controller. And it’ll give you a colors parameter you can inject in your controller with the data that the promise resolves with.

There’s not necessarily anything wrong with your approach, though. A slightly more structured version is one of the approaches in John Papa’s Angular style guide.

Answered By – JeffB

Answer Checked By – David Goodson (AngularFixing Volunteer)

Leave a Reply

Your email address will not be published.