Open BhaskaranR opened 7 years ago
any update on this?
Need too ;)
faced same issue too any update
If people are still looking for an answer to this, I found 2 hacks (Still not sure which to settle on) Both requires using a lib that is a fork of this one, but uses the new HttpClient module - https://ackerapple.github.io/angular-file/
Hack 1:
private uploadFiles(formData: FormData): Observable<HttpEvent<any>> {
// Prepare request options
// const url: string = Urls.DOC_UPLOAD_V1;
const url: string = 'https://localhost:3000/v1/upload';
const requestHeader: HttpHeaders = new HttpHeaders()
.set('Authorization', 'Bearer ' + *token*);
const options: any = {,
reportProgress: true,
headers: requestHeader
};
// Prepare request
const req = new HttpRequest<FormData>('POST', url, formData, options);
return this.http.request(req);
}
Action.ts
static Upload(formData: FormData): Action {
return {
type: 'UPLOAD',
payload: formData
};
}
static UploadStarted(httpEvent: HttpEvent<any>): Action {
return {
type: 'UPLOAD_STARTED',
payload: httpEvent
};
}
static UploadFailed(error: Error): Action {
return {
type: 'UPLOAD_FAILED',
payload: error
};
}
@Effect()
upload: Observable<Action> = this._actions$
.ofType('UPLOAD')
.switchMap((action: Action) => {
const formData: FormData = action.payload;
return this.uploadService.uploadFiles(formData)
.map((httpEvent: HttpEvent<any>) => {
return UploadActions.UploadStarted(httpEvent);
})
.catch(error => Observable.of(UploadActions.UploadFailed(error)));
});
Reducer.ts
case 'UPLOAD_STARTED':
{
const httpEvent: HttpEvent<any> = action.payload;
const partialState: IPartialAppState = {
error: null,
loading: false,
httpEvent: httpEvent
};
return Object.assign({}, appState, partialState);
}
And in my subscription in the component, i pass the httpEvent to:
<ngfUploadStatus
[(percent)] = "progress"
[httpEvent] = "httpEvent"
></ngfUploadStatus>
which handles the progress if you want. Otherwise you can subscribe to the event and based on the type, dispatch new actions to determine if things are complete or not.
Hack 2: Make the subscription in the upload service uploadFile method and dispatch different actions based on the event type. Something similar to this:
this.http.request(req).subscribe((event: HttpEvent<any>) => {
switch (event.type) {
case HttpEventType.Sent:
console.log('Request sent!');
break;
case HttpEventType.ResponseHeader:
console.log('Response header received!');
break;
case HttpEventType.DownloadProgress:
const kbLoaded = Math.round(event.loaded / 1024);
console.log(`Download in progress! ${ kbLoaded }Kb loaded`);
break;
case HttpEventType.Response:
console.log('😺 Done!', event.body);
}
});
}
With this path, if you want progress displayed, you can use a progress service that houses a BehaviorSubject to track things like progress% or isUploading or isDone states. The modified upload service can call .next with new values and any component interested in upload status can subscribe to the BehaviorSubject.
Not as elegant, but both should allow us to keep the ngrx architecture. Still experimenting...
I ended up going with a variant of hack 2. This should actually allow you to utilize ng2-file-upload as is, as long as you decouple the uploader from from the service that actually performs the upload. You can use the uploader to manage the upload queue and getting the files. Once you extract the files out, you can dispatch it out similar to what i described above. On success, you can clear the queue
This is the updated service and effect. The actions and reducer are mostly similar to the above
Upload Service:
public uploadFiles(formData: FormData): Observable<number> {
return new Observable((observer) => {
// Prep upload status
let percent: number = 0;
// Prepare request options
const url: string = 'https://localhost:3000/v1/upload';
const requestHeader: HttpHeaders = new HttpHeaders()
.set('Authorization', 'Bearer ' + *token*);
const options: any = {
reportProgress: true,
headers: requestHeader
};
// Prepare request
const req = new HttpRequest<FormData>('POST', urlTemp, formData, options);
this._httpEmitter = this._http.request(req).subscribe((event: HttpEvent<any>) => {
switch (event.type) {
case HttpEventType.Sent:
console.log('Request Sent!', event);
observer.next(percent);
observer.complete();
break;
case HttpEventType.UploadProgress:
percent: number = Math.round(100 * event.loaded / event.total);
console.log('Request Uploading..!', `${percent}%`);
this._store.dispatch(UploadActions.UploadProgress(percent));
break;
case HttpEventType.ResponseHeader:
console.log('Request Header received!', event);
break;
case HttpEventType.DownloadProgress:
console.log('Request Download in progress!', event);
break;
case HttpEventType.Response:
console.log('Request Completed!', event);
this._unsubscribeEmitter();
this._store.dispatch(UploadActions.UploadSucceeded(<Response>event.body));
break;
default:
console.log('Unhandled event type', event);
break;
}
},
(error) => {
console.log('Upload error', error);
this._unsubscribeEmitter();
this._store.dispatch(
UploadActions.UploadFailed(error)
);
});
});
}
private _unsubscribeEmitter(): void { if (this._httpEmitter) { this._httpEmitter.unsubscribe(); this._httpEmitter = null; } }
- Effects.ts
``` Typescript
@Effect()
upload: Observable<Action> = this._actions$
.ofType('UPLOAD')
.switchMap((action: Action) => {
const formData: FormData = action.payload;
return this.uploadService.uploadFiles(formData)
.map((uploadPercent: number) => UploadActions.UploadSucceeded(uploadPercent))
.catch(error => Observable.of(UploadActions.UploadFailed(error)));
});
Initially, i didn't want to dispatch actions from the service, but after looking at a few redux examples, I realize that was a baseless limitation. As long as the store is being modified only through actions (single pipeline), it should be ok to dispatch actions from anywhere. With this mentality, it became easier to manage the whole processes.
The one thing to call out, the uploadFiles method in the upload service returns an observable
Hope this helps someone :)
@Sliverb any chance you have that in a repo we can look at?
@shawnrmoss Sadly no, its a private project I'm helping with. However, I can extract more snippets for you. Which part would you like more details on?
Quick update, the design we ended up going with above has been working flawlessly in case any future ngrx users stumbles upon this thread.
If more code snippets/explanation/breakdown is needed, feel free to ask away. Happy to be of service.
We use ngrx in our application and all the communication to the service is through effect, action and store. the file upload submit breaks the current architecture. Can we wrap the entire upload queue to a store?