Using NGRX in a Route Resolver - rxjs

I am using Angular 6.
I am also using NGRX Store.
I am using a route guard to make sure the user is logged in to the application.
I then use a resolver to get the initial user profile, then place it in the NGRX store.
I am new to NGRX and I am not sure if this is the correct way to write the resolver.
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): any {
return this.loginService.getLoginData()
.pipe(
map((result:UserData) => {
this.store.dispatch(new userActions.SetLoginData(result));
this.loginService.getDropdownData(
result.userId,
result.countryCode,
).subscribe( data => {
this.store.dispatch(new userActions.SetDropdownData(data));
})
})
)
}
I am also not sure if this is the correct way to do the RXJS.
any suggestions,
Thanks

I'm going to point you to Preloading ngrx/store with Route Guards, an article of Todd Motto which explains it really well.
There is also an example of the guard in the NgRx example app
#Injectable()
export class CoursesGuard implements CanActivate {
constructor(private store: Store<CoursesState>) {}
getFromStoreOrAPI(): Observable<any> {
return this.store
.select(getCoursesState)
.do((data: any) => {
if (!data.courses.length) {
this.store.dispatch(new Courses.CoursesGet());
}
})
.filter((data: any) => data.courses.length)
.take(1);
}
canActivate(): Observable<boolean> {
return this.getFromStoreOrAPI()
.switchMap(() => of(true))
.catch(() => of(false));
}
}

First of all I think it would make sense to seperate the authentication check and the data resolution into separate classes. For authentication it makes more sense to use a CanActivate guard. See: https://angular.io/api/router/CanActivate
With this out of the way your resolver can focus on only getting the data that is actually required. Here you need to be aware that if you return an observable in your resolver, the observable needs to complete in order for the resolver to complete. The problem is that if you select something from the store, the resulting observable never completes, hence your resolver will never finish resolving your data. You can work around this fact by using the first() or take(1) operator. timdeschryvers answer has a nice example on how to accomplish that.

Related

How to prevent re-rendering with useSubscription()?

Many of you might have heard of GraphQL. It provides QUERY and MUTATION. Also it supports SUBSCRIPTION as 100% replacement of web socket. I'm a big fan of GraphQL and hooks. Today I faced an issue with useSubscription which is a hook version of SUBSCRIPTION. When this subscription is fired, a React component is re-rendered. I'm not sure why it causes re-rendering.
import React from 'react'
import { useSubscription } from 'react-apollo'
const Dashboard = () => {
...
useSubscription(query, {
onSubscriptionData: data => {
...
}
})
render (
<>
Dashboard
</>
)
}
Actually useSubscription's API doc doesn't say anything about this re-rendering now. It would be really appreciated if you provide me a good solution to preventing the re-rendering.
Thanks!
Just put your subscription in separate component as this guy did, and return null, now your root component won't rerender
function Subscription () {
const onSubscriptionData = React.useCallback(
handleSubscription, // your handler function
[])
useSubscription(CHAT_MESSAGE_SUBSCRIPTION, {
shouldResubscribe: true,
onSubscriptionData
})
return null
}
// then use this component
<Subscription />
In my experience, there is no way to prevent re-render when receiving new data in onSubscriptionData. If your global data is used for calculations, you should use useMemo for you global variable. On the other hand, you should consider do you need put your variable in onSubscriptionData? Are there other ways? Did you use useSubscription in right component? If your have to do that, you have to accept extra rendering.
Hopefully, my answer is helpful for your situation.

NGXS State documentation

I'm new to NGXS and I'm trying to fully understand the docs so I can start using it knowing what I'm doing.
There is one thing I don't understand in this code snippet from here.
export class ZooState {
constructor(private animalService: AnimalService) {}
#Action(FeedAnimals)
feedAnimals(ctx: StateContext<ZooStateModel>, action: FeedAnimals) {
return this.animalService.feed(action.animalsToFeed).pipe(tap((animalsToFeedResult) => {
const state = ctx.getState();
ctx.setState({
...state,
feedAnimals: [
...state.feedAnimals,
animalsToFeedResult,
]
});
}));
}
}
Just below this code, it says:
You might notice I returned the Observable and just did a tap. If we
return the Observable, the framework will automatically subscribe to
it for us, so we don't have to deal with that ourselves. Additionally,
if we want the stores dispatch function to be able to complete only
once the operation is completed, we need to return that so it knows
that.
The framework will subscribe to this.animalService.feed, but why?
The action, FeedAnimals, uses the injected service, AnimalService to feed the animals passed in the action's payload. Presumably the service is operates asynchronously and returns an Observable. The value of that Observable is accessed via the tap function and is used to update the ZooState state context based on completing successfully.
In order to use NGXS specifically and Angular in general, you really have to understand RxJS... here's my goto doc page for it

ReduxObservable cancellation based on action type and its data

