Когда возникает такая тривиальнейшая задача как отправка картинки $http запросом, ожидаешь, что ничего сложного возникнуть не может и такой функционал должен бы решаться на уровне фреймворка. Но не все так просто, как хотелось бы. Поэтому хочу поделиться небольшими примерами того, как отправлять файл и как можно отправить его вместе с другими полями формы.
Обработка прикрепления файлов
В первую очередь необходимо получить файлы из input. Просто присвоить модель не выйдет, ведь ngModel
не работает с input type file
. Они будут храниться в объекте события, который в angular называется $event
, его мы и передадим в качестве параметра функции addPhoto()
.
<input type="file" name="photo" (change)="addPhoto($event)"/>
Сама же функция, которая будет присваивать список прикрепленных файлов в переменную компонента, будет выглядеть так:
addPhoto(event) { let target = event.target || event.srcElement; this.files = target.files; }
event.srcElement — используется для поддержки кроссбраузерности.
Теперь для отправки файлов POST запросом необходимо создать FormData и к ней прикрепить полученные ранее файлы.
if (this.files) { let files :FileList = this.files; const formData = new FormData(); for(let i = 0; i < files.length; i++){ formData.append('photo', files[i]); } }
Для отправки нескольких файлов, input должен содержать атрибут multiple.
Данный код можно вставить как в функцию addPhoto()
, что позволит автоматически заливать файл на сервер при его выборе, так и в функцию сабмита формы.
Отправка FormData
После генерации формы остается лишь отправить её ангуларовским $http запросом. При этом указывать Content-Type нет необходимости, angular сам обнаружит и пропишет multipart/form-data
и boundary
.
this.http.post('endpoint-url', formData).map((response: Response) => response.json()).subscribe(resp => { //код выполняемый при успехе });
Раз уж мы отправляем файл по сабмиту формы, хотелось бы чтобы с файлом отправлялись и остальные поля формы. При этом поля могут быть не просто строками, а содержать вложенные объекты. Задачу достаточно легко решить путем преобразования модели формы в текстовую json строку. И добавить эту строку как поле FormData:
formData.append('data', JSON.stringify(this.form));
Финальный вид всего компонента
Суммируя выше написанный код в единый компонент, получим следующее:
<form name="form" (ngSubmit)="f.form.valid && submitRegister()" #f="ngForm" novalidate> <input type="text" placeholder="Фамилия" name="last" [(ngModel)]="form.name.last" #last="ngModel"/> <input type="text" placeholder="Имя" name="first" [(ngModel)]="form.name.first" #first="ngModel"/> <input type="file" name="photo" (change)="addPhoto($event)"/> <button type="submit">Отправить</button> </form>
import { Component } from '@angular/core'; import { Http } from '@angular/http'; @Component({ templateUrl: './form.component.html' }) export class FormComponent { form: any = {}; files: any; constructor(public http:Http) { this.form = { name: {} }; } addPhoto(event) { let target = event.target || event.srcElement; this.files = target.files; } submitRegister() { let final_data; if (this.files) { let files: FileList = this.files; const formData = new FormData(); for (let i = 0; i < files.length; i++) { formData.append('photo', files[i]); } formData.append('data', JSON.stringify(this.form)); final_data = formData; } else { //Если нет файла, то слать как обычный JSON final_data = this.form; } this.http.post('/api/form', final_data).map((response: Response) => response.json()).subscribe(resp => { //... }) } }
Итог
Конечно, данный код — лишь пример, и в вашем компоненте запрос будет вынесен в отдельный сервис, а имена переменных будут продуманней. Данный принцип отправки файлов подходит для реализации и в первой версии Angular. Отличия будут лишь в синтаксисе фреймворка.
Если вам полезен данный материал, подписывайтесь на мой блог, чтобы получать уведомления о новых публикациях на темы: верстки, javascript и разработки кроссплатформенных мобильных приложений.
О том, как я перешел с PhpStorm на VS Code