Haywire Spark
Haywire Spark

Reputation: 113

Multer and AngularJS

I can't figure out how to post through angular $http. Why? I'd like multer to parse and store my file and angular to get a copy when it is done. Better yet, I'd love to just let angular get a copy, then pass it down to the server.

I am able to upload a file using the snippets below:

// view (jade)
.container
  form(action="/upload", method="POST", enctype="multipart/form-data")
    input(type="file", name="f")
    input(type="submit", value="Upload")

// post route (express)
var multer = require('multer');
var upload = multer({ dest: 'uploads/' });
app.post('/upload', upload.single('f'), function(req, res) {
  console.log(req.file);
});

Running this with any sample file, returns the json I want to the console and the image is stored successfully in the proper directory. However:

// view (jade)
form(enctype="multipart/form-data" ng-submit="uploadFile(file)")
input(type="file", name="f", ng-model="file")
input(type="submit", value="Upload")

// ctrl (angular)
$scope.uploadFile = function(file) {
  console.log(file);
  $http.post('/upload', file);
};

// post route (express)
var multer = require('multer');
var upload = multer({ dest: 'uploads/' });
app.post('/upload', upload.single('f'), function(req, res) {
  console.log(req.file);
});

This returns undefined on both console inputs, even though I didn't change the file, I only changed how it was sent.

This leads me two believe one of two things:

The data submitted is not what I think it is. If this is the case, what is it? Nothing prints on the log.

OR

The data submitted is altered in some way by angular. If this is the case, how? Again nothing prints on the log.

Upvotes: 0

Views: 1750

Answers (1)

igreka
igreka

Reputation: 350

In the first case, by sending the data directly through the form to the server, you let html do the transformation magic under the hood.

In the second case, by posting through AngularJs via $http, you need to tell the middleware that is $http, the required transformation it needs to do to the request to match the attributes you passed to the form.

For having struggled with this myself for a while, here is an adaptation of the code I used (for $resource). But I think it should work for its underlying $http.

So in your controller:

$scope.uploadFile = function(file) {
   console.log(file);
   var fd = new FormData();
    fd.append('file', file);
    $http.post(uploadUrl, fd, {
        transformRequest: angular.identity,
        headers: {
            'Content-Type': undefined},
            enctype: 'multipart/form-data'
        }
     })
    .success(function(){
    })
    .error(function(){
    });   
 };

For that to work, you need to bind the file input field to the model. Of course, Angularjs chooses not to do that natively for some reasons.

In the jade view:

form(enctype="multipart/form-data" ng-submit....)
  input(type="file", name="f", ng-model="file" on change='angular.element(this).scope().readFile(this)')
  input(type="submit", value="Upload")

onchange is JavaScript and not AngularJs and it triggers the scope.readfile() function that we need to define:

In the controller:

$scope.readfile = function(elem) {
    var file= elem.files[0];
    var reader = new FileReader();
   reader.onload = function() {
       $scope.$apply(function(){
           $scope.file = file;
           $scope.imageUrl = reader.result // to display         image via ng-Src
       })
   }
   reader.readAsDataURL(file);
}

I think that should bring back some html magic back into angular when working with forms.

I suggest you look into JavaScript's onchange, FormData, and FileReader for code snippets like these and better documentation.

Upvotes: 2

Related Questions