1

I am trying to figure out how to pass an Id into my PlayerDetailController and then use it in my service.

I have the following route in my main app.js:

var players = require('./routes/players');
app.use('/players', players);

And I have the following routes inside of the players route (routes/players.js):

var express = require('express');
var router = express.Router();

/* GET /players listing. */
router.get('/', function(req, res, next) {
    res.render('players', { title: 'Players', action: 'list'});
});

/* GET /player details. */
router.get('/:id', function(req, res, next) {
    res.render('players', { title: 'Player Details', action: 'details'});
});

module.exports = router;

And the following in my main players.ejs template:

<!-- other layout elements -->
<% if (action === 'details') { %>
    <% include partials/players/details %>
<% } else if (action === 'update') { %>
    <% include partials/players/update %>
<% } else if (action === 'remove') { %>
    <% include partials/players/remove %>
<% } else { %>
    <% include partials/players/list %>
<% } %>
<!-- other layout elements -->

And my deatils.ejs partial:

<div ng-controller="PlayerDetailsController">
    <div class="playerDetails">
        <p>
            <strong>{{ player.name}} - {{ player.position }} - {{ player.team }}</strong><br/>
            Touchdowns: {{ player.touchdowns }}<br/>
            Yards: {{ player.yards }}
        </p>
    </div>
</div>

And my PlayerDetiails service:

// PlayerDetails Service
app.factory('PlayerDetails', ['$http', function($http){
    // below is where I'm trying to inject the Id
    return $http.get('/api/player/:id');
}]);

But I have no idea how I can pass the Id that shows up in my route (ex. http://localhost:3000/players/550130d32f3345bc065f2ecf) into my controller so that my controller calls and receives the correct player data. How would I do this? I thought I could add a id property to the res.render call, but I'm not sure what I would do with it in the EJS template. Any help would be greatly appreciated!

Fillip Peyton
  • 3,637
  • 2
  • 32
  • 60

2 Answers2

1

The ID passed to the route will be available in req.params.id. You can retrieve the player from the database using the ID and pass the object to the view.

Something like this, using Mongoose ODM. If you are using another database/ODM/ORM the code will be different but the idea is the same:

/* GET /player details. */
router.get('/:id', function(req, res, next) {
    Player.find({ _id: req.params.id }, function(err, player) {
        if(err) return next(err);
        res.render('players', { title: 'Player Details', action: 'details', player: player });
    });
});

Then you will have access to the Player document via player attribute.

If you want to create an API that will return all the player data in JSON format, you can do it like this:

/* GET /api/player/:id */
router.get('/:id', function(req, res, next) {
    Player.find({ _id: req.params.id }, function(err, player) {
        if(err) return next(err);
        res.json(player);
    });
});
victorkt
  • 13,992
  • 9
  • 52
  • 51
  • This is close. I am using Mongoose DB, but wouldn't I just be able to pass `req.params.id` into the `res.render()` params too? I'm not sure what do do with the param after I pass it into `res.render()` though. – Fillip Peyton Mar 15 '15 at 03:18
  • Yes, you can pass the id directly to the view via `res.render()`. But then what would you do with it? Querying the database from the view isn't a very good practice. It's better if the view receives the player data already fetched from the DB. – victorkt Mar 15 '15 at 03:20
  • That's also what I was confused about. Ultimately, I want to be able to inject an id into my `PlayerDetails` service (just made some changes to the original question above) – Fillip Peyton Mar 15 '15 at 03:22
  • Your service is calling `/api/player/:id` but your view is in `/player/:id`, right? If that's the case, you should return the entire document returned from the database in the api route (`/api/player/:id`). This way you will have all information about the player in JSON format. Then you can use the data from this http request in your angular app. Updated my answer with an example. – victorkt Mar 15 '15 at 03:27
1

Pass the id to your template:

router.get('/:id', function(req, res, next) {
    res.render('players', { title: 'Player Details', action: 'details', id: req.params.id});
});

ng-init receives the id:

<div ng-controller="PlayerDetailsController" ng-init="id = <%= id %>">

PlayerDetails service:

app.service('PlayerDetails', ['$http', function($http){
    return {
         get: function(id) {
           return $http({
            url: '/api/player/:id',
            params: {id: id},
            method: 'GET'
           });
         }
    };
}]);

Somewhere in PlayerDetailsController:

// id is from ng-init
PlayerDetails.get($scope.id).success(function(player) {
   $scope.player = player;
}).error(function(error) {
   console.error(error);
});
Miguel Mota
  • 20,135
  • 5
  • 45
  • 64
  • Since I have the `ng-controller` in a separate .ejs partial and I am `include`ing it, I don't think I have access to putting `id` in the `ng-init`. Is there any way to pass an id into the ejs partial? – Fillip Peyton Mar 15 '15 at 04:29
  • I noticed there's a new version of EJS (v2+) that has the ability to pass params to the include (https://github.com/mde/ejs#includes). Now I have an id in the `ng-init`. – Fillip Peyton Mar 15 '15 at 04:40
  • Also, since I returned an object with just a `get()` function, I no longer have access to the `.success()` and `.error()` callbacks in my `PlayerDetailsController`. My js console claims "undefined (.success()) is not a function" – Fillip Peyton Mar 15 '15 at 04:44
  • 1
    @FillipPeyton right, so you have call the get() method. `PlayerDetails.get($scope.id).success(function(player) { console.log(player) });` – Miguel Mota Mar 15 '15 at 04:48
  • Makes sense. So now I'm getting this syntax error: https://docs.angularjs.org/error/$parse/syntax?p0=d2f0dbe0c08c0930d&p1=is%20an%20unexpected%20token&p2=17&p3=playerId%3D5501324d2f0dbe0c08c0930d&p4=d2f0dbe0c08c0930d – Fillip Peyton Mar 15 '15 at 05:02
  • Which looks like its coming from here: `ng-init="playerId=5501324d2f0dbe0c08c0930d"` But I'm not sure why... – Fillip Peyton Mar 15 '15 at 05:04
  • @FillipPeyton maybe you need to wrap it in quotes? ng-init="id='<%= id %>'">? – Miguel Mota Mar 15 '15 at 05:05
  • I tried that as well, but it leaves me with a `CastError: Cast to ObjectId failed for value "undefined..."`. I did a `console.log` in the `PlayerDetails.get()` function and the passed param `id` is `undefined`. Any idea why? – Fillip Peyton Mar 15 '15 at 05:22
  • I also found this idea to output the id to the browser window scope and use that as the `PlayerDetails.get(window.id)` param. This worked fine. It just seems that `$scope.id` in your solution isn't capturing the value from `ng-init`. – Fillip Peyton Mar 15 '15 at 05:24
  • Issue was the controller gets initialized before `ng-init` does: http://stackoverflow.com/questions/18608161/angularjs-variable-set-in-ng-init-undefined-in-scope/18608510#18608510 So I just `$scope.$watch`ed for `id` and ran `PlayerDetails.get($scope.id)` once `$scope.id` was changed. Not sure if this is the best practice, but it works. – Fillip Peyton Mar 15 '15 at 05:34
  • @FillipPeyton Yep, that's perfectly legal and it works `$scope.$watch('id', function(){ console.log($scope.id); });` – Miguel Mota Mar 15 '15 at 05:38