0

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?

2
  • 1
    Any errors in your console? I also see no need to userPortalService: "userPortalService" in your resolve block Commented Sep 21, 2016 at 23:45
  • No errors in console. Removing the line you mention has no effect, so you are correct that it is not needed, thank you However, the change doesn't solve my problem. Commented Sep 22, 2016 at 12:43

5 Answers 5

1

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

Change this:

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

to this:

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

Sign up to request clarification or add additional context in comments.

6 Comments

Good catch, thank you, but the change has no effect. The "resolve" block is still reached with the correct stateParam.
Try writing the catch block too after the then block and console.log the error if any. Also, what do you get on line console.log("userPortals", userPortals); in resolve?
Added the catch block, but there is no error. In the console.log I see the userPortals, so they are being returned just fine. Problem is still that they are not being injected into the controller.
I don't think you need portalsForUserCtrl.$inject = ['portalsForUser', 'userPortalService']; please remove that and check
Removing it has no effect -- same result. How else would portalsForUserCtrl take the dependency if it's not injected or passed into the constructor?
|
0
+50

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?

6 Comments

I have added console.log line to my source code for the .state entry to show that the userId is correctly returned by $stateParams.
can you make a plunker for this? we can't possibly guess what is going on here...
did you try redoing a complete promise from the start? var def= $q.defer(); and in the callback: def.resolve(userPortals) instead of "return userPortals"
Plunker is at plnkr.co/edit/5sLccH5N6hZILfv1FR6k?p=info Let me know if it needs anything additional.
The issue is that I had a <div ng-if="vm.isLoggedIn"> <div ui-view></div> </div> block in the index.html. The isLoggedIn value was not set due to an error in code that I did not include. I am working through that issue now. Yours was the most helpful answer, if not entirely on point. So I'll give you the bounty. It was a good idea to make the plunker as well. I'll ask one more question: if the isLoggedIn value is false, does that mean that the portalsForUserCtrl will not instantiate? That seems to be the case.
|
0

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;
                        });
                }] 
          }

1 Comment

I tried this @Kalyan but it doesn't solve the problem. I still have to hit <enter> to cause the data to appear in the view.
0

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);
    });
}

2 Comments

$stateChangeError is an excellent idea. However, for my case it doesn't reveal any errors.
What about that the module names, are you tried to change the module name to the controller definition?
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.