Store File Locally via Mongoose Crate with MEAN app - angularjs

I'm having a bit of trouble setting up Mongoose Crate with my REST API using the MEAN stack. I'm pretty new to the whole thing so take it easy on me. I want to be able to upload a file to a local directory and then display that image within the controller. My route is setup as follows:
router.post('/posts', auth, function(req, res, next) {
var post = new Post(req.body);
post.author = req.payload.username;
// Mongoose Crate attach method
post.attach('image', req.files.image, function(err, post) {
if(err){ return next(err); }
post.save(function(err, post) {
if(err){ return next(err); }
res.json(post);
});
});
});
This is my Post model:
var mongoose = require('mongoose');
var crate = require('mongoose-crate');
var LocalFS = require('mongoose-crate-localfs');
var PostSchema = new mongoose.Schema({
title: String,
author: String
});
PostSchema.plugin(crate, {
storage: new LocalFS({
directory: './public/uploads'
}),
fields: {
image: {}
}
});
mongoose.model('Post', PostSchema);
And my controller:
app.controller('FeedCtrl', [
'$scope',
'posts',
function($scope, posts) {
$scope.posts = posts.posts;
$scope.addPost = function() {
posts.create({
title: $scope.title,
author: $scope.author,
image: $scope.image
});
$scope.title = '';
}
}
]);
And on the front end:
<form ng-submit="addPost()" method="post" enctype="multipart/form-data">
<div>
<input type="file" name="image" id="image">
</div>
<div class="message">
<input type="text" placeholder="Title" ng-model="title">
</div>
<button type="submit"></button>
</form>
<div>
<h3>{{post.title}}</h3>
<img src="{{post.image}}"/>
<span>posted by <a>{{post.author}}</a></span>
</div>
I read that by using Express you can get an uploaded file by just the input name and using req.files but I can’t seem to get that too work within the scope. If I’m reading that Mongoose Crate plugin correctly, it should save it to a directory of your choice (using the mongoose-crate-localfs plugin), by adding it to the “Post” model, and send back an array of the file meta data. I'm not able to retrieve any data after the post.
I feel like that scene in Seinfeld when they’re trying to write the script: https://www.youtube.com/watch?v=q9eDwHznMas. “Now we need something here…”. I just don’t know what it is. If someone can please take a look and please get me out of this funk that would be amazing. Thank you so much!

Express does not come with a multipart parser out of the box, req.files will be undefined if you are not using a multipart parsing middleware. A good multipart middleware to check out is multer, very easy to use. Also if you are planning making this an XHR request you will need to use the FormData api, and explicitly tell angular not to process the data. Im sure there are many good third party solutions but here is an article worth reading.

Related

AngularJS $http PUT request

I am buiding a CRUD apps with AngularJS and Django REST API.
I have created get and post method successfully but not getting how to put request. i tried stackoverflow lots of problem and youtube but i couldnt sort it out.
my current controller is:
app.controller('crudCtrl', function($scope, $http) {
$http.get("http://127.0.0.1:8000/api/v1/contact/?format=json")
.then(function(response) {
$scope.contacts = response.data; //this is get method that displayed all the list of contact
});
$scope.formModel = {}; // this is same input.js, it is POST method to to send data to database
$scope.onSubmit = function () {
console.log("hey, i am submitting");
console.log($scope.formModel);
$http.post('http://127.0.0.1:8000/api/v1/contact/', $scope.formModel).
success(function (data) {
console.log(":)");
}).error(function (data) {
console.log(":(");
});
};
$scope.selectUser = function (contact) {
console.log(contact); // it will select the data exactly where you click
$scope.clickedUser = contact;
};
$scope.updateUser = function (argument) { // it will get the form editable exactly which contact you clicked
};
});
and my edit view is, when i click on edit buttion, the form will be appear:
<form>
<input type="text" ng-model="clickedUser.userid">
<input type="text" ng-model="clickedUser.name">
<input type="text" ng-model="clickedUser.email">
<input type="text" ng-model="clickedUser.phone">
<button type="submit" ng-click="updateUser()" data-dismiss="modal">Submit</button>
</form>
Point to be noted, the edit form working nice on client side but it doesnt send the data to backend/API/Database.
can anyone tell me how can i do $http.put request? i tried w3school, youtube, and stackoverflow problem.
i got huge solution but i couldnt solve it.
this is my api endpoint for anything: http://127.0.0.1:8000/api/v1/contact/ so if i want to update particular field, i have to go through this url: http://127.0.0.1:8000/api/v1/contact/1 in the end of the url is id
I hope it is clear to you
You can try this as well
$http({method: "PUT", url: URL}).
then(
function(response) {
}, function(response) {
});
Can you just use angular put?
See: https://docs.angularjs.org/api/ng/service/$http#put
var clientData = {
text: "Put this somewhere"
};
$http.put( url, clientData ).then( function( serverResponse ) {
console.log(serverResponse);
},( error )=>{
console.log(serverError);
});

