angular @ngrx - redux 我应该在哪里处理不改变状态的导出操作
angular @ngrx - redux where should i handle actions like export that don't change the state
我有一个带有 "Export" 按钮的应用程序。点击后,APP从服务器获取一个大文本文件,并
使用 HTML5 blob API 到 "save as" 以便用户可以将其保存到他的磁盘
为了在dispatch export action中开始导出操作
this.store.dispatch(new scanListsActions.Export(id));
此操作随后由 "effects" 代码处理,该代码转到后端服务器以获取文件。
完成后,将调度 ExportSuccess 操作。
现在,我不想将文件内容存储在商店中,因为它非常大,
另一方面,我需要视图中的数据才能将其发送给用户。
目前你可以看到我在 reducer 代码(下面)中将它发送给了用户。
通常是如何完成的?将文件发送给用户的地方在哪里?
import * as fileSaver from 'file-saver'; // npm i --save file-saver
export function reducer(state: ScanListsState = initialState, action: ScanListsActions): ScanListsState {
switch (action.type) {
case ScanListsActionTypes.Export:
return {
...state,
loading: true
};
case ScanListsActionTypes.ExportSuccess:
// these 2 lines below send the file to the user (starts a download)
// where should i put this code. I don't think the reducer is the place for it
const blob = new Blob([action.payload.data], { type: 'text/plain; charset=utf-8' });
fileSaver.saveAs(blob, 'destination_list.txt');
return {
...state,
loading: false,
error: null
};
case ScanListsActionTypes.GeneralFail:
return {
...state,
loading: false,
error: action.payload
};
default:
return state;
}
}
效果代码
@Injectable()
export class ScanListsEffects {
constructor(private scanListService: ScanListService,
private actions$: Actions) { }
@Effect()
exportScanList$: Observable<Action> = this.actions$.pipe(
ofType(scanListsActions.ScanListsActionTypes.Export),
mergeMap((action: scanListsActions.Export) =>
this.scanListService.getScanList(action.payload).pipe(
map(sList => (new scanListsActions.ExportSuccess(sList))),
catchError(err => of(new scanListsActions.GeneralFail(err)))
)
)
);
}
你是对的,你不应该在减速器中这样做(至少我不会)。您应该在组件本身中执行此操作。
你应该以 url 或任何键的形式将 action.payload.data
存储在 reducer 中并对其做出反应。
像这样:
您的减速器状态应如下所示:
export interface ScanListsState {
url: string; // (I assume string),
loading: boolean;
error: any; // You can put whatever interface you want
}
除了 ScanListsActionTypes.ExportSuccess:
return:
之外,你的 reducer 中的其他一切都很好
return {
...state,
url: action.payload.data,
loading: false,
error: null,
};
然后在你的组件中
ngOnInit() {
this.reactToURLChangesOfExport();
}
exportClicked() {
this.store.dispatch(new scanListsActions.Export(id));
}
reactToURLChangesOfExport() {
this.store.pipe(
select(YourSelectorForTheSliceOfTheURL), // make a selector that will react to changes to the URL property of the slice of data in the store.
// put a takeUntil or however you want to destroy this subscription so it prevents memory leaks
).subscribe(url => {
const blob = new Blob([url], { type: 'text/plain; charset=utf-8' });
fileSaver.saveAs(blob, 'destination_list.txt');
})
}
您应该拨打服务电话以进行保存,就像您为阅读所做的那样。实际上,您会调用该服务:
类似于:
@Effect()
fileSaveExportScanList$: Observable<Action> = this.actions$.pipe(
ofType(scanListsActions.ScanListsActionTypes.ExportSuccess),
mergeMap((action: scanListsActions.ExportSuccess) =>
this.scanListService.saveScanList(action.payload).pipe(
map (() => (new scanListsActions.SaveSuccess())),
catchError(err => of(new scanListsActions.GeneralFail(err)))
)
)
);
然后拨打服务电话'saveScanList'
saveScanList(data: any){
const blob = new Blob([data], { type: 'text/plain; charset=utf-8' });
fileSaver.saveAs(list, 'destination_list.txt');
我有一个带有 "Export" 按钮的应用程序。点击后,APP从服务器获取一个大文本文件,并 使用 HTML5 blob API 到 "save as" 以便用户可以将其保存到他的磁盘
为了在dispatch export action中开始导出操作
this.store.dispatch(new scanListsActions.Export(id));
此操作随后由 "effects" 代码处理,该代码转到后端服务器以获取文件。 完成后,将调度 ExportSuccess 操作。
现在,我不想将文件内容存储在商店中,因为它非常大, 另一方面,我需要视图中的数据才能将其发送给用户。 目前你可以看到我在 reducer 代码(下面)中将它发送给了用户。
通常是如何完成的?将文件发送给用户的地方在哪里?
import * as fileSaver from 'file-saver'; // npm i --save file-saver
export function reducer(state: ScanListsState = initialState, action: ScanListsActions): ScanListsState {
switch (action.type) {
case ScanListsActionTypes.Export:
return {
...state,
loading: true
};
case ScanListsActionTypes.ExportSuccess:
// these 2 lines below send the file to the user (starts a download)
// where should i put this code. I don't think the reducer is the place for it
const blob = new Blob([action.payload.data], { type: 'text/plain; charset=utf-8' });
fileSaver.saveAs(blob, 'destination_list.txt');
return {
...state,
loading: false,
error: null
};
case ScanListsActionTypes.GeneralFail:
return {
...state,
loading: false,
error: action.payload
};
default:
return state;
}
}
效果代码
@Injectable()
export class ScanListsEffects {
constructor(private scanListService: ScanListService,
private actions$: Actions) { }
@Effect()
exportScanList$: Observable<Action> = this.actions$.pipe(
ofType(scanListsActions.ScanListsActionTypes.Export),
mergeMap((action: scanListsActions.Export) =>
this.scanListService.getScanList(action.payload).pipe(
map(sList => (new scanListsActions.ExportSuccess(sList))),
catchError(err => of(new scanListsActions.GeneralFail(err)))
)
)
);
}
你是对的,你不应该在减速器中这样做(至少我不会)。您应该在组件本身中执行此操作。
你应该以 url 或任何键的形式将 action.payload.data
存储在 reducer 中并对其做出反应。
像这样:
您的减速器状态应如下所示:
export interface ScanListsState {
url: string; // (I assume string),
loading: boolean;
error: any; // You can put whatever interface you want
}
除了 ScanListsActionTypes.ExportSuccess:
return:
return {
...state,
url: action.payload.data,
loading: false,
error: null,
};
然后在你的组件中
ngOnInit() {
this.reactToURLChangesOfExport();
}
exportClicked() {
this.store.dispatch(new scanListsActions.Export(id));
}
reactToURLChangesOfExport() {
this.store.pipe(
select(YourSelectorForTheSliceOfTheURL), // make a selector that will react to changes to the URL property of the slice of data in the store.
// put a takeUntil or however you want to destroy this subscription so it prevents memory leaks
).subscribe(url => {
const blob = new Blob([url], { type: 'text/plain; charset=utf-8' });
fileSaver.saveAs(blob, 'destination_list.txt');
})
}
您应该拨打服务电话以进行保存,就像您为阅读所做的那样。实际上,您会调用该服务:
类似于:
@Effect()
fileSaveExportScanList$: Observable<Action> = this.actions$.pipe(
ofType(scanListsActions.ScanListsActionTypes.ExportSuccess),
mergeMap((action: scanListsActions.ExportSuccess) =>
this.scanListService.saveScanList(action.payload).pipe(
map (() => (new scanListsActions.SaveSuccess())),
catchError(err => of(new scanListsActions.GeneralFail(err)))
)
)
);
然后拨打服务电话'saveScanList'
saveScanList(data: any){
const blob = new Blob([data], { type: 'text/plain; charset=utf-8' });
fileSaver.saveAs(list, 'destination_list.txt');