Dewey Dewey - 2 months ago 6
AngularJS Question

Angular state resolve not injecting into controller

I'm trying to get ui-router's resolve to pass its value to the controller

portalsForUserCtrl
.

Here is the router:

(function () {
'use strict';

var myApp = angular.module("myApp", ["common.services", "ui.router", 'ngMessages']);

myApp.config(["$stateProvider", "$urlRouterProvider",
function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/");

$stateProvider
.state("portalsForUser", {
url: "/userPortal/portalsForUser/:id",
templateUrl: "app/userPortal/portalsForUser.html",
controller: "portalsForUserCtrl as vm",
resolve: {
userPortalService: "userPortalService",
portalsForUser: function (userPortalService, $stateParams) {
var userId = $stateParams.id;
console.log(userId); //shows userId correctly
return userPortalService.getPortalsForUserPromise(userId)
.then(function (response) {
var userPortals = response.data;
console.log("userPortals", userPortals); //shows portals
return userPortals;
});
}
}
})
}]
);


Here is the entire controller:

(function () {
"use strict";

angular.module("myApp")
.controller("portalsForUserCtrl", portalsForUserCtrl);

portalsForUserCtrl.$inject = ['portalsForUser', 'userPortalService'];

function portalsForUserCtrl(portalsForUser, userPortalService) {
console.log("in portalsForUserCtrl");
var vm = this;
vm.portalsForUser = portalsForUser;
console.log(portalsForUser);
}

}());


In mainCtrl, which is the controller for index.html, I call:

$state.go("portalsForUser", ({ "id": userId }));


Here is the code for the view app/userPortal/portalsForUser.html:

<div class="container">
<table class="table table-condensed table-striped table-bordered">
<tbody>
<tr>
<th class="col-md-2">&nbsp;</th>
<th class="col-md-4">
Portal Name
</th>
</tr>

<tr ng-repeat="userPortal in vm.portalsForUser">
<td>
{{userPortal.portal.portalName}}
</td>
<td class="">
<a class="btn btn-primary" ui-sref="goSomewhere({id: userPortal.portal.id})">
Go
</a>
</td>
</tr>
</tbody>
</table>




Here is the code for the userPortalService:

(function () {
"use strict";

angular.module("myApp")
.service('userPortalService', userPortalService);

userPortalService.$inject = ['userPortalResource', '$http', 'appSettings']

function userPortalService(userPortalResource, $http, appSettings) {

var getPortalsForUserPromise = function (id) {
return $http.get(appSettings.serverPath + '/api/UserPortal/GetPortalsForUser/' + id);
};

return {
getPortalsForUserPromise: getPortalsForUserPromise
};
}

}());


The url changes to the correct
/userPortal/portalsForUser/:id
but the
portalsForUserCtrl
function does not fire. It is only when I hit enter on the same url that portalsForUserCtrl is instantiated and the data appears in the view. What am I missing?

Answer

On the documentation (https://angular-ui.github.io/ui-router/site/#/api/ui.router.state.$stateProvider) the specification of the method mentions the following:

The map object is:

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

with the following as an example:

resolve: {
    myResolve1:
        function($http, $stateParams) {
          return $http.get("/api/foos/"+stateParams.fooID);
        }
    }

So I suggest you change your code into one of these options to make it as simple a possible and, using chrome developer tool, place a breakpoint on the first line on the method:

resolve: {
    portalsForUser: ['userPortalService', '$stateParams', function (userPortalService, $stateParams) {
        var userId = $stateParams.id; //place your breakpoint here
        return userPortalService.getPortalsForUserPromise(userId);
    }] 
}

Check what is going on with $stateParams; it is not impossible that, for some reason, at this moment, everything is not initialized yet because values don't come from the url, therefore, the id property is undefined. Try to inject "$state" and see if $state.params.id contains what you expect instead. (like mentioned in here: https://angular-ui.github.io/ui-router/site/#/api/ui.router.state.$state). Here is what it could look like:

resolve: {
    portalsForUser: ['userPortalService', '$state', function (userPortalService, $state) {
        var userId = $state.params.id; //place your breakpoint here
        return userPortalService.getPortalsForUserPromise(userId);
    }] 
}

Hope that if it doesn't solve your problem, at least it will help you to find it.

EDIT:

It seems all the previous doesn't go to the right direction.

Here is my new direction: I used your plunker to create a hosted site on my computer (using http-server: https://www.npmjs.com/package/http-server). My version that doesn't seem to be very different than yours works perfectly. Here is the full code:

app.js:

(function () {
    'use strict';

    var myApp = angular.module("myApp", ["ui.router"]);

    myApp
        .config(config)
        .controller("portalsForUserCtrl", portalsForUserCtrl)
        .service('userPortalService', userPortalService)
        .controller("mainCtrl", mainCtrl)

    mainCtrl.$inject = ["userPortalService", "$state"];

    function mainCtrl(userPortalService, $state) {
        var vm = this;

        vm.clickMe = function () {
          var userId = 1;
            $state.go("portalsForUser", { "id": userId });
        }
    };

    config.$inject=["$stateProvider"];

    function config($stateProvider) {
            $stateProvider
                // PortalsForUser GET
                .state("portalsForUser", {
                    url: "/userPortal/portalsForUser/:id",
                    templateUrl: "portalsForUser.html",
                    controller: "portalsForUserCtrl as vm",
                    resolve: {
                        portalsForUser: ['userPortalService', '$stateParams', function (userPortalService, $stateParams) {
                            return userPortalService.getPortalsForUserPromise($stateParams.id).then(function(response){return response.data;});
                        }]
                    }
                })
        }

    userPortalService.$inject = ['$http', '$q', '$timeout']

    function userPortalService($http, $q, $timeout) {
        var getPortalsForUserPromise = function (id) {
            var myId=id;
            var deferred=$q.defer();

            $timeout(function(){
                deferred.resolve({data:[
                    {
                    id: 16,
                    portal: {
                        portalName: "Portal1-" + myId,
                        portalId: 1
                    }
                    },
                    {
                    id: 17,
                    portal: {
                        portalName: "Portal2-" + myId,
                        portalId: 2
                    }
                    }
                ]});
            },5000);
            return deferred.promise;
        };

        return {
            getPortalsForUserPromise: getPortalsForUserPromise
        };
    };

    portalsForUserCtrl.$inject = ['portalsForUser', 'userPortalService'];

    function portalsForUserCtrl(portalsForUser, userPortalService) {
        console.log("in portalsForUserCtrl");
        var vm = this;
        vm.portalsForUser = portalsForUser;
        console.log(portalsForUser);
    };
}());

index.html:

<html>
    <head></head>
</html>
<body ng-app="myApp">
    <!-- bower:js -->
    <script src="/bower_components/angular/angular.js"></script>
    <script src="/bower_components/angular-ui-router/release/angular-ui-router.js"></script>
    <!-- endbower -->

    <!-- inject:js -->
    <script src="app.js"></script>
    <!-- endinject -->
    <body ng-app="myApp" ng-controller="mainCtrl as vm">
        <button type="submit" class="btn btn-default" ng-click="vm.clickMe()">
            Click Me
        </button>
        <div ui-view></div>
    </body>
</body>

portalsForUser.html:

<div class="container">
    Portals For User
    <table class="table table-condensed table-striped table-bordered">
        <tbody>
            <tr>
                <th class="col-md-2">&nbsp;</th>
                <th class="col-md-4">
                    Portal Name
                </th>
            </tr>

            <tr ng-repeat="userPortal in vm.portalsForUser">
                <td>
                    {{userPortal.portal.portalName}}
                </td>
                <td class="">
                    <a class="btn btn-primary" ui-sref="goSomewhere({id: userPortal.portal.id})">
                        Go
                    </a>
                </td>
            </tr>
        </tbody>
    </table>

</div>

bower.json

{
  "name": "test",
  "description": "just a test",
  "main": "index.js",
  "authors": [
    "me"
  ],
  "license": "ISC",
  "homepage": "index.html",
  "private": true,
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "test",
    "tests"
  ],
  "dependencies": {
    "angular": "^1.5.8",
    "angular-ui-router": "ui-router#^0.3.1"
  }
}

I added the div ui-view in index.html like suggested by somebody else, but I believe this was already in your initial project. I also tried to simulate the service like the real one would work (with a promise and with a property data).

Are you sure you have correct versions of ui-router and angular?