How to send file to Sails from AngularJS without using custom directives?

So I need to upload an image to the server. Right now it works only with a POST request made via HTML. But I need to send other data and do other stuff before uploading it, so I'd like to send the request inside an Angular controller, using $http.post(...). I've seen some really complex solutions using custom directives, but I'd like to know if there's anyway to do something straight forward like:
$scope.uploadPhoto = function(){
var fd = new FormData();
fd.append("uploadFile", $scope.file);
$http.post('file/upload', fd);
}
Working code:
HTML:
<form id="uploadForm"
enctype="multipart/form-data"
action="/file/upload"
method="post">
<input type="file" name="uploadFile" />
<input type="submit" value="submit"/>
</form>
Sails Controller:
upload: function (req, res) {
var uploadFile = req.file('uploadFile');
uploadFile.upload(
{ dirname: '../../assets/images',
saveAs: function(file, cb) {
cb(null, file.filename);
}
}, function onUploadComplete (err, files) {
if (err) return res.serverError(err);
res.json({status:200,file:files});
});
}

Can't set headers after they are sent - Posting new Users through NodeJs

I'm trying to build an application using Google Maps API, NodeJs, Express, MongoDB, Mongoose and AngularJS and I'm facing a problem that I wasn't able to solve looking at other related SO Q/A.
Basically, I'm trying to post into my db users identified by an username and [latitude, longitude] who submit a certain form that I have in my view.
When I try to post users directly from applications like Postman everything goes ok, I'm able to see new users in my db.
When, instead, I try to post users directly when they submit, I get the following error in my Console:
/node_modules/mongodb-core/lib/topologies/server.js:766
catch(err) { process.nextTick(function() { throw err}); }
^
Error: Can't set headers after they are sent.
and the following log in my Google Chrome Console:
angular.js:10695 GET http://localhost:3000/users net::ERR_CONNECTION_REFUSED
Here is my View:
<form name="addForm" novalidate>
<div class="form-group">
<label for="username">Username
<span class="badge">All fields required</span>
</label>
<input type="text" class="form-control" id="username"
placeholder="OldandGold" ng-model="formData.username" required>
</div>
<div class="form-group">
<label for="latitude">Latitude</label>
<input type="text" class="form-control" id="latitude"
value="39.500" ng-model="formData.latitude" readonly>
</div>
<div class="form-group">
<label for="longitude">Longitude</label>
<input type="text" class="form-control" id="longitude"
value="-98.350" ng-model="formData.longitude" readonly>
</div>
<button type="submit" class="btn btn-danger btn-block"
ng-click="createUser()" ng-disabled="addForm.$invalid">Submit</button>
</form>
Here is my Schema:
// Pulls Mongoose dependency for creating schemas
var mongoose = require('mongoose');
var GeoJSON = require('geojson');
var Schema = mongoose.Schema;
var LocationSchema = new Schema({
name: {type: String, required: true},
location: {
type: {type : String, required: true},
coordinates : [Schema.Types.Mixed]
},
created_at: {type: Date, default: Date.now},
updated_at: {type: Date, default: Date.now}
});
// Sets the created_at parameter equal to the current time
LocationSchema.pre('save', function(next){
now = new Date();
this.updated_at = now;
if(!this.created_at) {
this.created_at = now
}
next();
});
// Indexes this schema in 2dsphere format (critical for running proximity searches)
LocationSchema.index({location: '2dsphere'});
module.exports = mongoose.model('mean-locations', LocationSchema);
Here is my Controller's createUser function:
$scope.createUser = function($rootScope, $on) {
// Grabs all of the text box fields
var userData = {
name: $scope.formData.username,
location: {
type: "Point",
coordinates: [$scope.formData.latitude,
$scope.formData.longitude]
}
};
console.log(JSON.stringify(userData));
// Saves the user data to the db
$http.post('/users', userData)
.success(function(data) {
// Once complete, clear the form (except location)
$scope.formData.username = "";
})
.error(function(data) {
console.log('Error: ' + data);
});
};
And, finally, here are my routes:
app.get('/users', function(req, res) {
// Uses Mongoose schema to run the search (empty conditions)
var query = User.find({});
query.exec(function(err, users) {
if (err)
res.send(err);
// If no errors are found, it responds with a JSON of all users
res.json(users);
});
});
// POST Routes
// --------------------------------------------------------
// Provides method for saving new users in the db
app.post('/users', function(req, res) {
// Creates a new User based on the Mongoose schema and the post body
var newuser = new User(req.body);
// New User is saved in the db.
newuser.save(function(err) {
if (err)
res.send(err);
// If no errors are found, it responds with a JSON of the new user
res.json(req.body);
});
});
Using my Stringify Log I'm able to see a proper json:
{"name":"MyPoint","location":{"type":"Point","coordinates":["50.064","16.260"]}}
I'm pretty new to NodeJs and I don't understand why this keeps happening.
What does cause this? How Can I solve this?
Thanks In Advance.
The problem is here, you have to stop execution if there is an error. (Note the return). For example if there's an error, in your code will send (res.send) the error and continues executing res.json() what it will end with the error you mentioned, because you have already set the header and send the response.
newuser.save(function(err) {
if (err)
return res.send(err);
// If no errors are found, it responds with a JSON of the new user
res.json(req.body);
});
The "Error: Can't set headers after they are sent." error usually indicates that you are sending multiple responses (using express).
For instance this code will (try) to send two responses in case of error
app.get('/users', function(req, res) {
// Uses Mongoose schema to run the search (empty conditions)
var query = User.find({});
query.exec(function(err, users) {
if (err)
res.send(err); // first response
// If no errors are found, it responds with a JSON of all users
res.json(users); // second response
});
});
To Fix this, ensure to exit after response has been send:
app.get('/users', function(req, res) {
// Uses Mongoose schema to run the search (empty conditions)
var query = User.find({});
query.exec(function(err, users) {
if (err) {
res.send(err);
return; // return here!
}
// If no errors are found, it responds with a JSON of all users
res.json(users);
});
});