I have React app which uses redux-observable with typescript. In this scenario, FetchAttribute Action gets triggered with a id and then make an ajax call.
In certain case, I would want to cancel the ajax request if "FETCH_ATTRIBUTE_CANCEL" action was triggered with the same id as of "FetchAttributeAction" action.
action$.ofType(FETCH_ATTRIBUTE)
.switchMap((request: FetchAttributeAction) => {
return ajax.getJSON(`/api/fetch-attribute?id=${request.id}`)
.flatMap((fetchUrl) => {
// return new action
})
.takeUntil(action$.ofType(FETCH_ATTRIBUTE_CANCEL));
});
interface FetchAttributeAction{
id: number;
}
Problem:
How do we cancel the execution based on action type + action data?
In my case, it would FETCH_ATTRIBUTE_CANCEL and id.
The key is to filter actions in the takeUntil notifier to only those which match the ID you care about.
action$.ofType(FETCH_ATTRIBUTE_CANCEL).filter(action => action.id === request.id)
So here's what it might look like:
Demo: https://stackblitz.com/edit/redux-observable-playground-xztkoo?file=fetchAttribute.js
const fetchAttributeEpic = action$ =>
action$.ofType(FETCH_ATTRIBUTE)
.mergeMap(request =>
ajax.getJSON(`/api/fetch-attribute?id=${request.id}`)
.map(response => fetchAttributeFulfilled(response))
.takeUntil(
action$.ofType(FETCH_ATTRIBUTE_CANCEL).filter(action => action.id === request.id)
)
);
You can also take a look at previous questions:
Redux Observable: If the same action is dispatched multiple times, how do I cancel one of them?
Independent chain cancellation in redux-observable?
Dispatch an action in response to cancellation
The OP also pointed out that they were using switchMap (as did I originally when I copied their code) which would have meant that the epic only ever had one getJSON at a time since switchMap will unsubscribe from previous inner Observables. So that also needed to be chained. Good catch!
I think you should be able to make takeUntil selective for a certain action id with pluck and filter.
ex:
.takeUntil(action%.ofType(FETCH_ATTRIBUTE_CANCEL)
.pluck('id')
.filter((cancelActionID) => cancelActionID === fetchID))
The non-obvious part to me is how to get the current fetchID to run that comparison. I might consider try using do to store in a temporary variable

Wakanda how to use server method in mobile side?

To bind my data on the mobile side it works like this :
getHeros() {
this.wakanda.getCatalog().then(ds => {
ds['Superhero'].query({orderBy:"ID desc",pageSize:3}).then(collection => {
this.favoriteSuperheroes = collection.entities;
});
});
}
But like this I work directly on the table. I have a method who provide me everything I want on the server side. The Wakand's documentation tells me to do it like this:
ds.Company.myDataClassMethod().then(function (result) {
});
It won't work, I can't call my method, someone can help me ?
If the query() on the table works for you while calling dataclasss method does not. A possible cause is that your class method is not set to available to the public.
Please check whether the scope of the class method is set to "public".
And test if your method can be accessed directly via REST:
127.0.0.1:8081/rest/Company/myDataClassMethod
Update: add type "any" to ds solves the problem. It appears
this.wakanda.getCatalog().then(ds:any => {
ds['Company'].myDataClassMethod().then(result => {
//Work with Result Here
});
});

Dispatch an action in response to cancellation

I started with the cancellation recipe from the redux-observable docs and want to extend it a bit.
Basically I have a scenario where after the cancellation is triggered, using takeUntil I want dispatch another action to cleanup, etc.
This is what I came up with so far: https://jsbin.com/zemenu/195/edit?js,output
Start a "Fetch User Info" and then hit Cancel. I want it to execute actions in this order:
- USER/FETCH
- REQUEST/STARTED
- USER/CANCELLED
- REQUEST/CANCELLED
This works in the way I have it setup right now. But, I have to rely on passing dispatch into the requestSequence function and then trigger it in finally. Is there a cleaner way to do this just with observable operators? So when that USER.CANCELLED is triggered some final action is mapped to inside the requestSequence observable.
Redux logger is enabled so check the console for all the actions.
Instead of using .takeUntil(), it sounds like you want to use .race(), which is fairly aptly named. Whichever stream emits first, wins! The other is unsubscribed.
You'll need to restructure some things a bit to use it as you want. You want to isolate the first action you emit immediately, your request.onStart(meta), separate from the ajax request Observable.fromPromise(apiCall(...args)). Then you want to race directly between that ajax and the cancellation, so you'd need to pass in the action$ ActionsObservable since you have all this in a helper.
https://jsbin.com/suvaka/edit?js,output
function requestSequence(apiCall, args, meta, action$) {
return Observable.of(request.onStart(meta))
.concat(
Observable.fromPromise(apiCall(...args))
.map((payload) => request.onSuccess(payload, meta))
.catch((e) => Observable.of(request.onError(e, meta)))
.race(
action$.ofType(USER.CANCELLED)
.map(() => request.onCancel(meta))
)
);
}
const fetchUserEpic = (action$, store) =>
action$.ofType(USER.FETCH)
.mergeMap(action =>
requestSequence(
userRequest,
[`/api/users/${action.payload}`],
{ activity: USER.FETCH, path: 'user' },
action$
)
);
Side note: be careful about premature abstractions like making those sorts of helpers. Even though you may repeat things in some epics, I've found abstracting it can make it much harder to grok later, especially if it's someone else who didn't write the code and aren't an Rx guru. Only you can know whether this advice applies to you and your codebase, of course.
The primary confusing point for me is all the arguments you have to pass to requestSequence, which will be tough for many to understand when they first come across it. If you find that very very commonly your epics do exactly the same thing and you want to reuse, perhaps abstracting the entire epic would be more clear, and create API utilities like userRequest below that you can test independently.
(untested, basically pseudo code)
const createApiEpic = options =>
action$ =>
action$.ofType(options.on)
.mergeMap(action =>
Observable.of(request.onStart(meta))
.concat(
options.effect(action.payload)
.map(payload => request.onSuccess(payload, meta))
.catch(e => Observable.of(request.onError(e, meta)))
.race(
action$.ofType(options.cancel)
.map(() => request.onCancel(meta))
)
)
);
const userRequest = id =>
Observable.ajax.getJSON(`/api/users/${id}`);
const fetchUserEpic = createApiEpic({
on: USER.FETCH,
effect: userRequest
cancel: USER.CANCELLED
});

Resources