Query MongoDB with Mongoose within Angular App - angularjs

I have an app that needs to query my Mongo Database and return all items that match the specified criteria so they can be rendered to the DOM. I have tested the database with both postman and locally through my app, due to this I have determined that all information is being stored correctly. My problem is that I don't entirely know where the query should take place within my app.
The user will use a drop down to specify a type of business, once this is done all business that match this type should populate the DOM. Below is the code that I have so far:
This is the user controller:
angular.module('UserCtrl', [])
.controller('UserSelectController', function($scope, queryFactory) {
//list out all of our specialty types of food in the below array -- this will populate in our dropdown selection for our user_BusDirectory.html view
$scope.listOfTypes = ['Type 1', 'Type 2', 'Type 3', 'Type 4', 'Type 5', 'Type 6', 'Type 7', 'Type 8'];
//invoke function to call a GET request to get business with that type of specialty
$scope.getBusiness = function(){
console.log('You selected: ', $scope.selectedType);
queryFactory.queryType($scope.selectedType);
};
});
The following resides in my factory:
angular.module('queryService', [])
.factory('queryFactory', function($http){
var queryType = function(type){
console.log('What is the type that has been passed in: ',type)
var query = businesses.find({specialty: type}).exec(function(err, businessMatches){
if(err){
console.log(err);
return res.send({ errorMessage : err})
}else{
res.json(businessMatches);
}
});
console.log("Did query recieve all the types? :", query);
}
return {
queryType: queryType
}
});
Within my Mongo database businesses is the name of the collection that I would like to query. I keep getting ReferenceError: businesses is not defined when I try to test the function which leads me to believe that my approach is misguided.

I spent some time to give you and idea what you structure should look like.
Your API handler on the server should look like this:
app.get('api/businesses', function(req, res) {
Businesses.find({specialty: req.query.type})
.then(function(businesses){
res.json(businesses);
})
.catch(function(error){
console.log("There was error retrieving businesses" + error);
});
});
and on the Front End the factory that makes http call should look like:
angular.module('queryService', [])
.factory('queryFactory', function($http){
var getBusinesses = function(type) {
return $http({
method: 'GET',
url: 'api/businesses?type=' + type
})
};
return {
getBusinesses: getBusinesses
}
});
and Controller has to do something with data after response comes back:
angular.module('UserCtrl', [])
.controller('UserSelectController', function($scope, queryFactory) {
$scope.listOfTypes = ['Type 1', 'Type 2', 'Type 3', 'Type 4', 'Type 5', 'Type 6', 'Type 7', 'Type 8'];
$scope.getBusiness = function(){
queryFactory.getBusinesses($scope.selectedType)
.then(function(response){
// do something with response.data
// put it on $scope.businesses
});
};
});

'businesses' is undefined because it has not been assigned. Your code is missing any server calls to retrieve the data. You need:
A REST call to get the data
Suggest passing 'type' in the server call so the endpoint returns only the data needed.
queryType should return a promise ($q) that is resolved when the data is returned.

Related

Testing Angular-Resource: Expected an Object, got a Resource

