-3

I am working off of an existing NgRx/Redux app and new to it (1st app). I have used a previous answer on SO and having trouble adding it to NgRx effects.

I have the following HTML/Angular in my component:

<div class="block">
  <label id="lbl">File </label>
  <input #fileInput type='file' (change)="_onChange($event)"/>
  <button class="btn btn-sm"  (click)="_uploadFile()">Upload The JSON File</button>
</div>

The TS file for the component has:

@Output() uploadFile = new EventEmitter();
    _uploadFile() {
    this.uploadFile.emit();
}

The module TS file:

    uploadFile() {
        this.store.dispatch(new PageActions.UploadFile());
    }

The action file:

export class UploadFile implements Action {
    readonly type = PageActionTypes.UploadFile;
    constructor(public payload: File) { }
}

The effects file:

@Effect()
uploadFile$: Observable<Action> = this.actions$.pipe(
    ofType(PACtions.PageActionTypes.UploadFile),
    map( action => action.payload ),
    // ...
);

From a previous SO answer I see this:

export class AppComponent  {
  uploadedData: any;

  onChange(event) {
    const reader = new FileReader();
    reader.onload = e => {
      const raw = (<FileReader>e.target).result as string;
      const res = JSON.parse(raw);
      this.uploadedData = res;
    }
    reader.readAsText(event.target.files[0]);
  }

  uploadFile() {
    console.log(this.uploadedData);
    // this.store.dispatch(new PageActions.UploadFile(this.uploadedData));
  }

}

How do I get the onChange($event) and uploadedData to fit into my components, effects, and actions files? My component only has @Output ... stuff and the work happens in the effects file.

Update

I have a downloadStateFile like so.

In the component.ts file:

@Output() downloadStateFile = new EventEmitter();
    _downloadStateFile() {
        this.downloadStateFile.emit();
    }

This works fine and is passed to the effects file to do the work:

@Effect({ dispatch: false })
    downloadStateFile$ = this.actions$.pipe(
       ofType(TheActionTypes.DownloadState),
       withLatestFrom(this.store.select(fromSpace.getTheState)),
       tap(([empty, wspace]) => {
            console.log(JSON.stringify(wspace));

            // This code below was suggested from the following link:
            // https://stackoverflow.com/questions/42360665/angular2-to-export-download-json-file
            var jsonState = JSON.stringify(wspace);
            const clickElement = document.createElement('a');
            clickElement.setAttribute('href', "data:text/json;charset=UTF-8," + encodeURIComponent(jsonState));
            clickElement.setAttribute('download', "state-file.json");
            clickElement.style.display = 'none';
            document.body.appendChild(clickElement);
            clickElement.click(); // simulate click
            document.body.removeChild(clickElement);
       })
   );

I need the uploadState to do something similar but to take the state in the file and upload the state to the reducer. But first it needs to do all the FileReader in the effect as per the project.

Update #2

I only have one component as the FileUploader component created in your mock doesn't exist in mine so I only have:

<div class="block">
  <label id="lbl">File </label>
  <input #fileInput type='file' (change)="_onChange($event)"/>
  <button class="btn btn-sm"  (click)="_uploadFile()">Upload The JSON File</button>
</div>

I have this in that .ts file of the html above based on what you provided:

@Output() onChange = new EventEmitter();
    _onChange(event) {
        this.onChange.emit(event.target.files[0]);
    }

In your code there is a second layer that is in readFile that passes in the inputFile payload. How do I add that to my above update? Thanks for your help.

4
  • can you post your reducer file? Commented Sep 4, 2019 at 2:56
  • How do I get to the reducer is the problem? Once I have the actions back from the effect I will send it to the reducer Commented Sep 4, 2019 at 4:46
  • (if I understand your question well) You need to use exhaustMap after your effect. then dispatch new action based on success or failure or anything else you want to do after file is uploaded. Handle new action in new effect with dispatch false. Commented Sep 11, 2019 at 9:06
  • @m.akari do you have an example of this? thanks. Commented Sep 11, 2019 at 21:42

1 Answer 1

0

Below is one way to organize the code fulfilling the above requirement.

  • Separating file-upload capability to an independent component. Essentially FileUploader component would be a dumb component which only communicates via EventEmitter.
  • Parent component hooks into upload event and dispatches appropriate action passing the data received from FileUploader as a payload.
  • store reducer sets appropriate state based on business logic. Simultaneously, effect can be hooked into the same (which simulates an 'API call')

Check out this mock implementation for the same.

EDIT

Based on the new requirements to use FileReader in effects, mock implementation has been modified to accommodate this change.

EDIT 2

Based on the new requirements to use not use store and simply utilize the effects, another Injectable can be introduced which is backed by Subject which then provides a way to communicate across component and effects. Sample code is updated in mock implementation.

Sign up to request clarification or add additional context in comments.

9 Comments

I need it not to be just angular but NgRx! It needs to have the side effects and reducer. The mock you provided doesn't.
It has everything. app.state.ts has the reducer, app.effects.ts has the effects and app.component.ts has the NGRX bindings
For the sake of this existing project, I want/need to have the ofChange, uploadData(?), and uploadFile in the effect. Can that be done? That is what I am looking for. So the FileReader I want in the effect. This is the way the project implemented things (not me). Actions just take in inputs and pass outputs.
See my downloadState updates above on how we need the pattern done with this project.
Thanks. getting closer. I uplated my question above with another detail about the inputFile payload. Learning a lot. Thanks.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.