Who is subscribing to loadCollection$ effect in this #ngrx example? - ngrx

I don't understand who is subscribing to the effect loadCollection$ and how is this observable started in this #ngrx example. Can someone explain?
#Effect()
loadCollection$: Observable<Action> = this.actions$
.ofType(collection.ActionTypes.LOAD)
.startWith(new collection.LoadAction())
.switchMap(() =>
this.db.query('books')
.toArray()
.map((books: Book[]) => new collection.LoadSuccessAction(books))
.catch(error => of(new collection.LoadFailAction(error)))
);

It's a self starting observable because of
ofType(collection.ActionTypes.LOAD)
The #ngrx effects framework subscribe to the loadCollection$.

Related

Problems using #ngrx/effects and firebase together. Success action is triggered instantly

I'm using Firebase with AngularFire and ngrx/effects (latest versions of everything).
Look at the following effect:
createGroupRequest$ = createEffect(() =>
this.actions$.pipe(
ofType(CreateGroupActions.createGroupRequest),
switchMap(action =>
of(this.afs.collection<Group>('groups').add(action.group))
.pipe(
map(() => CreateGroupActions.createGroupSuccess()), // This is fired instantly, NOT when the request is done
catchError(error => of(CreateGroupActions.createGroupFailure(error)))
))
)
);
constructor(
private actions$: Actions,
private store: Store<AppState>,
private afs: AngularFirestore,
) {
}
My main problem is that my success action is triggered instantly, and NOT when the add() request is done. I'm pretty sure it's because the collection.add() function of firebase returns a promise and not an observable.
I tried wrapping it with of() to make it an Observable but it didn't help. Any ideas?
I've solved it.
Changing of() to from() made it work.
When I read up on those operators in the official docs I noticed that of() doesn't actually turn the argument into an Observable, while from() does exactly that.

Optimistic update strategy using NGRX Effects?

We are integrating the NGRX library in a project at the company where we work and we want to perform optimistic updates at the front-end instead waiting for the server response to perform some action. What we have tried is to use the startWith operator, but it throws the Action properly and then, as the releaseService.deleteRelease method does not return an action, it throws the invalid action: null error.
We have tried to add the {dispatch: false} config to the #Effect, but then the first action is not thrown...
We have also though about using a tap oeprator and dispatch some action directly to the store, but we consider it an anti pattern.
So, is it possible to achieve this without creating an splitter intermediate action? Thanks.
#Effect()
deleteRelease$ = this.actions$.pipe(
ofType(ReleaseCardActions.ReleaseCardActionTypes.DeleteRelease),
exhaustMap((action: ReleaseCardActions.DeleteRelease) => {
return this.releaseService.deleteRelease(action.id).pipe(
startWith(new DeleteReleaseSuccess(action.id)),
catchError(() => of(new ReleasesApiActions.DeleteReleaseFailure()))
);
}),
);
Maybe I don't understand the question, but why not perform the optimistic update on the DeleteRelease action directly in the reducer, so your reducer and effect will fire on the same action independently.
Then, you can do the "real" update from the response coming from the effet.
#Effect()
deleteRelease$ = this.actions$.pipe(
ofType(ReleaseCardActions.ReleaseCardActionTypes.DeleteRelease),
exhaustMap((action: ReleaseCardActions.DeleteRelease) => {
return this.releaseService.deleteRelease(action.id).pipe(
map(new DeleteReleaseSuccess(action.id)),
catchError(() => of(new ReleasesApiActions.DeleteReleaseFailure()))
);
}),
);

During a forkJoin, how can I dispatch an action for each request as well as when they all complete?

