1

I'm trying to get my parent / child view templates to render a value that should be populated in it's parent route in ui.router, but I'm too new to the process to understand where I've done wrong.

I have a nav bar that contains a username, and a profile page that displays all the user data in forms. I thought that using resolve in the route would pre-populate the data object in the controller, but something is off, because my input fields are capable of populating the "ng-model" inputs with the right user information from the same object, but all the places I try to use the {{}} tags in my HTML templates don't resolve.

Factory:

myApp.factory('userFactory', function($http, $q) {
    var service = {};

    delete $http.defaults.headers.common['X-Requested-With'];

    service.getCurrentUser = function() {
        var deferred = $q.defer();
        $http.get('/getmyuser') // returns a user Object, with "name", "profileImage", etc
        .success(function(response) {
            deferred.resolve(response);
        }).error(function(err) {
            deferred.reject(err);
        });
        return deferred.promise;
    }

    return service;
});

Controller:

function UserCtrl($scope, currentUser, userFactory) {
    $scope.currentUser = currentUser.currentUser;

    $scope.isLoggedIn = function() { // Works when checked in the same template as the elements that do not work.
        if ($scope.currentUser) return true;
        else return false;
    }

    $scope.isAdmin = function() { // When I check this with ng-show, it works.
        if ($scope.currentUser) {
            var roles = $scope.currentUser.roles;
            for (index = 0; index < roles.length; ++roles) {
                if (roles[index].name == "admin") return true;
            }
        }
        return false;
    }
}

The module:

var myApp = angular.module('myApp', ['ui.router'])
    .controller('userCtrl', ['$scope', 'currentUser', 'userFactory', UserCtrl])
    .config(function ($stateProvider,$urlRouterProvider, $locationProvider) {
        $locationProvider.html5Mode(true);

        $stateProvider
        .state('core', {
            url: '',
            abstract:true,
            resolve: {
                userFactory: 'userFactory',
                currentUser: function(userFactory) {
                    return userFactory.getCurrentUser().then(function(response) {
                        return { currentUser: response };
                    });
                }
            },
            views: {
                "nav@": {
                    templateUrl: '/api/v1/partials/nav',
                    controller: 'userCtrl'
                }
            }
        })
        .state('core.profile', {
            url: '/profile',
            views: {
                "contentArea@core": {
                    templateUrl: '/api/v1/partials/profile'
                }
            }
        });
    });

HTML file 1 (index.html, being served as an ng-include inside a valid HTML wrapping with header section, body section, source includes for angular, etc):

<div ui-view="nav"></div>

HTML file 2 (nav.html, most of it being omitted, as it's a standard bootstrap nav skeleton ... portion of interest added. There are no ng-controller assignment anywhere in my documents, as this breaks the resolve injections):

<li ng-show="isLoggedIn()"> <!-- This works -->
    <a ui-sref="core.profile">
        Welcome, {{currentUser.name}}! <!-- This does not -->
    </a>
</li>
<!-- ... more code until the end of the nav bar -->
<div ui-view="contentArea"></div>

There's a profile page as well that's a child, but the inputs are working on that one without me having to include anything extra, however none of the {{}}s are resolved on that page either, regardless of the inputs working automagically. I thought placing all my initialization code in a resolve block would solve this, but obviously I'm missing something.

Any help would be greatly appreciate!

EDIT: Turns out that ng-include (which I was using as my way of breaking up the app into smaller templates) was creating independent scopes that had nothing to do with userCtrl, unless you wanted to refer to it as "$parent" or drop things into $rootScope.

I restructured the app to not use ng-include, and now I can access my bindings via ng-bind and regular ol' $scope, instead of using $rootScope or bothering with ng-includes independent scoping. Thanks to everyone for reviewing that, and sorry for the wild goose chase in the wrong direction.

2 Answers 2

1

So a couple things, you probably will be reusing this resolve a lot since its a user system so I will suggest a slightly different, but similar approach.

For your factory you should be resolving the user within and then storing it so that you aren't making repeated requests.

myApp.factory('userFactory', function($http) {
    var service = {};

    delete $http.defaults.headers.common['X-Requested-With'];

    service.currentUser = null;

    service.init = function() {
        //you don't need $q here because $http returns a promise
        $http.get('/getmyuser').then(function(response) {
            service.currentUser = response.data;
        });
    };

    service.getCurrentUser = function() {
        return currentUser;
    };

    return service;
});

Then your config can look like:

.state('core', {
    url: '',
    abstract:true,
    resolve: {
        userFactory: function(userFactory) {
            userFactory.init();
        }
    },
    views: {
        "nav@": {
            templateUrl: '/api/v1/partials/nav',
            controller: 'userCtrl'
        }
    }
});

.state('profile', {
    url: '/profile',
    parent: 'core',
    views: {
        "contentArea@core": {
            templateUrl: '/api/v1/partials/profile'
        }
    }
});

As you can see, the way to import this state is by defining it as a parent. Now finally you can do this in your controller:

function UserCtrl($scope, userFactory) {
    $scope.currentUser = userFactory.getCurrentUser;
}

This is just the concepts, I didn't test this so you might have to tinker with it a bit but hopefully this gets you in the right direction.

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

7 Comments

Thanks again, Long. This works in Plunkr, but I'm having trouble figuring out why it isn't working in my app. I think it might have something to do with either HTML5 mode being enabled (which is missing from my Plunkr example here...) or the way I've got Node configured to catch all the unrecognized routes.
HTML5 mode shouldn't be an issue. What errors are you seeing or why does it not work? Is currentUser not getting resolved?
When I log currentUser to the console inside the resolve response, it is complete and being returned fine. Or so it seems, at least. There are no errors, neither server or client, unfortunately.
Anyway you can put your actual app code into a plunkr? Without context its hard to troubleshoot the issue.
Tyler, I notice you aren't setting the core state as a parent to the profile state in the plunkr, are you doing it in your app?
|
0

You must return only the promise:

resolve: {
            userFactory: 'userFactory',
            currentUser: function(userFactory) {
                return userFactory.getCurrentUser();
            }
        },

then, in the controller, it's not anymore

function UserCtrl($scope, currentUser, userFactory) {
$scope.currentUser = currentUser.currentUser;

but

function UserCtrl($scope, currentUser, userFactory) {
$scope.currentUser = currentUser; // the object itself is directly available

4 Comments

Unfortunately, these changes did not work. Still no binding resolution for my brackets in my templates. Thank you, though.
Could you add a simple plunkr?
I created a simple Plunkr example here... after having made all the changes to my code base that were recommended, but it works just fine in Plunkr while my code base still doesn't work ... and I don't see any difference other than the fact that I'm running HTML5 mode with Node configured to catch all unrecognized routes to serve up index.html.
I even changed my http server call "/getmyuser" to a flat file (as it appears in Plunkr, via the currentUser.js file), and the bind still didn't resolve. I'm starting to think it's a configuration issue more than a code one.

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.