├── .gitignore
├── README.md
├── app
├── boot.ts
├── components
│ ├── app
│ │ ├── app.html
│ │ └── app.ts
│ ├── home
│ │ ├── home.html
│ │ └── home.ts
│ └── login
│ │ ├── login.html
│ │ ├── login.ts
│ │ └── user.ts
└── plugins
│ └── multipart-upload
│ ├── multipart-item.ts
│ └── multipart-uploader.ts
├── index.html
├── package.json
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | npm-debug.log
2 | *.js.map
3 | .idea
4 | app/**/*.js
5 | app/**/*.js.map
6 | node_modules
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # angular2-multipartForm
2 | A project for Angular 2 apps to upload multipart/form-data.
3 |
4 | It is something similar to the [ng2-file-upload](https://github.com/valor-software/ng2-file-upload) which only upload file.
5 |
6 |
7 | # How to start
8 | ```
9 | git clone https://github.com/wangzilong/angular2-multipartForm.git
10 | cd angular2-multipartForm
11 | npm install
12 | # run
13 | npm start
14 | ```
15 | # Input File in Angularjs 2
16 | home.html
17 | ```
18 |
19 | ```
20 | home.ts
21 | ```
22 | selectFile($event): void {
23 | var inputValue = $event.target;
24 | this.file = inputValue.files[0];
25 | console.debug("Input File name: " + this.file.name + " type:" + this.file.size + " size:" + this.file.size);
26 | }
27 | ```
28 |
29 | # Upload multipart/form-data
30 | home.ts
31 | ```
32 | this.upload = () => {
33 | if (null == this.file || null == this.email || null == this.password){
34 | console.error("home.ts & upload() form invalid.");
35 | return;
36 | }
37 | // init form item
38 | if (this.multipartItem == null){
39 | this.multipartItem = new MultipartItem(this.uploader);
40 | }
41 | if (this.multipartItem.formData == null)
42 | this.multipartItem.formData = new FormData();
43 |
44 | // fill field of form into formData.
45 | this.multipartItem.formData.append("email", this.email);
46 | this.multipartItem.formData.append("password", this.password);
47 | this.multipartItem.formData.append("file", this.file);
48 |
49 | // set callback function
50 | this.multipartItem.callback = this.uploadCallback;
51 |
52 | // upload
53 | this.multipartItem.upload();
54 | }
55 | ```
56 |
--------------------------------------------------------------------------------
/app/boot.ts:
--------------------------------------------------------------------------------
1 | import {bootstrap} from 'angular2/platform/browser'
2 | import {AppComponent} from './components/app/app'
3 |
4 | import {ROUTER_PROVIDERS} from 'angular2/router';
5 |
6 | bootstrap(AppComponent,[
7 | ROUTER_PROVIDERS
8 | ]);
--------------------------------------------------------------------------------
/app/components/app/app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/components/app/app.ts:
--------------------------------------------------------------------------------
1 | import {Component} from 'angular2/core';
2 | import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
3 |
4 |
5 | import {HomeComponent} from '../home/home';
6 | import {LoginComponent} from '../login/login';
7 |
8 | @Component({
9 | selector: 'my-app',
10 | template: `
11 |
15 |
16 |
17 | `,
18 | directives: [ROUTER_DIRECTIVES]
19 | })
20 |
21 | @RouteConfig([
22 | { path: '/home', name: 'Home', component: HomeComponent, useAsDefault: true },
23 | { path: '/login', name: 'Login', component: LoginComponent}
24 | ])
25 |
26 | export class AppComponent {
27 | }
28 |
--------------------------------------------------------------------------------
/app/components/home/home.html:
--------------------------------------------------------------------------------
1 |
2 |
17 |
--------------------------------------------------------------------------------
/app/components/home/home.ts:
--------------------------------------------------------------------------------
1 | import {Component} from 'angular2/core';
2 |
3 | import {RouteParams, ROUTER_DIRECTIVES} from 'angular2/router';
4 | import {MultipartItem} from "../../plugins/multipart-upload/multipart-item";
5 | import {MultipartUploader} from "../../plugins/multipart-upload/multipart-uploader";
6 |
7 | const URL = 'http://www.example.com/rest/upload/avatar';
8 |
9 | @Component({
10 | selector: 'home',
11 | templateUrl: 'app/components/home/home.html'
12 | })
13 | export class HomeComponent {
14 | private uploader:MultipartUploader = new MultipartUploader({url: URL});
15 |
16 | multipartItem:MultipartItem = new MultipartItem(this.uploader);
17 |
18 | email:string;
19 | password:string;
20 | file: File;
21 |
22 | upload : () => void;
23 | uploadCallback : (data) => void;
24 |
25 | constructor(){
26 | this.upload = () => {
27 | console.debug("home.ts & upload() ==>");
28 | if (null == this.file || null == this.email || null == this.password){
29 | console.error("home.ts & upload() form invalid.");
30 | return;
31 | }
32 | if (this.multipartItem == null){
33 | this.multipartItem = new MultipartItem(this.uploader);
34 | }
35 | if (this.multipartItem.formData == null)
36 | this.multipartItem.formData = new FormData();
37 |
38 | this.multipartItem.formData.append("email", this.email);
39 | this.multipartItem.formData.append("password", this.password);
40 | this.multipartItem.formData.append("file", this.file);
41 |
42 | this.multipartItem.callback = this.uploadCallback;
43 | this.multipartItem.upload();
44 | }
45 |
46 | this.uploadCallback = (data) => {
47 | console.debug("home.ts & uploadCallback() ==>");
48 | this.file = null;
49 | if (data){
50 | console.debug("home.ts & uploadCallback() upload file success.");
51 | }else{
52 | console.error("home.ts & uploadCallback() upload file false.");
53 | }
54 | }
55 |
56 |
57 | }
58 |
59 | selectFile($event): void {
60 | var inputValue = $event.target;
61 | if( null == inputValue || null == inputValue.files[0]){
62 | console.debug("Input file error.");
63 | return;
64 | }else {
65 | this.file = inputValue.files[0];
66 | console.debug("Input File name: " + this.file.name + " type:" + this.file.size + " size:" + this.file.size);
67 | }
68 | }
69 |
70 |
71 | }
72 |
73 |
--------------------------------------------------------------------------------
/app/components/login/login.html:
--------------------------------------------------------------------------------
1 | Login component
2 |
--------------------------------------------------------------------------------
/app/components/login/login.ts:
--------------------------------------------------------------------------------
1 | import {Component} from 'angular2/core';
2 |
3 | import {RouteParams, ROUTER_DIRECTIVES, Router} from 'angular2/router';
4 | import {NgForm} from 'angular2/common';
5 | import {User} from './user';
6 |
7 | @Component({
8 | selector: 'login',
9 | templateUrl: 'app/components/login/login.html'
10 | })
11 | export class LoginComponent {
12 |
13 | user = new User('111','222');
14 |
15 | constructor(public router: Router) {
16 | }
17 |
18 | submitted = false;
19 |
20 | onSubmit() { this.submitted = true; }
21 |
22 | login(event, name, password) {
23 | console.log("login name: " + name);
24 | console.log("login password: " + password);
25 | }
26 |
27 | get diagnostic() {
28 | return JSON.stringify(this.user);
29 | }
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/app/components/login/user.ts:
--------------------------------------------------------------------------------
1 | export class User{
2 | name: string;
3 | password: string;
4 |
5 | constructor(name, password) {
6 | this.name = name;
7 | this.password = password;
8 | }
9 | }
--------------------------------------------------------------------------------
/app/plugins/multipart-upload/multipart-item.ts:
--------------------------------------------------------------------------------
1 | import {MultipartUploader} from "./multipart-uploader";
2 |
3 | export class MultipartItem {
4 | public alias:string = 'file';
5 | public url:string = '/';
6 | public method:string = 'POST';
7 | public headers:any = [];
8 | public withCredentials:boolean = true;
9 | public formData:FormData = null;
10 | public isReady:boolean = false;
11 | public isUploading:boolean = false;
12 | public isUploaded:boolean = false;
13 | public isSuccess:boolean = false;
14 | public isCancel:boolean = false;
15 | public isError:boolean = false;
16 | public progress:number = 0;
17 | public index:number = null;
18 | public callback:Function = null;
19 |
20 | constructor(private uploader:MultipartUploader) {
21 | }
22 |
23 | public upload() {
24 | try {
25 | console.debug("multipart-item.ts & upload() ==>.");
26 | this.uploader.uploadItem(this);
27 | } catch (e) {
28 | //this.uploader._onCompleteItem(this, '', 0, []);
29 | //this.uploader._onErrorItem(this, '', 0, []);
30 | }
31 | }
32 |
33 | public init(){
34 | this.isReady = false;
35 | this.isUploading = false;
36 | this.isUploaded = false;
37 | this.isSuccess = false;
38 | this.isCancel = false;
39 | this.isError = false;
40 | this.progress = 0;
41 | this.formData = null;
42 | this.callback = null;
43 | }
44 |
45 | public onBeforeUpload() {
46 | }
47 |
48 | public onProgress(progress:number) {
49 | }
50 |
51 | public onSuccess(response:any, status:any, headers:any) {
52 | }
53 |
54 | public onError(response:any, status:any, headers:any) {
55 | }
56 |
57 | public onCancel(response:any, status:any, headers:any) {
58 | }
59 |
60 | public onComplete(response:any, status:any, headers:any) {
61 | this.callback(response);
62 | this.init();
63 | }
64 |
65 | private _onBeforeUpload() {
66 | this.isReady = true;
67 | this.isUploading = true;
68 | this.isUploaded = false;
69 | this.isSuccess = false;
70 | this.isCancel = false;
71 | this.isError = false;
72 | this.progress = 0;
73 | this.onBeforeUpload();
74 | }
75 |
76 | private _onProgress(progress:number) {
77 | this.progress = progress;
78 | this.onProgress(progress);
79 | }
80 |
81 | private _onSuccess(response:any, status:any, headers:any) {
82 | this.isReady = false;
83 | this.isUploading = false;
84 | this.isUploaded = true;
85 | this.isSuccess = true;
86 | this.isCancel = false;
87 | this.isError = false;
88 | this.progress = 100;
89 | this.index = null;
90 | this.onSuccess(response, status, headers);
91 | }
92 |
93 | private _onError(response:any, status:any, headers:any) {
94 | this.isReady = false;
95 | this.isUploading = false;
96 | this.isUploaded = true;
97 | this.isSuccess = false;
98 | this.isCancel = false;
99 | this.isError = true;
100 | this.progress = 0;
101 | this.index = null;
102 | this.onError(response, status, headers);
103 | this.callback(response);
104 | }
105 |
106 | private _onCancel(response:any, status:any, headers:any) {
107 | this.isReady = false;
108 | this.isUploading = false;
109 | this.isUploaded = false;
110 | this.isSuccess = false;
111 | this.isCancel = true;
112 | this.isError = false;
113 | this.progress = 0;
114 | this.index = null;
115 | this.onCancel(response, status, headers);
116 | }
117 |
118 | private _onComplete(response:any, status:any, headers:any) {
119 | this.onComplete(response, status, headers);
120 | }
121 |
122 | private _prepareToUploading() {
123 | this.isReady = true;
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/app/plugins/multipart-upload/multipart-uploader.ts:
--------------------------------------------------------------------------------
1 | import {MultipartItem} from "./multipart-item";
2 | export class MultipartUploader {
3 | public url:string;
4 | public authToken:string;
5 | public isUploading:boolean = false;
6 | public progress:number = 0;
7 | public isHTML5:boolean = true;
8 | public timeout:number = 10000;
9 |
10 | constructor(public options:any) {
11 | // Object.assign(this, options);
12 | this.url = options.url;
13 | this.authToken = options.authToken;
14 | }
15 |
16 | public uploadItem(item:MultipartItem) {
17 | console.debug("multipart-uploader.ts & uploadItem() ==>.");
18 | if (this.isUploading) {
19 | console.debug("multipart-uploader.ts & uploadItem() uploader is uploading now.");
20 | return;
21 | }
22 | this.isUploading = true;
23 | this._xhrTransport(item);
24 | }
25 |
26 | private _onBeforeUploadItem(item:any) {
27 | item._onBeforeUpload();
28 | }
29 |
30 |
31 | private _parseHeaders(headers:any) {
32 | let parsed:any = {}, key:any, val:any, i:any;
33 |
34 | if (!headers) {
35 | return parsed;
36 | }
37 |
38 | headers.split('\n').map((line:any) => {
39 | i = line.indexOf(':');
40 | key = line.slice(0, i).trim().toLowerCase();
41 | val = line.slice(i + 1).trim();
42 |
43 | if (key) {
44 | parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
45 | }
46 | });
47 |
48 | return parsed;
49 | }
50 |
51 | private _transformResponse(response:any, headers:any):any {
52 | return response;
53 | }
54 |
55 | private _isSuccessCode(status:any) {
56 | return (status >= 200 && status < 300) || status === 304;
57 | }
58 |
59 | private _render() {
60 | // todo: ?
61 | }
62 |
63 | _xhrTransport(item:any) {
64 | console.debug("multipart-uploader.ts & _xhrTransport() ==>.");
65 |
66 | let xhr = item._xhr = new XMLHttpRequest();
67 | xhr.timeout = this.timeout;
68 |
69 | //if (item.formData.length === 0){
70 | // throw new TypeError('Invalid form,form is empty.');
71 | //}
72 |
73 | this._onBeforeUploadItem(item);
74 |
75 | xhr.upload.onprogress = (event) => {
76 | };
77 |
78 | xhr.onload = () => {
79 | console.debug("multipart-uploader.ts & _xhrTransport.onload() ==>");
80 | let headers = this._parseHeaders(xhr.getAllResponseHeaders());
81 | let response = this._transformResponse(xhr.response, headers);
82 | let gist = this._isSuccessCode(xhr.status) ? 'Success' : 'Error';
83 | let method = '_on' + gist + 'Item';
84 | (this)[method](item, response, xhr.status, headers);
85 | this._onCompleteItem(item, response, xhr.status, headers);
86 | };
87 |
88 | xhr.onerror = () => {
89 | console.debug("multipart-uploader.ts & _xhrTransport.onerror() ==>");
90 | let headers = this._parseHeaders(xhr.getAllResponseHeaders());
91 | let response = this._transformResponse(xhr.response, headers);
92 | this._onErrorItem(item, response, xhr.status, headers);
93 | //this._onCompleteItem(item, response, xhr.status, headers);
94 | };
95 |
96 | xhr.ontimeout = () => {
97 | console.debug("multipart-uploader.ts & _xhrTransport.ontimeout() ==>");
98 | let headers = this._parseHeaders(xhr.getAllResponseHeaders());
99 | let response = this._transformResponse(xhr.response, headers);
100 | this._onErrorItem(item, response, xhr.status, headers);
101 | //this._onCompleteItem(item, response, xhr.status, headers);
102 | };
103 |
104 | xhr.onabort = () => {
105 | console.debug("multipart-uploader.ts & _xhrTransport.onabort() ==>");
106 | let headers = this._parseHeaders(xhr.getAllResponseHeaders());
107 | let response = this._transformResponse(xhr.response, headers);
108 | //this._onCancelItem(item, response, xhr.status, headers);
109 | this._onCompleteItem(item, response, xhr.status, headers);
110 | };
111 |
112 | xhr.open(item.method, this.url, true);
113 | xhr.withCredentials = item.withCredentials;
114 |
115 | if (this.authToken) {
116 | xhr.setRequestHeader('Authorization', this.authToken);
117 | }
118 | console.debug("multipart-uploader.ts & _xhrTransport() send...");
119 | xhr.send(item.formData);
120 | this._render();
121 | }
122 |
123 | public onSuccessItem(item:any, response:any, status:any, headers:any) {
124 | }
125 |
126 | public onErrorItem(item:any, response:any, status:any, headers:any) {
127 | this.isUploading = false;
128 | }
129 |
130 | public onCancelItem(item:any, response:any, status:any, headers:any) {
131 | }
132 |
133 | public onCompleteItem(item:any, response:any, status:any, headers:any) {
134 | }
135 |
136 | private _onSuccessItem(item:any, response:any, status:any, headers:any) {
137 | item._onSuccess(response, status, headers);
138 | this.onSuccessItem(item, response, status, headers);
139 | }
140 |
141 | public _onErrorItem(item:any, response:any, status:any, headers:any) {
142 | console.debug("multipart-uploader.ts & _onErrorItem() ==>" + " Error status:" + status);
143 | item._onError(response, status, headers);
144 | this.onErrorItem(item, response, status, headers);
145 | }
146 |
147 | private _onCancelItem(item:any, response:any, status:any, headers:any) {
148 | item._onCancel(response, status, headers);
149 | this.onCancelItem(item, response, status, headers);
150 | }
151 |
152 | public _onCompleteItem(item:any, response:any, status:any, headers:any) {
153 | item._onComplete(response, status, headers);
154 | this.onCompleteItem(item, response, status, headers);
155 |
156 | this.isUploading = false;
157 |
158 | //this.progress = this._getTotalProgress();
159 | this._render();
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Angular 2 QuickStart
5 |
6 |
7 |
9 |
11 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
37 |
38 |
39 |
40 |
41 |
42 | Loading...
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ng2-form",
3 | "version": "1.0.0",
4 | "scripts": {
5 | "tsc": "tsc",
6 | "tsc:w": "tsc -w",
7 | "lite": "lite-server",
8 | "start": "concurrent \"npm run tsc:w\" \"http-server\" "
9 | },
10 | "license": "ISC",
11 | "dependencies": {
12 | "angular2": "2.0.0-beta.0",
13 | "systemjs": "0.19.6",
14 | "es6-promise": "^3.0.2",
15 | "es6-shim": "^0.33.3",
16 | "reflect-metadata": "0.1.2",
17 | "rxjs": "5.0.0-beta.0",
18 | "zone.js": "0.5.10"
19 | },
20 | "devDependencies": {
21 | "concurrently": "^1.0.0",
22 | "lite-server": "^1.3.1",
23 | "typescript": "^1.7.3"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES5",
4 | "module": "system",
5 | "moduleResolution": "node",
6 | "sourceMap": true,
7 | "emitDecoratorMetadata": true,
8 | "experimentalDecorators": true,
9 | "removeComments": false,
10 | "noImplicitAny": false
11 | },
12 | "exclude": [
13 | "node_modules"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------