简体   繁体   中英

need help understanding angular ui-router specifically passing mongodb data from state to state?

so I'm trying to pass my mongodb data from state to state using ui-router but having trouble making the links and controller as i'm making an app where users have a profile and are able to click on other people profile to see them. I'm able to get the entire list of users profiles but when click, it doesn't get the data so the user profile is blank.

app.js

angular.module('MyApp', ['ui.router']).config(function($stateProvider, $urlRouterProvider, $authProvider) {

/**
 * App routes
 */
$stateProvider
  .state('home', {
      url: '/',
      controller: 'HomeCtrl',
      templateUrl: 'partials/home.html'
  })
  .state('about', {
      url: '/about',
      templateUrl: 'partials/about.html'
  })
  .state('match', {
      url: '/match',
      controller: 'matchCtrl',
      templateUrl: 'partials/match.html'
  })
  .state('match.list', {
      url: '/list',
      controller: 'matchCtrl',
      templateUrl: 'partials/match.list.html'
  })
  //this part is where I need help on most with the controller as it is not working
  .state('match.profile', {
      url: '/:displayName',
      templateUrl: 'partials/match.profile.html',
      controller: function($scope, $stateParams) {
          $scope.user = $scope.getUserProfile[$stateParams.displayName];
      }
  });
  $urlRouterProvider.otherwise('/');

account.js

angular.module('MyApp').factory('Account',function($http,$stateParams) {
    return {
        getProfile: function() {
            return $http.get('/api/me/:id');
        },
        getAllProfile: function() {
            return $http.get('/api/me');
        },
        getUserProfile: function() {
            return $http.get('/api/me' + $stateParams.displayName);
        },
        updateProfile: function(profileData) {
            return $http.put('/api/me/:id', profileData);
        }
    };

});

this part works where the mongodb data shows up on the list of users match.list.html

<div ng-repeat="user in user">
    <div class="col-xs-12 col-sm-6 col-md-6">
        <div class="well well-sm">
            <div class="row">
                <h1>{{user.displayName}}</h1>
                <h1>{{user.age}} </h1>
                <a  ng-href="#/match/{{user.displayName}}">
                    See {{user.displayName}}!
                </a> 
            </div>
        </div>
    </div> 
</div>

the profile part doesn't work as clicking on the a ng-href only lead to a blank profile without data.

match.profile.html

<h1>{{user.displayName}}</h1>
<h1>{{user.age}}</h1>
etc...

how would I go about fixing this so when i click on a user profile using ng-href on the list part. It go to the user profile with the data? Are there any examples that I find similar to this problem with ui-router?

edit does it have something to do with my controller? match.js

angular.module('MyApp')
.controller('matchCtrl', function($scope, toastr,  Account) {
   // set up the filter
    $scope.sortUser = 'displayName';
    $scope.sortReverse = false;
    $scope.searchUser = '';

    // get all of the users
    $scope.getAllProfile = function () {
        Account.getAllProfile()
            .then(function (response) {
                $scope.user = response.data;
            })
            .catch(function (response) {
                toastr.error(response.data.message, response.status);
            });
    };

    $scope.getUserProfile = function () {
        Account.getUserProfile()
            .then(function(response) {
                $scope.user = response.data;
            })
            .catch(function (response) {
                toastr.error(response.data.message, response.status);
            });
    };
    // get the users
    $scope.getAllProfile();
    $scope.getUserProfile();


});

the rest api i'm using on node

app.get('/api/me/', function(req, res) {
User.find(function(err, user) {
res.send(user);
    });
    });



     app.get('/api/me/:id', ensureAuthenticated, function(req, res) {
 User.findById(req.user, function(err, user) {
  res.send(user);
     });
    });


  app.put('/api/me/:id', ensureAuthenticated, function(req, res) {
   User.findById(req.user, function(err, user) {
if (!user) {
  return res.status(400).send({ message: 'User not found' });
}
user.picture = req.body.picture || user.picture;
user.displayName = req.body.displayName || user.displayName;
user.email = req.body.email || user.email;
user.save(function(err) {
  res.status(200).end();
});

}); });

getUserProfile is a method in Account service. You have used

$scope.getUserProfile[$stateParams.displayName]

Change it to

Account.getUserProfile($stateParams.displayName);

It looks something like this

.state('match.profile', {
    url: '/:displayName',
    templateUrl: 'partials/match.profile.html',
    controller: function($scope, $stateParams, Account) {
        $scope.user = Account.getUserProfile[$stateParams.displayName];
    }
});

and you have missed a slash in getUserProfile function:

getUserProfile: function() {
    return $http.get('/api/me' + $stateParams.displayName);
},

which should be

getUserProfile: function(){
    return $http.get('/api/me/' + $stateParams.displayName).then(function(res){
        return res.data;
    });
}

You aren't calling method correctly getUserProfile , It not available there in $scope , you have to call it from Account service. Method call happens by parenthesis () not like [] . Next thing is, you can get data from getUserProfile method by putting .then function over it.

Code

.state('match.profile', {
    url: '/:displayName',
    templateUrl: 'partials/match.profile.html',
    controller: function($scope, $stateParams, Account) {
        Account.getUserProfile($stateParams.displayName)
        .then(function(res){
           var data = res.data;
           $scope.user = data;
        }, function(error){
           console.log(error);
        });
    }
});

Your match.profile controller is never resolving the promise that's returned from the API by getUserProfile , which is why the UI is blank.

First off, the controller needs the Account service injected into it, as others have noted. The getUserProfile method needs to be called correctly (use () instead of []).

controller: function($scope, $stateParams, Account) {
  $scope.user = Account.getUserProfile($stateParams.displayName);
}

I'm also not sure that defining your Account factory to rely on $stateParams is going to work properly, since a factory is a singleton and $stateParams may not update properly as you change states; you'd have to check your Network tab in developer tools to ensure the API endpoint is being built correctly (or just log $stateParams inside the getUserProfile method). I think the better option though would be to take in the url variable as an argument. You're trying to pass it in anyway, but the method isn't expecting any arguments.

getUserProfile: function(displayName) {
  return $http.get('/api/me' + displayName);
}

So finally, your controller should look like this

controller: function($scope, $stateParams, Account) {
  Account.getUserProfile($stateParams.displayName)
  .then(function (profile) {
    $scope.user = profile;
  });
}

A few other tips with UI-Router

  • With UI-Router, you should be concerned primarily with states of the application, not URLs. The correct way to transition between states in UI-Router then is to use ui-sref instead of ng-href . Note that ui-sref takes a state name, not a url, so instead of <a ng-href="#/match/{{user.displayName}}"> , it'd be better to do <a ui-sref='match.profile({displayName: user.displayName})'> (note how you can still pass in your displayName variable to the $stateParams as an argument.

  • Your match.profile state is a perfect use case for a resolve function. Resolve functions allow you to load data before the state loads. This ensures that your data is always available to your state before the UI ever renders.

    .state('match.profile', { url: '/:displayName', templateUrl: 'partials/match.profile.html', resolve: { profile: function ($stateParams, Account) { return Account.getUserProfile($stateParams.displayName) .then(function (profile) { return profile; });
    } }, controller: function($scope, profile) { $scope.user = profile; } });

Notice how you can name the resolve function to be whatever you want, in this case profile . You can inject this directly into your controller and know for certain that your data will already be available to the UI as soon as the controller loads. No loading data, no resolving promises. This is much closer to the proper separation of concerns for a controller in the MVC architecture of Angular where a controller should not be concerned with loading its own data.

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