Post Angular form data to Node.js with Sendgrid/Nodemailer

久未见 提交于 2019-12-04 23:47:45

1. Uploading a file with a form in angular

The first trouble you have is that ng-model doesn't work with <input type="file" />. So, you will need to create a custom directive to populate the model with the file.

.directive('fileModel', ['$parse', function ($parse) {
    return {
        restrict: 'A',
        link: function(scope, element, attrs) {
            var model = $parse(attrs.fileModel);
            var modelSetter = model.assign;

            element.bind('change', function(){
                scope.$apply(function(){
                    modelSetter(scope, element[0].files[0]);
                });
            });
        }
    };
}]);

Then use the directive on your file input element like this:

<input type="file" file-model="email.attachment" ng-required="true" id="email-attachment" name="attachment" class="form-control" />

Note that I changed email.file to email.attachment to avoid confusion in the rest of the code.

Next, you need to include the file in your AJAX request. You will need to use FormData for that. Populate it from the scope using FormData.append():

$scope.submitEmail = function() {
    var formData = new FormData();
    Object.keys($scope.email).forEach(function(key) {
        formData.append(key, $scope.email[key]);
    });
    $http.post('/api/email', formData, {
        transformRequest: angular.identity,
        headers: {'Content-Type': undefined}
    }).then(function(data, status) {
        console.log("Sent ok");
    }, function(data, status) {
        console.log("Error");
    });
};

Notice I also passed a config object to $http.post(). This is to prevent angular from parsing the FormData object and setting the content type.

I relied heavily on this blog post for this part of the answer.

2. Sending an attachment with Nodemailer

To access the file in express, use multer.

Install:

$ npm install --save multer

Usage:

var multer = require('multer');
var upload = multer();

app.post('/api/email', upload.single('attachment'), function(req, res) {
    // req.file contains the 'attachment' file
    ...
});

From Nodemailer's Readme section on email fields it says to use an attachments property that is an array of attachment objects.

app.post('/api/email', upload.single('attachment'), function(req, res) {
    var mailOptions = {
        to: ['test@test.nl', req.body.to],
        from: req.body.from,
        subject: req.body.subject,
        text: req.body.text,
        attachments: [
            {
                filename: req.file.originalname,
                content: req.file.buffer
            }
        ]
    };

    mailer.sendMail(mailOptions, function(err, res) {
        if (err) { 
            console.log(err) 
        }
        console.log(res);
    });
});

The above example keeps the attachment in memory, which could be bad if large files are uploaded frequently. You can write files to disk instead:

var upload = multer({ dest: '/uploads' });

Then, instead of setting the file's buffer as the attachment's content, you would set the attachment's path to the file's path:

attachments: [
    {
        filename: req.file.originalname,
        path: req.file.path
    }
]

I finally got the job done thanks to gilly3. I had to do one minor change in his code. In the controller I changed:

formData.set(key, $scope.email[key]);

to the following code:

formData.append(key, $scope.email[key]);
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!