I'm using ngrx in an Angular project. In this example I have an array of requests. I want to dispatch an action after each request but also after all are done.
So far I have something looking like this:
Observable.forkJoin(requests).pipe(
map(() => new actions.requestsSuccessful()),
catchError(() => of(new actions.requestsFailed()))
);
where requests is an array of Observables.
The code above works fine, when all requests are done, my requestsSuccessful() action is correctly dispatched.
However, I'm implementing a progressbar, which I want to update after each request has been made, but I also want to keep the dispatch of the action where all requests have been made.
I can't figure out how to dispatch an action after each request while keeping the action when everything is done.
Any ideas?
forkJoin emits only when all Observables complete so it's not useful here. Instead, you can use concatAll and concat.
This is model example simulating what you want if I understand you correctly.
const makeRequest = (v) => of(v)
.pipe(
delay(1000), // Simulate delay
map(response => ({ action: 'WHATEVER', response })), // Map response into action
);
const requests = [makeRequest(1), makeRequest(2), makeRequest(3)];
from(requests)
.pipe(
concatAll(), // Execute Observables in order one at the time
concat(of({ action: 'ALL_DONE' })), // Append this when all source Observables complete
)
.subscribe(console.log);
See live demo (open console): https://stackblitz.com/edit/rxjs6-demo-zyhuag?file=index.ts
This demo will print the following output:
{action: "WHATEVER", response: 1}
{action: "WHATEVER", response: 2}
{action: "WHATEVER", response: 3}
{action: "ALL_DONE"}
Btw, in future RxJS versions there will be endWith operator that you can use instead of concat that makes it more readable. https://github.com/ReactiveX/rxjs/pull/3679
Haven't tested it. Maybe this works.
let progress=0
Observable.forkJoin(requests.map(e=>e.do(()=>progress++)).pipe(
map(() => new actions.requestsSuccessful()),
catchError(() => of(new actions.requestsFailed()))
);

ngrx effect not being called when action is dispatched from component

I am having an issue with the ngrx store not dispatching an action to the effect supposed to deal with it.
Here is the component that tries to dispatch:
signin() {
this.formStatus.submitted = true;
if (this.formStatus.form.valid) {
this.store.dispatch(new StandardSigninAction(this.formStatus.form.value.credentials));
}
}
The actions:
export const ActionTypes = {
STANDARD_SIGNIN: type('[Session] Standard Signin'),
LOAD_PERSONAL_INFO: type('[Session] Load Personal Info'),
LOAD_USER_ACCOUNT: type('[Session] Load User Account'),
RELOAD_PERSONAL_INFO: type('[Session] Reload Personal Info'),
CLEAR_USER_ACCOUNT: type('[Session] Clear User Account')
};
export class StandardSigninAction implements Action {
type = ActionTypes.STANDARD_SIGNIN;
constructor(public payload: Credentials) {
}
}
...
export type Actions
= StandardSigninAction
| LoadPersonalInfoAction
| ClearUserAccountAction
| ReloadPersonalInfoAction
| LoadUserAccountAction;
The effect:
#Effect()
standardSignin$: Observable<Action> = this.actions$
.ofType(session.ActionTypes.STANDARD_SIGNIN)
.map((action: StandardSigninAction) => action.payload)
.switchMap((credentials: Credentials) =>
this.sessionSigninService.signin(credentials)
.map(sessionToken => {
return new LoadPersonalInfoAction(sessionToken);
})
);
I can see in debug that the component does call the dispatch method. I can also confirm that StandardSigninAction is indeed instantiated because the breakpoint in the constructor is hit.
But the standardSignin$ effect is not called...
What can possibly cause an effect not being called?
How can I debug what is going on within the store?
Can someone please help?
P.S. I do run the above effect as follows in my imports:
EffectsModule.run(SessionEffects),
edit: Here is my SessionSigninService.signin method (does return an Observable)
signin(credentials: Credentials) {
const headers = new Headers({'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'});
const options = new RequestOptions({headers: headers});
const body = 'username=' + credentials.username + '&password=' + credentials.password;
return this.http.post(this.urls.AUTHENTICATION.SIGNIN, body, options).map(res => res.headers.get('x-auth-token'));
}
This is not going to be a definitive answer, but, hopefully, it will be helpful.
Before you start:
Make sure you are using the latest versions of the #ngrx packages (that are appropriate for the version of Angular you are using).
If you've updated any packages, make sure you re-start your development environment (that is, restart the bundler, the server, etc.)
If you've not done so already, you should have a look at the implementation of the Store - so that you make some educated guesses as to what could be going wrong. Note is that the Store is pretty light. It's both an observable (using the state as its source) and an observer (that defers to the dispatcher).
If you look at store.dispatch you'll see that it's an alias for
store.next, which calls next on the Dispatcher.
So calling:
this.store.dispatch(new StandardSigninAction(this.formStatus.form.value.credentials));
should just see an action emitted from the dispatcher.
The Actions observable that's injected into your effects is also pretty light. It's just an observable that uses the Dispatcher as its source.
To look at the actions that are flowing through the effect, you could replace this:
#Effect()
standardSignin$: Observable<Action> = this.actions$
.ofType(session.ActionTypes.STANDARD_SIGNIN)
with this:
#Effect()
standardSignin$: Observable<Action> = this.actions$
.do((action) => console.log(`Received ${action.type}`))
.filter((action) => action.type === session.ActionTypes.STANDARD_SIGNIN)
ofType is not an operator; it's a method, so to add do-based logging, it needs to be replaced with a filter.
With the logging in place, if you are receiving the action, there is something wrong with the effect's implementation (or maybe the action types' strings/constants aren't what you think they are and something is mismatched).
If the effect is not receiving the dispatched action, the most likely explanation would be that the store through which you are dispatching the StandardSigninAction is not that same store that your effect is using - that is, you have a DI problem.
If that is the case, you should look at what differs from the other SessionEffects that you say are working. (At least you have something working, which is a good place to start experimenting.) Are they dispatched from a different module? Is the module that dispatches StandardSigninAction a feature module?
What happens if you hack one of the working SessionEffects to replace its dispatched action with StandardSigninAction? Does the effect then run?
Note that the questions at the end of this answer aren't questions that I want answered; they are questions that you should be asking yourself and investigating.
Your store's stream may be stopping because of either unhandled errors or - perhaps more confusingly - errors that seem 'handled' using .catch that actually kill the stream without re-emitting a new Observable to keep things going.
For example, this will kill the stream:
this.actions$
.ofType('FETCH')
.map(a => a.payload)
.switchMap(query => this.apiService.fetch$(query)
.map(result => ({ type: 'SUCCESS', payload: result }))
.catch(err => console.log(`oops: ${err}`))) // <- breaks stream!
But this will keep things alive:
this.actions$
.ofType('FETCH')
.map(a => a.payload)
.switchMap(query => this.apiService.fetch$(query)
.map(result => ({ type: 'SUCCESS', payload: result }))
.catch(e => Observable.of({ type: 'FAIL', payload: e}))) // re-emit
This is true for any rxjs Observable btw, which is especially important to consider when broadcasting to multiple observers (like ngrx store does internally using an internal Subject).
I am using a later version of ngrx (7.4.0), so cartant's suggestion of:
.do((action) => console.log(`Received ${action.type}`))
should be...
... = this.actions.pipe(
tap((action) => console.log(`Received ${action.type}`)),
...
And in the end I discovered I had missed adding my new effects export to module, like:
EffectsModule.forRoot([AuthEffects, ViewEffects]), // was missing the ', ViewEffects'
If you are using version 8, ensure you wrap each action with createEffect.
Example:
Create$ = createEffect(() => this.actions$.pipe(...))
Another possible reason is that if you used ng generate to create the module where you imported the Effects make sure it is imported in the App Module as the following command 'ng generate module myModule' will not add it to the app module.

Angular2 timeout in http post request

Is it possible to make a timeout of 3 seconds in a post request ? How ?
My code for the moment
this.http.post('myUrl',
MyData, {headers: Myheaders})
.map(res => res.json())
.subscribe(
data => this.ret = data,
error => console.debug('ERROR', error),
() => console.log('END')
);
You can use the timeout operator like this:
this.http.post('myUrl',
MyData, {headers: Myheaders})
.timeout(3000, new Error('timeout exceeded'))
.map(res => res.json())
.subscribe(
data => this.ret = data,
error => console.debug('ERROR', error),
() => console.log('END')
);
You might need to import timeout like so
import 'rxjs/add/operator/timeout'
I couldn't get it to work without that on rxjs 5.0.1 and angular 2.3.1
Edit 20 April 2018
please refrain from importing operators that way, in rxjs +5.5 you can import operators like this one with
import { timeout } from 'rxjs/operators/timeout';
// Use it like so
stream$.pipe(timeout(3000))
Please checkout this document which explains pipeable operators in great depth.
I modified the answer of Thierry to get it working with the latest release. It is necessary to remove the second parameter of the timeout function. According to a discussion regarding an angular-cli issue, the timeout function always throws a TimeoutError now.
this.http.post('myUrl',
MyData, {headers: Myheaders})
.timeout(3000)
.map(res => res.json())
.subscribe(
data => this.ret = data,
error => console.debug('ERROR', error),
() => console.log('END')
);

Resources