In the introduction of this series I mentioned that for the part 1 of this series we shall look at how to upload an image in Angular 2 and then send that image via HTTP request to an API for processing.
This article assumes that you already have angular 2 framework setup.
Based on the scenario defined in the introduction we need a profile page that enables the user to upload an image.
So first lets create a simple html template
<div> <h2>Upload Image</h2> <input type="file" accept="image/*" (change)="handleInputChange($event)"/> </div>
In the template above the only thing going on is the input tag of type file, of course in the real world you would have other form inputs in your profile page. But for the purpose of this article lets keep it simple. You would notice that we are listening to the change event of the input tag which would trigger the handleInputChange
function. Now lets implement that function
import {Component} from '@angular/core'; import { Observable } from 'rxjs/Observable'; import { Http, Headers, Request, Response, RequestOptions } from '@angular/http'; import 'rxjs/add/operator/catch'; import 'rxjs/add/operator/map'; @Component({ selector: 'my-app', templateUrl: 'app.component.html', }) export class AppComponent { private apiBaseUrl = 'http://api-test.com'; //this is a fake url. Put in your own API url. headers: Headers = new Headers(); constructor(private _http: Http) {} /** * Handles the change event of the input tag, * Extracts the image file uploaded and * makes an Http request with the image file. */ handleInputChange (event) { var image = event.target.files[0]; var pattern = /image-*/; var reader = new FileReader(); if (!image.type.match(pattern)) { console.error('File is not an image'); //of course you can show an alert message here return; } let endPoint = '/upload/profileImage'; //use your own API endpoint let headers = new Headers(); headers.set('Content-Type', 'application/octet-stream'); headers.set('Upload-Content-Type', image.type) this.makeRequest(endPoint, 'POST', image, headers).subscribe( response => {this.handleSuccess(response); }, error => {this.handleError(error); } ); } /** * Makes the HTTP request and returns an Observable */ private makeRequest (endPoint: string, method: string, body = null, headers: Headers = new Headers()): Observable<any> { let url = this.apiBaseUrl + endPoint; this.headers = headers; if (method == 'GET') { let options = new RequestOptions({ headers: this.headers }); return this._http.get(url, options) .map(this.extractData) .catch(this.extractError); } else if (method == 'POST') { let options = new RequestOptions({ headers: this.headers }); return this._http.post(url, body, options) .map(this.extractData) .catch(this.extractError); } } /** * Extracts the response from the API response. */ private extractData (res: Response) { let body = res.json(); return body.response || { }; } private extractError (res: Response) { let errMsg = 'Error received from the API'; return errMsg; } private handleSuccess(response) { console.log('Successfully uploaded image'); //provide your own implementation of handling the response from API } private handleError(errror) { console.error('Error uploading image') //provide your own implementation of displaying the error message } }
It may seem like a lot is going on in the app.component.ts
file, but actually if you take a closer look you would realize that it is not the case. It is actually quite straight forward.
The ‘handleInputChange’ function which is the only public function there is called after a new file is selected for upload from the file input tag.
The interesting part is where it calls the makeRequest
function, you notice the two headers that are passed; Content-Type
and Upload-Content-Type
and there is a good reason for that. In angular 2 when posting a File or Blob the http request class expects the content-type to be application/octet-stream
else it will either fail or cast the body to a string which will cause the API not be able to recognize the content of the body as a File or Blob.
While the other header Upload-Content-Type
which is a custom header is used specially for the API. The API expects it (as you will see in the next article) and uses its value to determine the extension of the image.
And there you have it. You can view the entire code in plunker.
It is worth it to note that in the real world when implementing this it is recommended to abstract the http call in an injectable module. So for instance you would have an HttpService class which will handle the HTTP requests. And then inject the HttpService class into the AppComponent class.
Lets go on to the next article.