angular @ngrx - избыточность, где я должен обрабатывать такие действия, как экспорт, которые не меняют состояние - PullRequest
0 голосов
/ 09 января 2020

У меня есть приложение с кнопкой «Экспорт». При нажатии приложение получает с сервера большой текстовый файл и использует API-интерфейс BLOB-объекта HTML5 для сохранения как, чтобы пользователь мог сохранить его на свой диск

, чтобы начать экспортную операцию отправки действие экспорта

this.store.dispatch(new scanListsActions.Export(id));

это действие затем обрабатывается кодом «эффектов», который отправляется на внутренний сервер для получения файла. по завершении отправляется действие ExportSuccess.

Теперь я не хочу хранить содержимое файла в хранилище, так как оно очень большое, с другой стороны, мне нужны данные в представлении для отправки это пользователю. в настоящее время вы можете видеть, что я отправляю его пользователю в коде редуктора (ниже).

Как обычно это делается? где находится место для отправки файла пользователю?

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)))
            )
        )
    );
}

Ответы [ 2 ]

1 голос
/ 10 января 2020

Вы должны сделать сервисный звонок для сохранения, так же, как вы сделали для чтения. В результате вы вызываете сервис:

Что-то вроде:

    @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');
1 голос
/ 09 января 2020

Вы правы, вы не должны делать это в редукторе (по крайней мере, я бы не стал). Вы должны делать это в самом компоненте.

Вы должны сохранить action.payload.data в редукторе в терминах url или любого другого ключа и реагировать на него.

Примерно так:

Ваше состояние редуктора должно выглядеть следующим образом:

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');
  })
}
...