I'm not even exactly sure what the problem is here, I've been trying to figure it out for a couple days. I'm somewhat new to the MEAN stack, so I'm probably missing something obvious. I basically copied the boilerplate code for the mean.io back-end controllers, models, and routes and Angular controllers, views, and services. I'm now getting this error when I create a new Client via the browser:
CastError: Cast to ObjectId failed for value "undefined" at path "_id"
at ObjectId.cast (/Users/d/Web/personal/mean-app/node_modules/mongoose/lib/schema/objectid.js:116:13)
at ObjectId.castForQuery (/Users/d/Web/personal/mean-app/node_modules/mongoose/lib/schema/objectid.js:165:17)
at Query.cast (/Users/d/Web/personal/mean-app/node_modules/mongoose/lib/query.js:2270:32)
at Query.findOne (/Users/d/Web/personal/mean-app/node_modules/mongoose/lib/query.js:1117:10)
at Query.exec (/Users/d/Web/personal/mean-app/node_modules/mongoose/node_modules/mquery/lib/mquery.js:2181:16)
at Query.exec (/Users/d/Web/personal/mean-app/node_modules/mongoose/lib/query.js:1748:19)
at Function.ClientSchema.statics.load (/Users/d/Web/personal/mean-app/app/models/client.js:80:48)
at exports.client (/Users/d/Web/personal/mean-app/app/controllers/clients.js:15:12)
at paramCallback (/Users/d/Web/personal/mean-app/node_modules/express/lib/router/index.js:151:7)
at param (/Users/d/Web/personal/mean-app/node_modules/express/lib/router/index.js:133:11)
controllers/clients.js:
exports.client = function(req, res, next, id) {
Client.load(id, function(err, client) {
if (err) return next(err);
if (!client) return next(new Error('Failed to load client ' + id));
req.client = client;
next();
});
};
models/client.js:
ClientSchema.statics.load = function(id, cb) {
this.findOne({ _id: id }).populate('user').exec(cb);
};
When I insert a client via the mongo shell manually (db.clients.save({ name: "Bob" })), it saves and appears in Node, but when I go to edit it via the Angular view, it can't save, giving me a similar error:
TypeError: Cannot read property 'id' of undefined
I'm thinking that perhaps this means that my back-end code is find and it's the front-end code that isn't communicating all the necessary data to the back-end. For reference:
public/js/controllers/clients.js
angular.module('mean.clients').controller('ClientsController', ['$scope', '$routeParams', '$location', 'Global', 'Clients', function ($scope, $routeParams, $location, Global, Clients) {
$scope.global = Global;
$scope.create = function() {
var client = new Clients({
name: this.name,
contactName: this.contactName
});
client.$save(function(response) {
$location.path('clients/' + response._id);
});
this.name = '';
this.contactName = '';
};
// etc.
}]);
public/js/services/clients.js
angular.module('mean.clients').factory('Clients', ['$resource', function($resource) {
return $resource('clients/:clientId', {
clientId: '#_id'
}, {
update: {
method: 'PUT'
}
});
}]);
You will need to include the _id in your client object.
Related
I´m working on an android game using ionic framework and firebase.
My plan is to let users login using facebook login with firebase, after this i want to save the game data to the users database key.
The first part is working. the script makes an database array based on the users facebook details. but the problem is after this is made, i cant seem to let angular change any database data. It seems like the authData is stuck in the login function...
Is there a way to keep the authdata for use in different controllers and functions?
app.factory("Auth", function($firebaseAuth) {
var FIREB = new Firebase("https://name.firebaseio.com");
return $firebaseAuth(FIREB);
});
app.controller('HomeScreen', function($scope, Auth, $firebaseArray) {
Auth.$onAuth(function(authData){
$scope.authData = authData;
});
var users = new Firebase("https://name.firebaseio.com/users/");
// create a synchronized array
$scope.users = $firebaseArray(users);
$scope.facebooklogin = function() {
Auth.$authWithOAuthPopup("facebook").then(function(authData){
users.child(authData.facebook.cachedUserProfile.id).set({
Username: authData.facebook.displayName,
Id: authData.facebook.cachedUserProfile.id,
Gender: authData.facebook.cachedUserProfile.gender,
Email: authData.facebook.email,
level: "1"
});
}).catch(function(error){
});
}
$scope.facebooklogout = function() {
Auth.$unauth();
}
$scope.changeLVL = function(authData) {
users.child(authData.facebook.cachedUserProfile.id).set({
level: "2"
});
}
});
And this is the datastructure it creates in firebase
users
998995300163718
Email: "Name#email.com"
Gender: "male"
Id: "998995300163718"
Username: "name lastname"
level: "1"
and after trying to edit i get this error... (using the changelevel function)
TypeError: Cannot read property 'facebook' of undefined
at Scope.$scope.changeLVL (http://localhost:8100/js/controllers.js:35:23)
at fn (eval at <anonymous> (http://localhost:8100/lib/ionic/js/ionic.bundle.js:21977:15), <anonymous>:4:218)
at http://localhost:8100/lib/ionic/js/ionic.bundle.js:57606:9
at Scope.$eval (http://localhost:8100/lib/ionic/js/ionic.bundle.js:24678:28)
at Scope.$apply (http://localhost:8100/lib/ionic/js/ionic.bundle.js:24777:23)
at HTMLButtonElement.<anonymous> (http://localhost:8100/lib/ionic/js/ionic.bundle.js:57605:13)
at HTMLButtonElement.eventHandler (http://localhost:8100/lib/ionic/js/ionic.bundle.js:12103:21)
at triggerMouseEvent (http://localhost:8100/lib/ionic/js/ionic.bundle.js:2870:7)
at tapClick (http://localhost:8100/lib/ionic/js/ionic.bundle.js:2859:3)
at HTMLDocument.tapMouseUp (http://localhost:8100/lib/ionic/js/ionic.bundle.js:2932:5)
The main issue is you're relying on the cachedUserProfile.gender property to exist. This isn't guaranteed to be there for every user. You'll need to find a fallback to avoid an error.
Let's simplify by injecting the user via the resolve() method in the router. Don't mind the structure of the code, it's from the Angular Styleguide (my preferred way of writing Angular apps).
angular.module("app", ["firebase"])
.config(ApplicationConfig)
.factory("Auth", Auth)
.controller("HomeScreen", HomeController);
function Auth() {
var FIREB = new Firebase("https://name.firebaseio.com");
return $firebaseAuth(FIREB);
}
function ApplicationConfig($stateProvider) {
$stateProvider
.state("home", {
controller: "HomeScreen",
templateUrl: "views/home.html"
})
.state("profile", {
controller: "ProfileScreen",
templateUrl: "views/profile.html",
resolve: {
currentUser: function(Auth) {
// This will inject the authenticated user into the controller
return Auth.$waitForAuth();
}
}
});
}
function HomeController($scope, Auth, $state) {
$scope.googlelogin = function() {
Auth.$authWithOAuthPopup("google").then(function(authData) {
users.child($scope.authData.google.cachedUserProfile.id).set({
Username: $scope.authData.google.cachedUserProfile.id,
Gender: $scope.authData.google.cachedUserProfile.gender || ""
});
$state.go("app.next");
});
}
}
function ProfileController(currentUser) {
console.log(currentUser.facebook); // injected from router
}
The benefit of this approach is that you don't have to check for authenticated users in the controller. If the user is injected, you know you have an authenticated user.
Check out the AngularFire docs for more information.
Trying to return a list of articles specific to a sectionId in angular controller. I can get back the entire list of articles using articles.query but the passed sectionId query param gets completely ignored. I tried a to abstract it to an articles service but I'm not sure how to build services correctly yet so it threw more errors. Any help / examples of how I might achieve this, preferable as a service, would be great. I am using mean.js. Thanks in advance!
Sections controller
angular.module('sections').controller('SectionsController', ['$scope', '$stateParams', '$location', 'Authentication', 'Sections', 'SectionArticlesList', 'Articles',
function($scope, $stateParams, $location, Authentication, Sections, SectionArticlesList, Articles) {
..........
// Find existing Section
$scope.findOne = function() {
$scope.section = Sections.get({
sectionId: $stateParams.sectionId // sections return fine
});
// problem starts here
$scope.articles = Articles.query({
section:$stateParams.sectionId // this param is ignored
});
$scope.articles.$promise.then(function(data) {
// all articles in collection returned instead of section specific
console.log('Articles: '+ JSON.stringify(data));
$scope.articles = data;
});
Articles Model
var ArticleSchema = new Schema({
created: {
type: Date,
default: Date.now
},
title: {
type: String,
default: '',
trim: true,
required: 'Title cannot be blank'
},
content: {
type: String,
default: '',
trim: true
},
section: {
type: Schema.ObjectId,
ref: 'Section'
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
UPDATE:
Following up on the advice of Kevin B and Brad Barber below, I tried adding a factory and node server route, passing the $stateParams.sectionId to the specific factory instance. I created a new route in the articles.routes.server to make sure not to have a conflict with the standard ‘/articles/:articleId’ route. Unfortunately, anything I try still either throws errors or everything in the articles collection which is the opposite of what I want to do.
section.client.controller
// Find existing Section
$scope.findOne = function() {
$scope.section = Sections.get({
sectionId: $stateParams.sectionId
});
// fails no matter what is passed to query
$scope.articles = SectionArticlesList.query($stateParams.sectionId);
//$scope.SectionArticlesList.query($stateParams.sectionId);
//$scope.articles = SectionArticlesList.query({sectionId});
$scope.articles.$promise.then(function(data) {
// still only ever returns full list of articles
console.log('Length of articles: '+ JSON.stringify(data.length));
$scope.articles = data;
});
….
articles.client.services
angular.module('articles').factory('SectionArticlesList', ['$resource',
function($resource) {
return $resource('articles/:articleSectionId', {
articleSectionId: '#_id'
}, {
update: {
method: 'PUT'
}
});
}
]);
articles.server.routes
// Custom route
app.route('/articles/:articleSectionId')
.get(articles.listBySectionID);
// Custom binding
app.param('articleSectionId', articles.listBySectionID);
articles.server.controller
The server side controller function never appears to get called because neither of the console.logs show up.
exports.listBySectionID = function(req, res, id) {
console.log('listBySection Called....'); // this is never printed
// have tried passing - id, sectionId and {section:id}
Article.find( {section:id} ).sort('-created').populate('user', 'displayName').exec(function(err, articles) {
if (err) {
console.log('listBySection err triggered...'); // this neither
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.json(articles);
}
});
};
I think I have tried everything I can think of to pass the sectionId correctly but nothing has worked yet. The latest error in the console is the 404 below.Interestingly the sectionId IS getting through but as if it is looking for one single resource such as a single blog post.
GET /sectionArticles?sectionId=555bfaf29a005c30cbfe6931 404
I don't quite understand how the default mean.js /articles route and it's corresponding list function works but duplicating a similar structure and passing an id as a param to the query to retrieve only specific results doesn't.
Would really like to understand how this should be wired up. If anyone can point out what I am doing wrong I’d appreciate it!
The issue here is, that i don't know how to pass some scope.data to expressjs when using ngResource, so then it can be used with express route to insert something to DB.
ExpressJS REST
router.route('/Data')
.get(function(req,res){
var username = req.username;
var collection = db.collection('users');
collection.find({username:username}).toArray(function (err, doc){
res.send(doc[0].pets);
});
})
.post(function(req,res){
!!//I would like to use some data from angular here//!!
var name = req.body.name;
var surname = req.bodysurname;
collection.update({username: username}, {
$push: {
"details": {
name: name,
surname: surname
}
}
}, function (err, result) {
if (err) throw err;
});
});
Angular Factory
(function() {
'use strict';
angular
.module('App')
.factory('Factory', function ($resource) {
return $resource("/Data",{},
{ get:{ method:"GET",
cache:true,
isArray:true},
save:{ method:"POST",
cache:true,
isArray:false
}});
});
})();
Controller.js
This one works fine i use this function with ng-click()
$scope.load = function(){
Factory.get(function (data){
$scope.data = data;
});
};
With this one i have problem i have ng-models name and surname in view and i would like to send them to server so it can be used in REST route as req.body.name and req.body.surname.
$scope.AddData = function(){
Factory.save()
});
};
I think that data should be passed here in this function AddData, however i haven't succeeded yet.
So i tried as Jesus said but without results?
$scope.AddData = function(){
Factory.save($scope.name) //I tried ({name:$scope.name}) too
});
};
After advices from Jesús Quintana I checked the details of method POST and it occurred that everything was all right on angular side i was missing extension of body parser on server Express side
app.use(bodyParser.json())
So now it looks like this
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
NgResource works like a class with private and public methods:
For example Factory.save() is a public method and you must pass the data to be stored in the server, for example:
$scope.AddData = function(factoryData){
Factory.save(factoryData);
});
};
But also have a private method and the above example is the same to this:
$scope.AddData = function(factoryData){
var factory = new Factory(factoryData);
factory.$save(); // Is the same method but is private because factory is a instance of the factory
});
};
Both example are valid methods but must be used of differents ways.
EDIT
I create this little plunkr to see the network request: http://plnkr.co/edit/1bdblyrsW0jr7rXIAVNn?p=
I am looking to have parameters in the route, by using a colon before the variable name
// dynamic pages for each ITEM, once selected
// from $routeParams.itemID in ItemCtrl
.when('/:itemID', {
templateUrl: 'views/item.html',
controller: 'ItemController'
})
When a div-box is clicked, Angular should route to the specific item
<div class="itemBox" ng-click="getItem(item._id)">
Right now, the call to the node/express API seems to be working
[16:36:18.108] GET http://localhost:8080/api/items/534240001d3066cc11000002 [HTTP/1.1 304 Not Modified 4ms]
But this error logs in the console:
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (http.js:691:11)
...
at Promise.<anonymous> (/Users/Username/Downloads/project/v19/app/routes.js:41:8)
...
Line 41 (for 41:8?) in routes.js is res.json(item);
// load the item model
var Item = require('./models/item');
// get One item
app.get('/api/items/:item_id', function(req, res) {
// use mongoose to get the one item from the database
Item.findById({
_id : req.params.item_id
},
function(err, item) {
// if there is an error retrieving, send the error. nothing after res.send(err) will execute
if (err)
res.send(err)
res.json(item); // return the item in JSON format
});
});
Though it seems like the issue might be in the Controller because all of the other API calls work.. And so I tried passing $routeParams all over the place!
angular.module('ItemCtrl', [])
// inject the Item service.factory into our controller
.controller('ItemController', function($scope, $routeParams, $http, Items, isEmptyObjectFilter) {
// get an Item after clicking it
$scope.getItem = function(id, $routeParams) {
Items.getOne(id, $routeParams)
// if successful getByID, call our function to get the Item data
.success(function(data, $routeParams) {
// assign our Item
$scope.item = data;
// for use with a parameter in appRoutes.js using itemID as the variable
$scope.itemID = $routeParams.itemID;
})
.error(function(data) {
console.log('Error: ' + data);
});
};
});
Or maybe it's the service? Does this need to pass $routeParams as function(id, $routeParams)
angular.module('ItemService', [])
// super simple service
// each function returns a promise object
.factory('Items', function($http) {
return {
get : function() {
return $http.get('/api/items');
},
getOne : function(id) {
return $http.get('/api/items/' + id);
},
create : function(itemData) {
return $http.post('/api/items', itemData);
},
delete : function(id) {
return $http.delete('/api/items/' + id);
}
}
});
Would really appreciate some assistance debugging this.. Thanks
It looks like you are getting the data correctly. The problem is that you want the route to change after successfully getting the API call?
$routeParams won't change the route for you. That just gets the data. Use $location to change the route.
.controller('ItemController', function($scope, $routeParams, $location, $http, Items, isEmptyObjectFilter) {
$scope.getItem = function(id) {
Items.getOne(id)
.success(function(data) {
$scope.item = data;
$scope.itemID = $routeParams.itemID;
// redirect
$location.path('/' + $routeParams.itemID);
});
});
});
Since all of your data seems to be ready to go, you just need Angular to redirect to the route. $location should be the way to go.
That message is because you are getting an error and executing the res.send() method, and after that you have res.json(), express is trying to respond twice.
Try changing:
if (err)
res.send(err)
To:
if (err) {
res.json({ error: err });
} else {
var object = item.toObject();
res.json(object);
}
Angular resource example:
angular.module('ItemService')
.factory('Items', ['$resource', function($resource) {
return $resource('/api/items/:itemID', {
itemID: '#_id'
}, {
update: {
method: 'PUT'
}
});
}]);
Now you can do this in your controller:
// Find
Items.get({
itemID: $routeParams.itemID
}, function(item) {
$scope.item = item;
});
// Update
$scope.item.name = 'New name';
$scope.item.$update();
// Remove
$scope.item.$remove();
I'm scratching my head on this.
I'm running an expressjs site with angularjs as my front-end resource, and my problem is that I've set up my API to query my datastore with a parameterized query. When I hit the route, the parameter is not appearing in my angularjs resource query, so I end up just getting the entire data set instead of one object by id.
/api/index.js:
var _getSingleRequest = function(req, res, next)
{
models.Request
.findOne({'_id': req.body.id})
.exec(function(err, request){
if(err) return res.send(500, err);
if(!request) return res.send(404, new Error("Request not found"));
res.send(request);
});
};
...
return {
_getSingleRequest: getSingleRequest
}
/server.js
...
var api = require('./api');
app.get('/api/request/:id', api.getSingleRequest);
...
/public/js/controllers/controller.js
...
function Request($scope, $resource, $routeParams, Request)
{
$scope.request = Request.query({_id : $routeParams.id});
...
}
...
/public/js/services/services.js
services.Request = function($resource)
{
return $resource('/api/request/:id', {id:'#id'}, {'get': {method:'GET', isArray: true}});
}
console
Mongoose: requests.find({}) { fields: undefined, safe: undefined, sort: [ [ 'requestedDate', 1 ] ] }
GET /api/request?_id=51b8cc2a06859bd418000001 304 179ms
it appears that the get request is coming through properly, but in the Mongoose query there are no parameters being passed.
what simple mistake am I making, and what other information can I post to help me figure out how to get out of the infinite loop of banging my head on the wall?
In your Request function, you're passing _id:
Request.query({_id : $routeParams.id});
^^^
But in your service, you're expecting id:
$resource('/api/request/:id', {id:'#id'}
^^^
That will generate requests looking like this (which is also what your log says):
/api/request/?_id=XXX
But your backend is expecting this:
/api/request/XXX
req.body.id is for the request body, not route parameters, which come from the request URI. You want req.params.id