MEAN.JS Contact Form

Trying to create a contact form and feedback form for my website. Here is my route and controller I'm using however I need to understand what's going on with my routes and how to capture the input fields from the form implement this inside MEAN.JS:
route.js:
app.route('/mail').get(mail.createmail);
app/controller.js:
exports.createmail = function(req, res) {
var mailOpts, smtpTrans;
// create reusable transporter object using SMTP transport
var transporter = nodemailer.createTransport({
service: 'Gmail',
auth: {
user: 'administrator#radleaf.com',
pass: '34Girls34*goo'
}
});
// NB! No need to recreate the transporter object. You can use
// the same transporter object for all e-mails
// setup e-mail data with unicode symbols
var mailOptions = {
from: 'Fred Foo ✔ <foo#blurdybloop.com>', // sender address
to: 'ty#radleaf.com', // list of receivers
subject: 'Hello ✔', // Subject line
text: 'Hello world ✔', // plaintext body
html: '<b>Hello world ✔</b>' // html body
};
// send mail with defined transport object
transporter.sendMail(mailOptions, function (error, info) {
if (error) {
console.log(error);
} else {
console.log('Message sent: ' + info.response);
}
});
};
Not sure how this work with the HTML with the view:
<form action="mail">...</form>
If I understand the question correctly, you're asking how you can gather data which is input into a form, send that data to expressJS, and then use that data to send an outbound email.
If so, then here is your flow:
Step 1: Create a form in a view and map it to an AngularJS controller
<form name="contactForm" data-ng-submit="sendMail()">
Name: <input type="text" data-ng-model="contact_name">
Message: <input type="text" data-ng-model="contact_msg">
<button type="submit">
</form>
Step 2: In your AngularJS controller, use a $http request to send your data to your Express Route
$scope.sendMail = function() {
// Simple POST request example (passing data) :
$http.post('/mail', {name: contact_name, msg: contact_msg}).
success(function(data, status, headers, config) {
// this callback will be called asynchronously
// when the response is available
}).
error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
}
Step 3: Use your ExpressJS Route as an API to call your ExpressJS Controller.
(It looks like you've got this bit covered)
app.route('/mail').get(mail.createmail);
Step 4: Receive and do something with the data passed through the $http POST
exports.createmail = function(req, res) {
var data = req.body;
Now you can use the data, like this
var mailOptions = {
from: data.name, // sender name
text: data.msg, // plaintext body
};
MeanJS 0.4.0 also has a working example of NodeMailer which might help: https://github.com/meanjs/mean/tree/0.4.0
With angularJS, you can remove the action attribute, and just use angularJS ngSubmit directive, and call a function in your controller which would now visit the endpoint with $http.get.

AngularJS: how to implement a simple file upload with multipart form?

I want to do a simple multipart form post from AngularJS to a node.js server,
the form should contain a JSON object in one part and an image in the other part,
(I'm currently posting only the JSON object with $resource)
I figured I should start with input type="file", but then found out that AngularJS can't bind to that..
all the examples I can find are for wraping jQuery plugins for drag & drop. I want a simple upload of one file.
I'm new to AngularJS and don't feel comfortable at all with writing my own directives.
A real working solution with no other dependencies than angularjs (tested with v.1.0.6)
html
<input type="file" name="file" onchange="angular.element(this).scope().uploadFile(this.files)"/>
Angularjs (1.0.6) not support ng-model on "input-file" tags so you have to do it in a "native-way" that pass the all (eventually) selected files from the user.
controller
$scope.uploadFile = function(files) {
var fd = new FormData();
//Take the first selected file
fd.append("file", files[0]);
$http.post(uploadUrl, fd, {
withCredentials: true,
headers: {'Content-Type': undefined },
transformRequest: angular.identity
}).success( ...all right!... ).error( ..damn!... );
};
The cool part is the undefined content-type and the transformRequest: angular.identity that give at the $http the ability to choose the right "content-type" and manage the boundary needed when handling multipart data.
You can use the simple/lightweight ng-file-upload directive.
It supports drag&drop, file progress and file upload for non-HTML5 browsers with FileAPI flash shim
<div ng-controller="MyCtrl">
<input type="file" ngf-select="onFileSelect($files)" multiple>
</div>
JS:
//inject angular file upload directive.
angular.module('myApp', ['ngFileUpload']);
var MyCtrl = [ '$scope', 'Upload', function($scope, Upload) {
$scope.onFileSelect = function($files) {
Upload.upload({
url: 'my/upload/url',
file: $files,
}).progress(function(e) {
}).then(function(data, status, headers, config) {
// file is uploaded successfully
console.log(data);
});
}];
It is more efficient to send a file directly.
The base64 encoding of Content-Type: multipart/form-data adds an extra 33% overhead. If the server supports it, it is more efficient to send the files directly:
$scope.upload = function(url, file) {
var config = { headers: { 'Content-Type': undefined },
transformResponse: angular.identity
};
return $http.post(url, file, config);
};
When sending a POST with a File object, it is important to set 'Content-Type': undefined. The XHR send method will then detect the File object and automatically set the content type.
To send multiple files, see Doing Multiple $http.post Requests Directly from a FileList
I figured I should start with input type="file", but then found out that AngularJS can't bind to that..
The <input type=file> element does not by default work with the ng-model directive. It needs a custom directive:
Working Demo of "select-ng-files" Directive that Works with ng-model1
angular.module("app",[]);
angular.module("app").directive("selectNgFiles", function() {
return {
require: "ngModel",
link: function postLink(scope,elem,attrs,ngModel) {
elem.on("change", function(e) {
var files = elem[0].files;
ngModel.$setViewValue(files);
})
}
}
});
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app">
<h1>AngularJS Input `type=file` Demo</h1>
<input type="file" select-ng-files ng-model="fileArray" multiple>
<h2>Files</h2>
<div ng-repeat="file in fileArray">
{{file.name}}
</div>
</body>
$http.post with content type multipart/form-data
If one must send multipart/form-data:
<form role="form" enctype="multipart/form-data" name="myForm">
<input type="text" ng-model="fdata.UserName">
<input type="text" ng-model="fdata.FirstName">
<input type="file" select-ng-files ng-model="filesArray" multiple>
<button type="submit" ng-click="upload()">save</button>
</form>
$scope.upload = function() {
var fd = new FormData();
fd.append("data", angular.toJson($scope.fdata));
for (i=0; i<$scope.filesArray.length; i++) {
fd.append("file"+i, $scope.filesArray[i]);
};
var config = { headers: {'Content-Type': undefined},
transformRequest: angular.identity
}
return $http.post(url, fd, config);
};
When sending a POST with the FormData API, it is important to set 'Content-Type': undefined. The XHR send method will then detect the FormData object and automatically set the content type header to multipart/form-data with the proper boundary.
I just had this issue. So there are a few approaches. The first is that new browsers support the
var formData = new FormData();
Follow this link to a blog with info about how support is limited to modern browsers but otherwise it totally solves this issue.
Otherwise you can post the form to an iframe using the target attribute.
When you post the form be sure to set the target to an iframe with its display property set to none.
The target is the name of the iframe. (Just so you know.)
I hope this helps
You could upload via $resource by assigning data to params attribute of resource actions like so:
$scope.uploadFile = function(files) {
var fdata = new FormData();
fdata.append("file", files[0]);
$resource('api/post/:id', { id: "#id" }, {
postWithFile: {
method: "POST",
data: fdata,
transformRequest: angular.identity,
headers: { 'Content-Type': undefined }
}
}).postWithFile(fdata).$promise.then(function(response){
//successful
},function(error){
//error
});
};
I know this is a late entry but I have created a simple upload directive. Which you can get working in no time!
<input type="file" multiple ng-simple-upload web-api-url="/api/post"
callback-fn="myCallback" />
ng-simple-upload more on Github with an example using Web API.
I just wrote a simple directive (from existing one ofcourse) for a simple uploader in AngularJs.
(The exact jQuery uploader plugin is https://github.com/blueimp/jQuery-File-Upload)
A Simple Uploader using AngularJs (with CORS Implementation)
(Though the server side is for PHP, you can simple change it node also)

Resources