Опубликовать Angular данные формы для Node.js с помощью Sendgrid/Nodemailer
Я следил за этим примером, чтобы отправить данные из моего приложения Angular в Node.js, чтобы отправить веб-форму в Sendgrid. Это отлично работает после некоторых изменений и очень много спасибо за быстрый запуск. Теперь отправьте мои данные формы в Sendgrid!
Для этого проекта я использую Angular Fullstack, чтобы иметь возможность использовать функции Node в моем приложении Angular.
Однако этот пример имеет только поля ввода и текстовую область. Я хочу иметь возможность добавлять файл (PDF, Docx, например), чтобы люди могли отправлять вложение получателю через Sendgrid. Я искал решение, но не смог найти рабочий пример. Возможно, потому что это невозможно, чего я хочу достичь.
МОЙ ВИД (КЛИЕНТ):
<div ng-controller="ContactFormCtrl" id="contactformbox" style="margin-top:50px;" class="mainbox" >
<div class="panel panel-info" >
<div class="panel-heading">
<div class="panel-title">Solliciteer direct</div>
</div>
<div style="padding-top:30px" class="panel-body" >
<form id="loginform" class="form-horizontal" role="form" name="contactform">
<div style="margin-bottom: 25px" class="input-group">
<span class="input-group-addon"><i class="glyphicon glyphicon-user"></i></span>
<input type="email" name="to" ng-model="email.to" ng-required="true" id="email-to" class="form-control" name="username" value="" placeholder="The emailadres from the employer">
</div>
<div style="margin-bottom: 25px" class="input-group">
<span class="input-group-addon"><i class="glyphicon glyphicon-lock"></i></span>
<input type="email" name="from" ng-model="email.from" ng-required="true" id="email-from" class="form-control" name="email-from" placeholder="Your e-mail address">
</div>
<div style="margin-bottom: 25px" class="input-group">
<span class="input-group-addon"><i class="glyphicon glyphicon-lock"></i></span>
<input type="text" name="subject" ng-model="email.subject" ng-required="true" id="email-subject" class="form-control" name="subject" placeholder="Your subject please">
</div>
<div style="margin-bottom: 25px" class="input-group">
<input type="file" name="file" ng-model="email.file" ng-required="true" id="email-file" class="form-control" name="file">
</div>
<div style="margin-bottom: 25px" class="input-group">
<textarea ng-model="email.text" name="text" placeholder="Enter Text Here.." class="form-control" rows="5" id="comment"></textarea>
</div>
<div style="margin-top:10px" class="form-group">
<!-- Button -->
<div class="col-sm-12 controls">
<button id="emailSubmitBn" class="btn btn-success" type="submit" ng-click="submitEmail()">Submit</button>
</div>
</div>
</form>
</div>
</div>
МОЙ КОНТРОЛЛЕР (КЛИЕНТ):
angular.module('angularMyApp')
.controller('ContactFormCtrl', function ($scope, $http) {
$scope.submitEmail = function() {
console.log("TEST");
//Request
$http.post('/api/email', $scope.email)
.success(function(data, status) {
console.log("Sent ok");
})
.error(function(data, status) {
console.log("Error");
})
};
});
MY APP.JS(SERVER):
'use strict';
// Set default node environment to development
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
var express = require('express');
var config = require('./config/environment');
var http = require('http');
var bodyParser = require('body-parser');
var options = {
auth: {
api_key: process.env.SENDGRID_APIKEY;
}
}
var nodemailer = require('nodemailer');
var sgTransport = require('nodemailer-sendgrid-transport');
// Setup server
var app = express();
var server = require('http').createServer(app);
require('./config/express')(app);
require('./routes')(app);
var mailer = nodemailer.createTransport(sgTransport(options));
app.post('/api/email', function(req, res) {
var mailOptions = {
to: ['[email protected]', req.body.to],
from: req.body.from,
subject: req.body.subject,
text: req.body.text
};
mailer.sendMail(mailOptions, function(err, res) {
if (err) {
console.log(err)
}
console.log(res);
});
});
// Start server
server.listen(config.port, config.ip, function () {
console.log('Express server listening on %d, in %s mode', config.port, app.get('env'));
});
// Expose app
exports = module.exports = app;
Ну, есть две основные проблемы:
- Clientside: как я могу разместить вложение с Angular до Node в этой форме? Нужно ли сначала загружать файл, или отправить его на Node с помощью $http.post? Или мне нужно использовать ng-file-upload?
- Serverside: как я могу отправить вложение в Sendgrid/Nodemailer. Отправка жестко заданного файла из моего app.js на сервере в Sendgrid не работает. Почта успешно отправляется в Sendgrid, но не содержит вложения.
Большое спасибо заранее!
Ответы
Ответ 1
1. Загрузка файла с формой в angular
Первая проблема заключается в том, что ng-model
не работает с <input type="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]);
});
});
}
};
}]);
Затем используйте директиву в элементе ввода файла следующим образом:
<input type="file" file-model="email.attachment" ng-required="true" id="email-attachment" name="attachment" class="form-control" />
Обратите внимание, что я изменил email.file
на email.attachment
, чтобы избежать путаницы в остальной части кода.
Затем вам нужно включить файл в свой запрос AJAX. Для этого вам нужно будет использовать FormData
. Заполните его с помощью 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");
});
};
Примечание. Я также передал объект конфигурации $http.post()
. Это делается для предотвращения angular от анализа объекта FormData
и установки типа содержимого.
Я полагался на этот пост в блоге для этой части ответа.
2. Отправка приложения с помощью Nodemailer
Чтобы получить доступ к файлу в экспресс, используйте multer.
Установка:
$ npm install --save multer
Использование:
var multer = require('multer');
var upload = multer();
app.post('/api/email', upload.single('attachment'), function(req, res) {
// req.file contains the 'attachment' file
...
});
Из Раздел Readme для Nodemailer по полям электронной почты он говорит, что использует свойство attachments
, которое представляет собой массив attachment
объектов.
app.post('/api/email', upload.single('attachment'), function(req, res) {
var mailOptions = {
to: ['[email protected]', 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);
});
});
В приведенном выше примере хранится вложение в памяти, что может быть плохо, если большие файлы загружаются часто. Вместо этого вы можете записывать файлы на диск:
var upload = multer({ dest: '/uploads' });
Затем вместо того, чтобы устанавливать файл buffer
в качестве вложения content
, вы должны установить вложение path
в файл path
:
attachments: [
{
filename: req.file.originalname,
path: req.file.path
}
]
Ответ 2
Наконец-то я получил работу благодаря gilly3. Мне пришлось сделать одно небольшое изменение в его коде. В контроллере я изменил:
formData.set(key, $scope.email[key]);
к следующему коду:
formData.append(key, $scope.email[key]);