简体   繁体   中英

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?

You have a syntax error in the $state.go statement.

Change this:

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

to this:

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

There might be an issue with dependency injection. Try this -

resolve: {
           portalsForUser: ['userPortalService', '$stateParams', function (userPortalService, $stateParams) {
           var userId = $stateParams.id;
                 return userPortalService.getPortalsForUserPromise(userId)
                        .then(function (response) {
                            var userPortals = response.data;
                            console.log("userPortals", userPortals);
                            return userPortals;
                        });
                }] 
          }

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?

Based in your code, I've seen that the your controller it's associated to the module clubSkedApp and your config it's associated to the myApp module.

Use the same module for both, or include the module of your controller like this.

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

Another approach is check why the state it's not loaded. Ui-router isn't good to raise errors, the only way that i find to check the errors in a route's change is the following:

myApp.run(runFn);

runFn.$inject = ['$rootScope'];

function runFn($rootScope){

  //Show the errores caused by the resolve function
  $rootScope.$on('$stateChangeError', function (event, toState, toParams, 
    fromState, fromParams, error) {
      event.preventDefault();
      console.log(error);
    });
}

I know the problem. The solution is very simple.

You need to add <div ui-view></div> into index.html to display your view in the later state like below code.

  <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>

For more detail

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM