MEAN stack newbie here. I'm having difficulty understanding how delete works in MEAN. I'm using this SO Q&A and tutorial as guides, but whenever I test it out I get an error saying the data can't be deleted. Can somebody tell me what I've been doing wrong?
Here are my codes:
Controller JS
$scope.deleteProduct = function (value, idx) {
var this_id = value._id;
// delete
$http.delete('/api/products/delete:' + this_id)
.success(function (data) {
console.log(data);
})
.error(function (data) {
console.log('Error: ' + data);
})
}
Node Server
app.delete('/api/products/delete:', productController.delete);
Server's "Controller"
module.exports.delete = function (req, res) {
Service.remove({
_id: req.params._id
}, function (err, service) {
if (err) {
res.send(err);
}
else {
res.json({message: "Delete successful."});
}
});
}
This is how I understood this. Is this correct?
Controller JS gets the id to be deleted and calls $http's delete request(?), using said ID and the /api/products/delete:.
Node Server sees that I called '/api/products/delete:' and passes the request to Server's Controller to complete the request.
Server's Controller deletes the data and returns status.
Where did I go wrong? Please help.
Also, I've been seeing some posts that say $resource works better than $http. Why?
Thank you.
I think you've got a couple things wrong here.
In Express in order to use params you need to have something in the route that can be replaced. i.e /api/:id express replaces the :id with whatever you pass in so if you send /api/1, request.params.id is 1
So first problem is your route is
app.delete('/api/products/delete:', productController.delete);
tha dosen't mean anything to Express. I think you want
app.delete('/api/products/:id', productController.delete);
now req.params.id should contain the parameter you send. Note im dropping the underscore here. you could use
app.delete('/api/products/:_id', productController.delete); and keep the underscore if you like.
Second mistake I think is your Angular code. you have the : in your call it should just be
$http.delete('/api/products/' + this_id)
Now you're sending the route with whatever Id you are trying to delete i.e
/api/products/1
Now Express gets that and can map it to /api/products/:id and replace the id and now your controller should work. barring any other issues.
Edit
I'm not very familiar with Angular but I think the reason people are saying to use $resource is it is easier. You can directly call the different HTTP verbs directly on the objects themselves objects like
product.update and product.delete rather than trying to craft the http calls yourself. I'm sure there is a lot more to it than that but its a feature that's built into Angular that can be leveraged. I think one of the catches is the URLs for the resources just have to be set up a specific way on the server but I believe there was a way to override them in Angular.
Related
I have anPOST api endpoint lets say /users to fetch the list of users.
It is POST because body of the request is very huge and might not fit in url for GET request.
suppose the body of user POST have a key called age , which should give me user of certain age ie kind of filtering
now in express i have route like
app.post('/users', function(r,res){
// function body
})
and i cant actually put any code inside that function body
so i was able to intercept the request by using one more handler for /users and putting it before the original handler but obviously it intercepts all /users requests and breaks earlier functionality
how can i intercept only the request with particular age and then pass through other requests to the original handler, so that original functionality keeps working?
I want to know how can i do this using route handlers and not middlewares ?
i cant mess with the url or request body also
First off, this sounds like a really bad design so really the better way to fix things is to just fix the URL design so you don't have this conflict between code you can and can't modify. I say this because it sounds like you're trying to "hack" into something rather than make a proper design.
If your code is using the regular body-parser middleware, then the body of the post will already be parsed and in req.body. So, you can look for the desired parameter in req.body.age and check its value.
If it meets your criteria, then you can process the request and you're done. If it doesn't meet your request, then you call next() to continue processing to other request handlers.
// make sure this is defined BEFORE other /users request handlers
app.post('/users', function(req, res, next) {
// test some condition here
if (+req.body.age > 30) {
// process the request and send a response
res.send("You're too old");
} else {
// continue processing to other request handlers
next();
}
})
The way I deal with this is if I have a route that works, and I need something else, I add another route that is similar. This way you leave the original alone - which provides a working service. This is what I think you re describing.
You can call routes anything you like. If you want a list of users you can pass a variable like this:
$.get('/contactCard/'+qry);
app.get('/contactCard/:sort', function(req, res) {
var cId = req.params.sort;
console.log('cId: ' + cId);
then you set up your search query and go get the data a bit like this:
let params = {
TableName: ddbTable,
ProjectionExpression : "cEmail,Forename,Surname",
KeyConditionExpression: "ID = :e ",
ExpressionAttributeValues: {
":e" : cId
}
};
console.log("params", JSON.stringify(params, null, 2));
docClient.query(params, function(err, data) {
then you check for error or success:
if (err) {
console.log("Error:", JSON.stringify(err, null, 2));
} else {
console.log("Success", JSON.stringify(data, null, 2));
let contacts = data;
then here you render to the page you want and pass the data as you wish.
res.render('members/contactcard', {
contacts:contacts,
static_path: '/static'
});
}
});
I'm going to make web application (SPA) with:
Backend: Node.js (express)
Frontend: Jade + AngularJS
Database: Mongoose
I will send data (as a form) to backend in this way ExpressJS AngularJS POST (Check ANSWER)
It will be simple CRUD.
However i wondering how should I display data from backend?
For example:
I'll run application
var Partner = require('../model/partners');
router.get('/', function (req, res) {
Partner.find({}, function (err, partnerList) {
if (err) throw err;
res.render('campaign', {
partnerList: partnerList
});
});
});
And how should i display data (partnerList). Maybe in this way?
- each item in partnerList
= item.name
Or maybe there is another better way with angular to display data at view? I'm asking because later i'd like remove or update items from partnerList (CRUD operation). And it may be a problem because i will have to send item._id as a parameter to angular function?
For example if i will add button to remove record:
- each item in partnerList
= item.name
button(type='remove' ng-click="sub('#{item._id}')")
script.
app.controller('view1Ctrl', function($scope, $http) {
$scope.sub = function(id) {
$http.post('/',id).
success(function(data) {
console.log("posted successfully");
}).error(function(data) {
console.error("error in posting");
})
}
});
Probably it won't work correct
As said in previous coment, from my point of view I prefere to send the minimum required data from the backend to the client, but it depends of you infrastructure and you concurrent users.
Example 1:
You have a web app with +5K concurrent users, in this case is better handle all the huge stuff at frondend side or you will need to spend a lot of money in your backend hardware.
Practical case:
Users POST a new comment in a blog page. You sanitize the text string at the backend and put it at you preferred datastore... But JUST respond with a simple json like {"status": "ok"}. If the frond end recive this, modify the DOM with the text string that the client sent to the backend in the POST stage, but not send again all the HTML with this (for example) 500 characters comment.
If server responds with {"status":"error"}, modify the DOM to let the user know what's the problem about his comment (more specified json message {"status":"error", "data":"you comment is bigger than 500 chars"})
Problems:
You need extra frontend code to handle these situations in the client side. So this "maybe" will inpact on the user the 1st time that it visits your page.
Pros:
Less hardware costs
Overall less server response times.
More user interactive website modeling only certain parts of the DOM at any moment.
...
Example 2:
You have a simple page with low concurrent users. Then you choose. Let you backend to handle everything? Or keep working with json responses?
I always use the 1st example. Hope this helps in your question.
I think the preferred method would be to set up a second route from express to specifically render JSON, then use angular's $http method to get that data and use it in your controller. If you want to do it with a single route, you can pass the JSON data as a string to your view on the server-side, but it might get a little unruly.
// app.js
...
partnerList: JSON.stringify(partnerList);
...
// index.jade
div(ng-repeat="item in partnerList")
p {{ item.name }}
button(type='remove', ng-click="sub(item._id)")
...
script.
app.controller('view1Ctrl', function($scope, $http) {
$scope.partnerList = JSON.parse(#{partnerList});
...
EDIT To use the JSON string example, you would have to render using the Unbuffered Code syntax. But I'm not sure how you would do that inside a script. block. To instead go the route of serving JSON separately, change your server routes to this:
var Partner = require('../model/partners');
router.get('/', function (req, res) {
res.render('campaign');
});
router.get("/partner-list", function(req, res) {
Partner.find({}, function (err, partnerList) {
if (err) throw err;
res.json({ partnerList: partnerList });
});
});
Then your angular code will query that /partner-list path with $http.get().
script.
app.controller('view1Ctrl', function($scope, $http) {
$http.get("/partner-list").then(function(result) {
$scope.partnerList = result.data.partnerList;
});
...
});
I've got some data from a JSON file, which I use in my HTML getting it first from AngularJS like this:
$http.get('js/data.json').success(function(data) {
$scope.data = data;
});
And I want to update this JSON file after clicking a button in the HTML:
<button ng-click="postData(id)">Post</button>
You cannot write on files via JavaScript only (AngularJS).
You are to go via server side and point your "post" request to a server side script (i.e: PHP) and make that script do the job.
This sort of thing won't work. The file you are trying to write to would be on a server; and as it is right now, it would be a static resource. I'd suggest reading up on Angular resources, here. You can set up your server-side code to perform CRUD operations on the json file, but an actually database would be best. If you prefer to use a json format, Mongodb is your best choice; here is a link to Mongodb University, which offers free courses. I've done it in the past, and it's been great.
Now, for some actually help in your situation:
You can perform a GET request on your json file because it's seen as a static resource. The POST request, however, needs server-side scripting to do anything.
$http.get('api/YOUR_RESOURCE').success(function(data) {
$scope.database = data;
});
$http.post('api/YOUR_RESOURCE', {
data_key: data_value,
data_key2: data_value2
}).success(function(data) {
data[id].available = false;
});
This may be further ahead on your path to learning Angular, but here is a snippet of Node.js server code, with a Mongo database and Mongoose to handle the 'Schema', to help you get an idea of how this works:
var mongoose = require('mongoose'),
YOUR_RESOURCE = mongoose.model('YOUR_RESOURCE');
app.route('/api/YOUR_RESOURCE')
// This should be your GET request; 'api/
.get(
// Get all docs in resource
YOUR_RESOURCE.find().exec(function (err, data) {
if (err) {
return res.status(400).send({
message: SOME_ERROR_HANDLER
});
} else {
res.json(data); // return list of all docs found
}
});)
// Add new doc to database
.post(function (req, res) {
// The keys of the object sent from your Angular app should match
// those of the model
var your_resource = new YOUR_RESOURCE(req.body);
your_resource.save(function (err) {
if (err) {
return res.status(400).send({
message: SOME_ERROR_HANDLER
});
} else {
// returns newly created doc to Angular after successful save
res.json(your_resource);
}
});
);
Here is an SO page with a list of resources on getting started with Node; I recommend Node because of it's ease of use and the fact that it is written in JS. The Mongo University lessons also go through setting up you server for use with the database; you can choose between several flavors, such as Java, .NET, Python or Node.
There is a bit left out in the examples above, such as the Mongoose model and Node setup, but those will be covered in the resources I've linked to on the page, if you choose to read them. Hope this helps :)
So I have been working on a SPA project using Sailsjs. The problem is:
When the first page loads, I use res.view or res.render in my controller. But, for subsequent requests, I dont want to use res.view or res.render, rather I'd like to use res.json.
Right now, I use the method:
return req.param('json') ? res.json(myObj) : res.view('layout', myObj);
This works, but I was looking for a more generic and abstract method in which my controller itself would know (on the basis of req.param('json')) whether to use res.view or res.json without me having to tell it explicitly.
Any help ?
This is what res.ok() is for.
In the controller action below, res.ok will either display the myAction.ejs using data as the view locals, or respond with data as JSON, depending on how the request came in (i.e. via AJAX, sockets or a regular browser request):
module.exports = {
myAction: function(req, res) {
var data = {someKey: "someVal"};
return res.ok(data);
}
}
Internally, the ok response uses req.wantsJSON to determine what to do; it checks headers, looks for an xhr object and generally does its best to guess your intent. req.wantsJSON is available for all requests (as is req.isSocket), so you can use them yourself as needed.
So after a bit of tinkering, I resolved this using a service.
I wrote a service (in GlobalUtils.js):
render: function (req, res, obj) {
if(req.param('json')) {
return res.json(obj);
}
else {
return res.view('layout', obj);
}
}
And I use this service in my controllers, like so:
return GlobalUtils.render(req, res, myObj);
But still, looking for a better method.
I'm trying to implement update functionality to an Express.js app, and I'd like to use a PUT request to send the new data, but I keep getting errors using PUT. From everything I've read, it's just a matter of using app.put, but that isn't working. I've got the following in my routes file:
send = function(req, res) {
req.send(res.locals.content);
};
app.put('/api/:company', function(res,req) {
res.send('this is an update');
}, send);
When I use postman to make a PUT request, I get a "cannot PUT /api/petshop" as an error. I don't understand why I can't PUT, or what's going wrong.
You may be lacking the actual update function. You have the put path returning the result back to the client but missing the part when you tell the database to update the data.
If you're using MongoDB and ExpressJS, you could write something like this :
app.put('/api/:company', function (req, res) {
var company = req.company;
company = _.extend(company, req.body);
company.save(function(err) {
if (err) {
return res.send('/company', {
errors: err.errors,
company: company
});
} else {
res.jsonp(company);
}
})
});
This mean stack project may help you as it covers this CRUD functionality which I just used here swapping their articles for your companies. same same.
Your callback function has the arguments in the wrong order.
Change the order of callback to function(req, res).
Don't use function(res, req).
Also if you want to redirect in put or delete (to get adress), you can't use normal res.redirect('/path'), you should use res.redirect(303, '/path') instead. (source)
If not, you'll get Cannot PUT error.
Have you been checking out your headers information?
Because header should be header['content-type'] = 'application/json'; then only you will get the update object in server side (node-express), otherwise if you have content type plain 'text/htm' like that you will get empty req.body in your node app.