When trying to test some simple angular code using $resource, I end up with a Resource object which contains a $promise key and hence a failure of the form: Failure/Error: Expected Resource(...) to equal Object(...)
I was expecting to get back the object I passed to the respond method as part of httpBackend.expectGET('/books/5.json').respond(my_book). Am I using $resource wrong or is something wrong in my test?
Code
var bookApp = angular.module('bookApp',
[
'ngResource',
]
);
function BookController(scope, $resource) {
var ctrl = this;
var Book = $resource('/books/:selected.json', {selected:'#id'});
ctrl.fetch_book = function(id){
console.log('Selecting options ' + id);
Book.get({selected:id}, function(data){
console.log('Received: ' + JSON.stringify(data));
ctrl.current_book = data;
});
};
}
BookController.$inject = ['$scope', '$resource'];
bookApp.component('book', {
controller: BookController
});
Test
describe('component: tree', function() {
var component_controller, $componentController, httpBackend, my_book;
beforeEach(module('bookApp'));
beforeEach(inject(function($httpBackend, _$componentController_) {
httpBackend = $httpBackend;
$componentController = _$componentController_;
}));
describe('$ctrl.fetch_book(book_id)', function(){
beforeEach(function() {
component_controller = $componentController('book');
my_book = {title: 'Sanctuary', id: '5'};
});
it('fetches the book with id=book_id', function(){
httpBackend.expectGET('/books/5.json').respond(my_book);
component_controller.fetch_book(5);
httpBackend.flush();
console.log('Options: ' + JSON.stringify(component_controller.current_book));
console.log('constructor: ' + JSON.stringify(component_controller.current_book.constructor.name));
expect(component_controller.current_book).toEqual(my_book);
});
});
});
Result
$ bundle exec teaspoon -f documentation
component: tree
$ctrl.fetch_book(book_id)
fetches the book with id=book_id (FAILED - 1)
# Selecting options 5
# Received: {"title":"Sanctuary","id":"5"}
# Options: {"title":"Sanctuary","id":"5"}
# constructor: "Resource"
Failures:
1) component: tree $ctrl.fetch_book(book_id) fetches the book with id=book_id
Failure/Error: Expected Resource({ title: 'Sanctuary', id: '5',
$promise: Promise({ $$state: Object({ status: 1, value:
<circular reference: Object> }) }), $resolved: true }) to equal
Object({ title: 'Sanctuary', id: '5' }).
Finished in 0.02600 seconds
1 example, 1 failure
Try this in your tester:
expect(component_controller.current_book).toEqual(angular.toJSON(my_book));
It'll strip the object's properties and you'll have a match.
You can also try angular.equals but I haven't tested that.
Try adding the following to your spec file and see if it works. I saw it in the PhoneCat example and it worked for me.
beforeEach(function() {
jasmine.addCustomEqualityTester(angular.equals);
});
You can try doing something like this:
expect(component_controller.current_book.toJSON()).toEqual(my_book);
I had the same issue where I got an error of
Expected object to be a kind of Object, but was Resource(
This is what I had before:
expect(self.project).toEqual(mockProject);
And after I added .toJSON() it was all good:
expect(self.project.toJSON()).toEqual(mockProject);
Hope this helps!

GET request format using mongoose and angular

In regard to the getRooms function, I expected to console.log, on partial page load (/rooms) , an array of objects containing roomName, moderator, and description as outlined by my mongoose model (Room) and data in the db, so that I could render some of this information to the page. Instead I am console logging what appears to be my index.html code as response on the client side and the server is never reached. My POST and PUT requests are working, and although this is rudimentary, it seems I am not understanding how to properly go about making this GET request. If someone could inform me as to how this is done properly, I would appreciate it.
//roomController.js
angular.module('chatApp').controller('roomController', ['$scope','$http','$location', '$cookies', function($scope, $http, $location, $cookies){
// $scope.rooms = [
// {'name': 'Biology', 'description': 'Discuss the wonders of Bio'},
// {'name': 'Literature', 'description': 'From Steinbeck to Shakespeare'},
// {'name': 'Dark Souls 3', 'description': 'Discuss gameplay from DS3'},
// {'name': 'The Life of Pablo', 'description': "Discuss Kanye West\'s the Life of Pablo"},
// {'name': 'Daredevil', 'description': 'Discuss the Netflix original Daredevil'},
// {'name': 'React JS', 'description': 'Discuss ReactJS projects'}
// ];
$scope.getRooms = function(){
$http.get('/rooms').then(function(response){
$scope.roomCount = response.data.length;
console.log(response.data.length);
console.log(response);
});
};
$scope.createRoom = function(){
var newRoom = {
roomName: $scope.roomName,
moderator: $cookies.get('currentUser'),
description: $scope.roomDescription
};
$http.post('/createRoom', newRoom).then(function(){
$scope.roomName = '';
$scope.moderator = '';
$scope.description = '';
$location.path('/createRoom');
bootbox.alert('Sucessfully created Room.');
});
};
}]);
//server side route
//get rooms
app.get('/rooms', function(req,res){
Room.find({}, function (err, rooms) {
res.send(rooms);
console.log(rooms);
});
});
//relevant part of partial page
<div class="container-fluid" id="roomsPage" data-ng-init="getRooms()">
check your serverside routes. youre logging the index.html page because your request isnt hitting any express routes. so instead it hits the app.get(*) route and its returning the html of your index.html page. make sure everything is spelled right and youre using a get on the other end instead of a post unless you mean to

Mongoose and Angular Populate Issue

I am new to mongoose and Angular and I am having an issue with mongoose's populate method. I have the following two mongoose schemas
var JobSchema = new mongoose.Schema({
jobName: String,
jobType: String,
status: String,
examples: [{type: mongoose.Schema.Types.ObjectId, ref: 'Example'}]
});
mongoose.model('Job', JobSchema);
and
var ExampleSchema = new mongoose.Schema({
content: String,
job: {type: mongoose.Schema.Types.ObjectId, ref: 'Job'}
});
mongoose.model('Example', ExampleSchema);
So basically the Job schema contains Example's. I also have the following Express route for getting the examples from a particular Job. I used this tutorial to figure out how to do this.
var Job = mongoose.model('Job');
var Example = mongoose.model('Example');
router.get('/jobs/:job', function (req, res) {
req.job.populate('examples', function (err, job) {
if (err) {return next(err);}
res.json(job);
});
});
Also, I am using the following to automatically retrieve the job from mongo and attach it to req.
router.param('job', function (req, res, next, id) {
var query = Job.findById(id);
query.exec(function (err, job) {
if (err) {
return next(err);
}
if (!job) {
return next(new Error('can\'t find job'));
}
req.job = job;
return next();
});
});
I also have the following Angular factory that uses this route
app.factory('jobs', ['$http', function ($http) {
var o = {
jobs: []
};
o.get = function (id) {
return $http.get('/jobs/' + id).then(function (res) {
return res.data;
});
};
return o;
}]);
I also created the following state which is supposed to immediately populate the examples for a given Job id using the above factory.
.state('jobs', {
url: '/jobs/{id}',
templateUrl: '/jobs.html',
controller: 'NerCtrl',
resolve: {
post: ['$stateParams', 'jobs', function ($stateParams, jobs) {
return jobs.get($stateParams.id);
}]
}
});
The problem comes when I actually try to show the examples using a controller.
app.controller('NerCtrl', [
'$scope',
'job',
function ($scope, job) {
$scope.examples = job.examples;
}]);
The view that tries to use $scope.examples just displays {{examples}} rather than the actual content of the scope variable. In fact, nothing in the controller seems to work with the `job` injection (not even simple 'alerts').
It looks the problem comes from the `job` injection in the controller. This is supposed to refer to the job that is retrieved in the resolve given the id but it doesn't look like this is working.
In addition, I have curled an example record's url (eg. curl http://localhost:3000/jobs/56920a1329cda48f16fc0815) and it does return the desired Job record, so it does look like the route part is working correctly. I suspect the problem is somewhere in the 'resolve' or the way in which I am injecting the result of the resolve into the controller.
Ok this was a silly mistake. The post inside the Job state should have been job. i.e.
.state('jobs', {
url: '/jobs/{id}',
templateUrl: '/jobs.html',
controller: 'NerCtrl',
resolve: {
job: ['$stateParams', 'jobs', function ($stateParams, jobs) {
return jobs.get($stateParams.id);
}]
}
});
In my inexperience, I did not know what post was referring to, but I suppose it refers to the job that is returned from jobs.get($stateParams.id) which is then the name that gets injected in the controller. So obviously the name in resolve must be consistent with what is injected in the controller.

Use a custom filter with a controller

I've created a filter that checks the user id and then filters out all results in a ng-repeat that don't have that user id.
If I place my filter,
UserService.getCurrentUser()
.then(function(user_id){
$scope.user_id = user_id;
});
In my controller it works fine, but I want to keep my controller as clean as possible so I want to move it out.
I created a file called "userFilter.js" and this is the code,
angular.module('addMovieseat', [])
.filter('user_filter', function() {
UserService.getCurrentUser()
.then(function(user_id){
$scope.user_id = user_id;
});
});
But when I inject the filter into my controller I get an error,
Error: [$injector:unpr] Unknown provider: user_filterProvider <- user_filter <- addMovieCtrl
Also this is my service where I get my current user id,
(function(){
"use strict";
angular.module('addMovieseat')
.factory('UserService', function($http, $q){
return{
getCurrentUser: function(){
var user_id = null,
deferred = $q.defer();
// Get the current user id and use it to filter out all movies that do not correspond to that id in the main overview.
$http.get(('/users.json'),{ cache: true}).
success(function (data, status, headers, config) {
if (status == 200) {
deferred.resolve(data.id);
} else {
deferred.resolve(false);
console.error('Error happened while getting the user list.')
}
});
return deferred.promise;
}
}
});
})();
It sounds as if you might be better served using ng-if on your repeated items. Like this
<li ng-repeat="movie in movies" ng-if="movie.user_id == user_id"></li>
Assuming $scope.user_id in your controller is the id being checked for, and your list of movies is an array. Like this:
$scope.user_id = 'Tim';
$scope.movies = [
{name: 'Movie Title 1', user_id: 'Tim'},
{name: 'Movie Title 2', user_id: 'Jane'},
{name: 'Movie Title 3', user_id: 'Bob'}
];
This will only render the movie with user_id 'Tim'.
EDIT: Documentation for ng-if
EDIT 2: Based on the comment by OP, updated code to reflect specific issue.
i actually think what your are looking for is
<li ng-repeat="movie in movies|filter:{user_id:user:id}:strict"></li>
as shown here
https://docs.angularjs.org/api/ng/filter/filter
using ng-if might lead to weird behavior under certain circunstances make sure you read https://docs.angularjs.org/api/ng/directive/ngIf

Angular and MEAN.js $scope attributes are undefined

I am currently developing a little app with Angular and MEAN.js.
I would like to plot a D3 chart with some real data coming from the model.
To do so, I would have to read from the $scope.campaign variable and initialize the $scope.dataBarChart with the data from $scope.campaign.tokens that is an array.
// Find existing Campaign
$scope.findOne = function() {
$scope.campaign = Campaigns.get({
campaignId: $stateParams.campaignId
});
$scope.dataBarChart = [
{
'key': 'Token Requested',
'values': [['10.10.2014', 550000], ['11.10.2014',300000]]
}, {
'key': 'Token Consumed',
'values': [['10.10.2014', 250000], ['11.10.2014',200000]]
}
];
};
When I try to log the value of $scope.campaign, I get all the data that I need. Unfortunately, when I try to access the $scope.campaign.tokens, I get an error like impossible to access tokens from undefined value. Basically, it seems that there is no data, but I know from the log that is not like this.
The complete code is simply the same, but with a console.log line
// Find existing Campaign
$scope.findOne = function() {
$scope.campaign = Campaigns.get({
campaignId: $stateParams.campaignId
});
console.log($scope.campaign)
$scope.dataBarChart = [{
'key': 'Token Requested',
'values': [['10.10.2014', 550000], ['11.10.2014',300000]]
}, {
'key': 'Token Consumed',
'values': [['10.10.2014', 250000], ['11.10.2014',200000]]
}];
};
The console.log shows the right content, but when I try to use it $scope.campaign.tokens, it says undefined.
Anyone suggestions?
Thanks
Try
$scope.campaign.$promise.then(function(data) {
console.log(data.tokens)
});

Resources