๐ช ๋ฌธ์ ์ํฉ
์ง๋ ํ๋ก์ ํธ ์ ์ง๋ณด์ ์งํ์ค ์ ์ก๋์ง ์๋ ํ์ผ์ด ์กด์ฌํ์ฌ, ์์์๋ฃ๊ฐ ๋์ง ์๋ ๊ฒฝ์ฐ๊ฐ ๋ฐ์ํ์๋ค. ๊ทธ๋ฆฌํ์ฌ ngrx๋ฅผ ํ์ฉํ์ฌ ์๋ฌ๋ ํ์ผ์ store๋ด์์ ์ฒ๋ฆฌ ํด๋ณด๊ธฐ๋ก ํ์๋ค. ๋ฌธ์ ์ํฉ ๋ฐ ํด๊ฒฐ ๋ฐฉ์์ ์๋์ ๊ฐ๋ค.
์์ฒ๋ผ "3๋ฒ"์์ ์๋ฌ๊ฐ ๋ ๊ฒฝ์ฐ ๋ฉ์ถ๋ ๊ฒ์ด ์๋ Store์ ๋ด๋๋ค.
๐จ๐ผ 500 ์๋ฌ ๋ ๊ฒฝ์ฐ Store์ ๋ด๊ธฐ
๐ฆง 1๋ฒ ์ํฉ์์ ์๋ฌ๊ฐ ๋ฐ์ํ ๋ ค๋ฉด actions์ addFileAction๋ฅผ ๋ฑ๋กํ๋ค.
//file.actions.ts
import { createAction, props } from '@ngrx/store';
import { File } from './file'; // File ๋ชจ๋ธ์ ๋ง๋ค์ด์ผ ํ๋ค.
export const addFileAction = createAction(
ActionTypes.ADD_FILE,
props<{ file: File}>()
)
๐1๋ฒ ์ํฉ์์ ํด๋น ์ปดํผ๋ํธ์์ ํ์ผ์ addFileAction๋ฅผ ํธ์ถํ๋ค.
// ์ฌ์ฉ๋๋ ์ปดํผ๋ํธ
import { Store } from '@ngrx/store'; //1.Store Import
import { addFileAction } from '../store/reducers/file/file.actions' //2.๋ฑ๋ก๋ actions import
import { File } from '../store/reducers/file/file'; //3.File ๋ชจ๋ธ ์ ์ธ
export class RxjsComponent implements OnInit {
constructor(private store: Store) {
}
errorPush(){
const fileItem:File ={
id:0, //๊ณ ์ ID
file:this.formData, //ํ์ผ
progressBar:0, // % ํ์๋ฅผ ์ํด ์ฌ์ฉ
loaded:0, // loaded ์๋ฃ
total:0 // total
}
this.store.dispatch(addFileAction({file:fileItem}))
}
}
๐ฌ2๋ฒ ์ํฉ์์ Reducer๋ฅผ ํตํด 3๋ฒ Store์ ๋ด๋๋ค.
import { createReducer, on } from '@ngrx/store';
import { FileState } from './file.state';
import * as FileActionsTypes from './file.actions'; // 1.actions์ ์ ์ธ๋ ๊ฒ์ import
//2.์ด๊ธฐ ์ํ ์ธํ
export const initialState:FileState = {
fileList: []
}
export const fileReducer = createReducer(
initialState, //
on(FileActionsTypes.addFileAction,(state,{ file }) =>{
//๊ธฐ์ค ๋ฌธํญ์ , ์๋ก์ด file ์ถ๊ฐ
return [...state.fileList,file];
})
)
๐ฏ ์๋ฌ๋ ํ์ผ ๋ฆฌ์คํธ ํ์ฌ ํ์ผ์ ์ก API๋ฅผ ํตํด ์ฌ์ ์ก ํ๊ธฐ
์ธ์ ๋ Store์ ๋ด์ ํ์ผ์ ๋ฆฌ์คํธํ ํ์ฌ ๋น๋๊ธฐ๋ก ํ์ผ์ ์ฌ์ ์ก ์์ผ๋ณด์.
๐ Store์ ํ์ผ์ ๋ฆฌ์คํธํ ํ๊ธฐ
ํ์ฌ store(this.store.select(getFileList))์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์์ fileList์ ๋ด์์ค๋ค.
/* ํด๋น ์ปดํผ๋ํธ typescript */
//ํ์ผ ngrx
import { Store } from '@ngrx/store';
import { getFileList } from '../store/reducers/file/file.selectors';
export class RxjsComponent implements OnInit {
public fileList$ = this.store.select(getFileList); // Store ๋ฐ์ดํฐ selectors
public fileList: File[];
ngOnInit() {
this.fileList$.subscribe(
(result)=>{
this.fileList = _.cloneDeep(result); // this.fileList$์ property์ง์ ์ ์ธ ์ ๊ทผ์ ๋ถ๊ฐ๋ฅ ํ๋ฏ๋ก ์๋ก์ด ๊ฐ์ฒด๋ก ๋ณต์ฌ
},
(err)=>{
console.error('err : ',err);
},
()=>{
console.log('complete');
}
)
}
}
๋ค์์ผ๋ก html์ ํ๋ฉด์ ๋ณด์ฌ์ค๋ค.
<div *ngFor="let item of fileList">
<div class="progress">
<div class="progress-bar progress-bar-striped" [style.width]="item.progressBar+'%'" role="progressbar"
aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<h1 *ngIf="item.progressBar !== 100">{{ item.progressBar }}%</h1>
<h1 *ngIf="item.progressBar === 100 && (!item.isFinsh && (!item.isError))">์๋ฒ์ ์ ์ก์ค</h1>
<h1 *ngIf="item.progressBar === 100 && (item.isFinsh)">์ ์ก์๋ฃ</h1>
<h1 *ngIf="(item.progressBar === 100 && (item.isError)) || (item.progressBar !== 100 && (item.isError))">์ ์ก์คํจ</h1>
</div>
์ด 4๊ฐ์ง์ ๊ฒฝ์ฐ์ ๋ฐ๋ผ ์ ์ก ์ํ๋ฅผ ๋ณด์ฌ์ค๋ค
- *ngIf="item.progressBar !== 100" ๊ฐ์ ๊ฒฝ์ฐ๋ ํ์ฌ 100%๊ฐ ์๋๋ ๊ฒฝ์ฐ ์ฆ ์ ์ก์ค์ธ ๊ฒฝ์ฐ
- *ngIf="item.progressBar === 100 && (!item.isFinsh && (!item.isError))" ๋ 100%์ด๋ฉด์ ์๋ฒ์์ ๋ต์ฅ์ ๋ฐ์ง ๋ชปํ๊ฒฝ์ฐ
- *ngIf="item.progressBar === 100 && (item.isFinsh)" ๋ 100%์ด๋ฉด์ ์๋ฒ์์ ์ ์ก์ด ์๋ฃ๋๊ฒฝ์ฐ
- *(item.progressBar === 100 && (item.isError)) || (item.progressBar !== 100 && (item.isError))" ๋ ์๋ฒ์ ์ ์ก์ ์คํจํ์๊ฑฐ๋, ์ ๋ก๋์ ์คํจํ ๊ฒฝ์ฐ
๐ํ์ผ ์ฌ์ ์ก ํ๊ธฐ
angular์ http์ต์ ์๋ ํ์ผ์ ๋ก๋ ์ํ๋ฅผ ํ์ธํ ์ ์๋๋ก ์ต์ ์ ๋ฃ์ ์ ์๋ค.reportProgress:true,observe:'events'
๋ฅผ ํตํด ํ์ผ ์ ๋ก๋ ๊ด์ฐฐ ํ ์ ์๋ค.
// fileuploadService.ts
import { Injectable } from '@angular/core';
import { environment } from 'src/environments/environment';
import { HttpClient } from '@angular/common/http';
export class FileuploadService {
constructor(private http: HttpClient) {
}
fileUplaod(fb:any){
return this.http.post<any>(`${environment.apiUrl}/board/fileupload`,fb,{
reportProgress:true,
observe:'events'
});
}
}
๊ทธ๋ฆฌ๊ณ ํด๋น ์ปดํผ๋ํธ์์ "fileUplaod"๋ฅผ ํธ์ถํ๋ค. ๋ฏธ๋ฆฌ ๋ด์๋์ fileList์ ๋ฐ๋ณต์ ๋๋ ค์ ๊ฐ file์ api๋ฅผ ํธ์ถํ์ฌ ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌํ๋ค.
submit(){
this.fileList.forEach((fileItem)=>{
const formData= new FormData();
formData.append('id', fileItem.id as any) ;
formData.append('file' , fileItem.file.get('file'));
this.fileuploadService.fileUplaod(formData)
.pipe(
timeout(2000),
)
.subscribe(
(event: (HttpEvent<any>))=>{
switch (event.type) {
case HttpEventType.Sent:
console.log('Request has been made!');
break;
case HttpEventType.ResponseHeader:
console.log('Response header has been received!');
break;
case HttpEventType.UploadProgress:
//์
๋ก๋ ์ํ๋ฅผ ๊ด์ฐฐํ์ฌ ํ์ฌ ์
๋ก๋๋ฅผ ๊ณ์ฐํ๋ค.
fileItem.progressBar = Math.round((event.loaded / event.total) * 100);
break;
case HttpEventType.Response:
console.log('User successfully created!', event.body);
setTimeout(() => {
// ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌ๋ ํ์ผ๋ค์ ๊ฐ์๋ฅผ ํ์
ํ๊ธฐ ์ํด finshCount++
this.finshCount++;
// ๋จ์ผ ํ์ผ์ ์ ์ก์ํ ์ฑ๊ณต์ ํ์ธํ๊ธฐ ์ํด isFinsh = true์ฒ๋ฆฌ
fileItem.isFinsh = true;
}, 2000);
break;
default:
break;
}
},
(err)=>{
console.error(`${fileItem.id} : file upload error `, err);
setTimeout(() => {
// ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌ๋ ํ์ผ๋ค์ ๊ฐ์๋ฅผ ํ์
ํ๊ธฐ ์ํด errorCount++
this.errorCount++;
// ๋จ์ผ ํ์ผ์ ์ ์ก์ํ ์๋ฌ๋ฅผ ํ์ธํ๊ธฐ ์ํด isError = true์ฒ๋ฆฌ
fileItem.isError = true;
}, 2000);
},
()=>{
console.log(`${fileItem.id} : finally`);
// ์ต์ข
์ ์ผ๋ก ์๋ฌcount + ์ฑ๊ณตcount๊ฐ ์ ์ฒด ํ์ผ ๊ฐ์์ ๊ฐ์ผ๋ฉด ์ ์ฒด ์ ์ก ์๋ฃ
if(this.fileList.length === (this.finshCount + this.errorCount)){
this.isFinsh = true;
}
}
)
})
}
๐ฆง ์ต์ข ํ๋ฉด ํ์ธ
๐์ ์ก์
๐์ ์ก์ค
๐์ ์ก์๋ฃ
๐ฃํ๊ธฐ
๊ฐ์ธ์ ์ผ๋ก rxjs์ effects๋ถ๋ถ์ ํ์ฉํ์ง ๋ชปํ์๋ค. ์ด์ ๋ effects์์ store์ ๋ฐ์ดํฐ๋ฅผ ๋ฐฐ์ด๋ก ๊ฐ์ ธ์์ effects์์ rxjs๋ฅผ ํ์ฉํ์ฌ api ํธ์ถํ๋ ๋ถ๋ถ์ ํด๊ฒฐํ์ง ๋ชปํ์๋ค.
๊ทธ๋ฆฌํ์ฌ ๋ณด์ํ ์ ์ ngrx์ effects๋ถ๋ถ์ ํ์ฉํ์ฌ ํ์ผ์ ์ก์ ํ๋ ๊ฒ์ด๋ค.
'Angular' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
๐ Angular ng-content (0) | 2022.10.26 |
---|---|
Ngrx ๋์ (0) | 2022.06.16 |
Rxjs? (0) | 2022.02.19 |
๐ฅทAngular์ ๋ค๊ตญ์ด ๊ด๋ฆฌ (0) | 2021.11.07 |
๋๊ธ