I'm making a call to the server using resource and when I go to the base URL of
/viewEvent
It works fine. I receive all the database entries. However, when I go to
/viewEvent/1234
where 1234 is the eventID
I get a undefined is not a function and this is a crash from within angular. Stack trace is
TypeError: undefined is not a function
at copy (http://localhost:8000/js/lib/angular/angular.js:593:21)
at http://localhost:8000/js/lib/angular/angular-resource.js:410:19
at wrappedCallback (http://localhost:8000/js/lib/angular/angular.js:6846:59)
at http://localhost:8000/js/lib/angular/angular.js:6883:26
at Object.Scope.$eval (http://localhost:8000/js/lib/angular/angular.js:8057:28)
at Object.Scope.$digest (http://localhost:8000/js/lib/angular/angular.js:7922:25)
at Object.Scope.$apply (http://localhost:8000/js/lib/angular/angular.js:8143:24)
at done (http://localhost:8000/js/lib/angular/angular.js:9170:20)
at completeRequest (http://localhost:8000/js/lib/angular/angular.js:9333:7)
at XMLHttpRequest.xhr.onreadystatechange (http://localhost:8000/js/lib/angular/angular.js:9303:11) angular.js:575
When I examine the server, the request was made correctly. I can see that it got 1234 and it pulls the correct entry from the mongo database.
This is the controller logic
.controller("viewEventsController", ["$scope", 'EventService', '$location', function($scope, EventService, $location){
var path = $location.path().split('/');
var pathSize = path.length;
$scope.events = [];
if(pathSize === 2){
console.log("No event ID");
$scope.events = EventService.query();
}
else{
console.log("Event ID specified");
EventService.get({"eventID": path[pathSize - 1]}, function(data){
//$scope.events.push(data);
console.log(data);
}, function(error){
console.log(error);
});
}
}]);
and the service logic
service.factory('EventService', function($resource){
return $resource('api/viewEvent/:eventID');
});
It never makes it back to the controller so I'm "confident" it's not that. (watch it be that)
Not sure if the best way, but I got it working by doing
In service:
service.factory('EventService', function($resource){
return $resource('api/viewEvent/:eventID',
{eventID:"#eventID"},
{
'getSingleEvent': {
url: "api/viewEvent/:eventID",
method: "GET",
isArray: true
}
}
);
controller
var path = $location.path().split('/');
var pathSize = path.length;
EventService.getSingleEvent({"eventID":path[pathSize - 1]}, function(result){
$scope.updateEvent();
});
Server
routes = require('./routes')
var router = express.Router();
router.get('/api/viewEvent/:eventID', routes.viewEvent);
and in the routes directory I have a js file with
var mongoose = require('mongoose');
var db = mongoose.createConnection('localhost', 'eventApp');
var eventSchema = require('../models/createEvent.js').eventSchema;
var event = db.model('events', eventSchema);
exports.viewEvent = function(req, res){
console.log(req.params.eventID);
if(req.params.eventID) {
event.find({"_id": req.params.eventID}, function (error, events) {
console.log(events);
res.send(events);
});
}
else{
event.find({}, function (error, events) {
console.log(events);
res.send(events);
})
}
};
Related
As you can see below, in my server.js file I have a /POST Info request that gets called on a form submittal.
I started to get confused on reading about the different between app.post and express routes and if in anyway using routes would benefit my code here.
Within the /POST Info I have two axios requests to 2 different APIs and I think it would be wise to move the code elsewhere to make it cleaner.
Would knowing how routes work here benefit me anyway?And if you can explain the difference here that would be great.
app.post('/Info', function (req, res) {
var State = req.body.State;
var income = Number(req.body.income);
var zip = req.body.ZIP;
axios.post('https://taxee.io/api/v2/calculate/2017', {
//data sent to Taxee.io
"exemptions": 1
, "filing_status": "single"
, "pay_periods": 1
, "pay_rate": income || 100000
, "state": State || "NY"
}, {
headers: {
'Authorization': "Bearer <API_KEY>"
//headers
}
}).then(function (response) {
var obj = {
income: '$' + income
, fica: response.data.annual.fica.amount
, federal: response.data.annual.federal.amount
, residence: State + ", " + zip
, state: response.data.annual.state.amount
}
axios.get("https://www.quandl.com/api/v3/datasets/ZILL/Z" + zip + "_RMP.json?api_key=<API_KEY>").then(function (response) {
var monthRent = response.data.dataset.data[0][1]
obj.rent = monthRent
obj.yearlyRent = Number(monthRent) * 12;
}).then(function (response) {
res.send(obj);
});
}).catch(function (error) {
alert('error');
});
}
There are two ways to define routes in an Express application:
Use the Express application (app) object directly:
const express = require('express')
const app = express()
app.post(...)
app.get(...)
app.put(...)
// and so on
Or use the router object:
const express = require('express')
const app = express()
const router = express.Router()
router.post(...)
router.get(...)
router.put(...)
// and so on
app.use(router)
My guess is that you've been reading about the latter snippet of code with the router object. Using Express' Router object can indeed make code cleaner to read as there more of a separation of concerns.
There's nothing wrong with calling an external API from your own API. For example, in a project of mine, I call the Google Calendar API on this line. The only difference between mine is yours is that I used the Google APIs Node.js Client while you used standard HTTP requests. I could have certainly used HTTP requests as shown here.
Your code is fine, but can be improved. For example, instead of:
axios.post('...', {
exemptions: 1,
filing_status: 'single',
pay_periods: 1,
pay_rate: income || 100000,
state: State || 'NY'
})
You could call an helper function that prepares the options object:
function prepareOptions (state = 'NY', income = 100000) {
return {
exemptions: 1,
filing_status: 'single',
pay_periods: 1,
pay_rate: income,
state: State
}
}
Then call it like so:
axios.post('...', prepareOptions(State, income))
This makes for more readable code.
Finally, there is no reason to use axios on the server side. Simply use Node's built in HTTP module.
app.post('/Info', function (req, res) {
var uData ={
state: req.body.State,
income : Number(req.body.income),
zip: req.body.ZIP
};
taxee(uData).then(function(data){
return rent(data) ;
}).then(function(fullData){
res.send(fullData);
}).catch(function (error) {
res.render('error');
});
function taxee(data) {
return new Promise((resolve, reject) => {
var income = data.income;
var state = data.state;
var zip = data.zip;
axios.post('https://taxee.io/api/v2/calculate/2017', {
//data sent to Taxee.io
"exemptions": 1
, "filing_status": "single"
, "pay_periods": 1
, "pay_rate": income || 100000
, "state": state || "NY"
, }, header).then(function (response) {
var taxData = {
income: '$' + income
, fica: response.data.annual.fica.amount
, federal: response.data.annual.federal.amount
, stateTax: response.data.annual.state.amount
, state
, zip: zip
}
resolve(taxData);
}).catch(function (error) {
console.log('break');
resolve(error);
});
});
};
function rent(data) {
return new Promise((resolve, reject) => {
axios.get("https://www.quandl.com/api/v3/datasets/ZILL/Z" + data.zip + "_RMP.json?api_key=d7xQahcKCtWUC4CM1LVd").then(function (response) {
console.log(response.status, ' status');
var monthRent = response.data.dataset.data[0][1];
data.rent = monthRent
data.yearlyRent = Number(monthRent) * 12;
return data;
}).then(function (response) {
resolve( data);
}).catch(function (error) {
reject(error);
});
});
}
module.exports = {
taxee
, rent
};
Ended up putting the code above into clean promise methods. Really happy how it worked out!
I have a method in a NodeJS app that handles scraping a URL, and when successful, saving that data in a Mongo database, and showing the results.
Main method:
//url parameter
app.get('/urls/', function(req, res) {
var client = new MetaInspector(req.query.url, {
timeout: 5000
});
client.on("fetch", function() {
var imagesArray = [];
var keywordsArray = [];
var now = new Date();
var dateVal = dateFormat(now, "mm/dd/yyyy h:MM:ss");
for (var i = 0; i < client.images.length; i++) {
// we only want jpgs. nothing else.
if (client.images[i].indexOf('.jpg') > -1) {
imagesArray.push({
"image": client.images[i]
})
}
}
for (var i = 0; i < client.keywords.length; i++) {
keywordsArray.push({
"keyword": client.keywords[i]
})
}
var newUrls = Urls({
url: client.url,
date_added: dateVal,
keywords: req.body.keywords,
author: client.author,
description: client.description,
ogTitle: client.ogTitle,
ogDescription: client.ogDescription,
image: client.image,
images: imagesArray,
keywords: keywordsArray
});
newUrls.save(function(err) {
if (err) throw err;
res.send('Success' + newUrls);
});
});
client.on("error", function(err) {
console.log(err);
});
client.fetch();
});
This all works well and good. But I'm using Pug and Express and have specific routes setup. I'd like instead of sending the newUrls obj to the res.send, have it go to a particular route and pass it to a particular pug template I already have setup:
// Route.js
var express = require('express');
var router = express.Router();
var Urls = require('../models/urlModel');
var Footer = require('../models/footerModel');
/* URL Saved Success Page */
router.get('/saved', function (req, res) {});
});
module.exports = router;
My view lives in a pug file located at:
/views/saved.pug
div#body
include nav.pug
div.container.item-container
div.row
div.col-md-8
h1 Item successfully saved.
h5 {item}
h6 {description}
I've tried using the res.send method, but that doesn't work. Any suggestions on how to handle this?
For my understanding, you want the request redirected to /saved with payload after urls saved to database, in this scenario, you could user res.redirect with query string
newUrls.save(function(err){
var payload = JSON.stringify({
url: client.url,
date_added: dateVal,
keywords: req.body.keywords,
author: client.author,
description: client.description,
ogTitle: client.ogTitle,
ogDescription: client.ogDescription,
image: client.image,
images: imagesArray,
keywords: keywordsArray
})
//append the payload as a query string
res.redirect(`/saved?payload=${payload}`)
})
and in /saved route, you could parse the query and use res.render
router.get('/saved', function (req, res) {});
let payload = JSON.parse(req.query.payload);
if(payload){
res.render('saved', payload)
}
});
I have a form, which a user inserts product data in the databse. When the save button is pressed, the method POST occurs. After the Post, the GET method, is called. Apparently, this works in localhost but on Openshift the POST is executed but the list does't auto refresh.
Server.js (GET, POST method)
var ResourcesSchema = new mongoose.Schema({
name: String,
serialnumber: String,
modelno: String,
description: String
});
var Resources = mongoose.model("Resources", ResourcesSchema);
app.get("/resources", function(req, res){
Resources.find(function(err, resources)
{
res.send(resources);
});
});
app.post("/resources", function(req, res){
var resources = new Resources(req.body);
resources.save(function(err, doc){
console.log(doc);
res.json(doc);
});
});
resources.js
app.controller("ResCtrl", function($scope, $http) {
$http.get('/resources')
.success(function(response) {
var resource = "";
$scope.resources = response;
$scope.resource = "";
});
$scope.addResource = function() {
console.log($scope.resource);
$http.post('/resources', $scope.resource)
.success(function(response){
$scope.resources.push(response);
});
$http.get('/resources')
.success(function(response) {
var resource = "";
$scope.resources = response;
$scope.resource = "";
});
};
});
Found the solution:
Had to do a function named $scope.all to retrieve data from the database and i had to call it from inside the $scope.addResource function:
$scope.addResource = function() {
console.log($scope.resource);
$http.post('/resources', $scope.resource)
.success(function(response){
$scope.resources.push(response);
$scope.all();
});
};
$scope.all = function(){
$http.get('/resources')
.success(function(response) {
var resource = "";
$scope.resources = response;
$scope.resource = "";
});
};
so if i make a post to db, the $scope.all(); will auto-refresh the list
I would suggest to make GET request after successful POST. You may get not updated data because GET request happens before data saved into database (we are dealing with asynchronous behavior here).
app.controller("ResCtrl", function($scope, $http) {
$http.get('/resources')
.success(function(response) {
var resource = "";
$scope.resources = response;
$scope.resource = "";
});
$scope.addResource = function() {
console.log($scope.resource);
$http.post('/resources', $scope.resource)
.success(function(response){
//$scope.resources.push(response); /*not sure we need this*/
// will make get call only after data saved successfully
$http.get('/resources')
.success(function(response) {
var resource = "";
$scope.resources = response;
$scope.resource = "";
});
}, function(err){console.log(err);});
};
});
I have my JSON querying and creating correctly. I'm a bit stuck on how to remove items from the server. They are being removed in angular but I can't seem to get the connection right for removing them on the server.
My server.js:
var hcController = require('./server/controllers/services-controller.js')
//REST API
app.get('/api/hc', hcController.list);
app.post('/api/hc', hcController.create);
app.delete('/api/hc:_id', hcController.delete);
My server-side model
var mongoose = require('mongoose');
module.exports = mongoose.model('HealingCenterData',{
title: String,
shortname: String,
summary: String,
description: String
});
My server-side controller
var Hc = require('../models/healingcenter-model.js')
module.exports.create = function (req, res) {
var hc = new Hc(req.body);
hc.save(function (err, result){
res.json(result);
});
}
module.exports.list = function (req,res) {
Hc.find({}, function (err, results){
res.json(results);
});
}
module.exports.delete = function (req, res) {
???????
});
}
My angular service:
app.factory("HC", ["$resource", function($resource) {
return {
API: $resource('/api/hc/:id')
}
}]);
My angular controller:
app.controller('servicesController', ['$scope', 'HC','$resource', function ($scope, HC, $resource) {
HC.API.query(function(results) {
$scope.services = results;
});
$scope.createService = function() {
var service = new HC.API();
service.title = $scope.serviceTitle;
service.shortname = $scope.serviceShortname;
service.summary = $scope.serviceSummary;
service.description = $scope.serviceDescription;
service.$save(function(result){
$scope.services.push(result);
$scope.serviceTitle = '';
$scope.serviceShortname = '';
$scope.serviceSummary = '';
$scope.serviceDescription = '';
});
}
$scope.removeItem = function(index){
$scope.services.splice(index, 1);
}
}]);
My JSON structure
{ "_id" : ObjectId("53bea9366a03a66c2dad68bb"), "title" : "Auto Clinic", "shortname" : "auto_clinic", "summary" : "Volunteers evaluate car problems and make minor repairs. Labor is free, and the car owner pays for any needed parts. Oil changes are performed at a reduced cost. All services are performed on Saturdays.", "description" : "No additional information yet.", "__v" : 0 }
On the server side try (I'm assuming you are using moongose) :
exports.delete = function(req,res){
if(req.params.id !==null || req.params.id!==undefined){
Hc.remove({_id:req.params.id},function(err){
res.send(200);
});
}
};
on the client side:
angular controller:
var endPoint = $resource('/api/hc/:id', {id:'#tId'});
$scope.removeItem = function(id){
var ep = new endPoint({tId:id});
ep.$delete(function(res){
//TODO: update local array in scope
});
};
EDIT:
you can just use the resource directly in the controller or just the service as you have done in your case, that's totally fine.
I'm currently looking into building a small REST based service to which I can POST some data into a mongoose db and GET the data back.
Here's my main.js file:
var http = require("http");
var DAO = require("./DAO");
var express = require("express");
var util = require('util');
var app = express();
app.use(express.bodyParser());
app.post('/postIsles',function(req,res){
DAO[req.method](req.body);
res.send("body" + req.body.name);
});
app.get('/getIsles',function(req,res){
var isleVar = DAO[req.method](req);
res.send(isleVar);
});
app.listen("3000");
console.log("\nApp available at http://127.0.0.1:3000\n");
And DAO.js:
var mongoose = require('mongoose');
//Connect to database
mongoose.connect( 'mongodb://127.0.0.1:27017/library_database' );
//Schemas
var Isle = new mongoose.Schema({
name: String,
description: String,
lastStocked: Date
});
//Models
var IsleModel = mongoose.model( 'Isle', Isle );
function POST(request) {
var name = request.name;
var description = request.description;
var lastStocked = request.lastStocked;
console.log("POST REQ +" + request);
var isle = new IsleModel({
name: name,
description: description,
lastStocked: lastStocked
});
isle.save( function( err ) {
if( !err ) {
return console.log( 'created' );
} else {
return console.log( err );
}
});
}
function GET(request) {
return IsleModel.find( function( err, islesT ) {
if( !err ) {
console.log("isles :"+islesT);
return islesT;
} else {
return console.log( err );
}
});
}
exports.POST = POST;
exports.GET = GET;
When I try to run the GET, I get the following error:
TypeError: Converting circular structure to JSON
at Object.stringify (native)
I'm a bit unsure how to overcome this.
Remember when using Node.js: any operation that involves IO will be asynchronous.
Model#find is an asynchronous method, so isleVar is not set to the result you're expecting. Your result will only be available inside of the anonymous function that you pass into IsleModel.find
To fix your GET method, you'll need to modify your code to take into account the asynchronicity of the DB request and only send the response once your app has had a chance to retrieve data.
Below, is an example of one possible solution to fix /getIsles:
In main.js, modify your get route to pass in res (so it can be handled asynchronously)
app.get('/getIsles',function(req,res){
return DAO[req.method](req, res);
});
In DAO.js, have response send the data inside of your callback to IsleModel.find
function GET(request, response) {
IsleModel.find( function( err, islesT ) {
if( !err ) {
console.log("isles :"+islesT);
response.send(islesT);
} else {
return console.log( err );
}
});
}