How do i can use concatLastestFrom? - ngrx

I am try use effects with my store, but 2 days i have this error:
Idk why it happened, can u help me please?
stackblitz simulation
Action:
export const LoadMeetings = createAction('[ Meetings/Api ] Load Meetings');
Effect:
readonly loadMeetings$ = createEffect(() =>
this.actions$.pipe(
ofType(fromMeetingsActions.LoadMeetings),
concatLatestFrom(action => this.store.select(fromMeetings.selectMeetingsState)),
)
);
Selector:
export const selectMeetingsState =
createFeatureSelector<MeetingsStateModel>(meetingsFeatureKey);
//This doesn't work too
const selector = <T>(mapping: (state: MeetingsStateModel) => T) => createSelector(selectMeetingsState, mapping);
export const selectSelectedMeetingId = selector((state) => state.selectedMeetingId);
I try find answer in google, docs, youtube, q&a and any services, but zero feed back..
I try change operator map, concatMap, switchMap, pipe and another..
I try change selectors, but zero result

Effects usually map to an action ie:
readonly loadMeetings$ = createEffect(() =>
this.actions$.pipe(
ofType(fromMeetingsActions.LoadMeetings),
concatLatestFrom(action => this.store.select(fromMeetings.selectMeetingsState)),
map(() => fromMeetings.loadMeetingsSucceeded)
),
);
Stackblitz: https://stackblitz.com/edit/angular-pq8vlw?file=src%2Fmeetings%2F%2Bstate%2Feffects.ts
If they don't return an action you need to add { dispatch: false }:
readonly loadMeetings$ = createEffect(() =>
this.actions$.pipe(
ofType(fromMeetingsActions.LoadMeetings),
concatLatestFrom((action) =>
this.store.select(fromMeetings.selectMeetingsState)
)
),
{ dispatch: false }
);
Stackblitz: https://stackblitz.com/edit/angular-h2yifk?file=src%2Fmeetings%2F%2Bstate%2Feffects.ts

Related

How to get the store state in Effects using withLatestFrom operator

I'm trying with this
payloadToBeSaved$ = createEffect(() => this.actions$
.ofType(SOME_ACTION)
.withLatestFrom(this.store$)
.map(([action: Action, storeState: AppState]) => {
// Do something ...
});
But this.actions$ is an observable and I have to use this.actions$.pipe()....
when I try with pipe() all lines red with errors. Don't know how to fix.
#Effect()
shipOrder = this.actions.pipe(
ofType<ShipOrder>(ActionTypes.ShipOrder),
map(action => action.payload),
concatMap(action =>
of(action).pipe(
withLatestFrom(store.pipe(select(getUserName)))
)
),
map([payload, username] => {
...
})
)
Reference: Start using ngrx/effects for this

I can't display a single entity object with ngrx

Hello I use ngrx and spring boot to display all products. But I can't display one selected product.
My getProductAction et effect work well.
This is the result in Redux:
Some help please
This is my state and initialState:
export interface ProduitState extends EntityState<Produit>{
isLoading: boolean;
selectedProduitId: any;
error: any;
produits: any;
searchQuery: string;
}
enter image description here
export const produitAdapter: EntityAdapter<Produit> = createEntityAdapter<Produit>({
selectId: (produit: Produit) => produit.id,
sortComparer: false,
});
export const produitInitialState: ProduitState = produitAdapter.getInitialState({
isLoading: true,
selectedProduitId: null,
error: null,
produits: [],
searchQuery: ''
});
export const selectedProduitId = (state: ProduitState) => state.selectedProduitId;
export const selectIsLoading = (state: ProduitState) => state.isLoading;
export const selectError = (state: ProduitState) => state.error;
export const selectSearchQuery = (state: ProduitState) => state.searchQuery;
This my action:
export const getProduitAction = createAction('[Produit] Get Produit', props<{produitId: string}>());
const produitPayload = props<{produit: Produit}>();
export const getProduitSuccessAction = createAction('[Produit] Get Produit Success', produitPayload);
This is my reducer:
on(ProduitActions.getProduitAction, (state, {produitId}) => ({
...state, selectedProduitId: undefined, isLoading: true, isLoaded: false, error: null
})),
This is my effect:
getProduit$ = createEffect(() =>
this.actions$.pipe(
ofType(getProduitAction),
exhaustMap(action => {
alert("getProduit Effects "+ action.produitId);
return this.produitService.getProduit(action.produitId)
.pipe(
//tap(res => console.log(res + "TAG EEEEE")),
map((produit, id) => getProduitSuccessAction({produit})),
catchError(err => this.handleError(err))
)
})
)
);
I do this code to get de current selected id:
public produitsState = createFeatureSelector<state.ProduitState>('produit');
private selectors = state.produitAdapter.getSelectors(this.produitsState);
private selectCurrentProduitId = createSelector(this.produitsState, state.selectedProduitId);
private isLoading = createSelector(this.produitsState, state.selectIsLoading);
private error = createSelector(this.produitsState, state.selectError);
private searchQuery = createSelector(this.produitsState, state.selectSearchQuery);
I do this code to get de current selected product but it doesn't work:
getCurrentProductSelected() {
//console.log("ProduitStore: getCurrentProductSelected()");
//console.log(this.store.select(this.selectCurrentProduitId));
alert(this.getProducts());
return combineLatest(
this.getProducts(),
this.store.select(this.selectCurrentProduitId),
(products, selectedId) => selectedId.map(id => {
alert(id +" getCurrentProductSelected");
alert(products +" getCurrentProductSelected");
return products[id];
})
);
}
I try to get the product but I have nothing:
let id = this.route.snapshot.paramMap.get('id');
this.store.dispatchGetProduitAction(id);
this.produit$ = this.store.getCurrentProductSelected();
this.produit$.pipe(
map(produit =>{
alert(produit.nom + " this.produit$");
})
);
Help please. Thanks
It does not look like you set 'selectedProduitId' anywhere. And without that, how would 'getCurrentProductSelected' return anything. If i understand your idea correctly, you need to do something like this:
on(ProduitActions.getProduitAction, (state, {produit}) => ({
...state, selectedProduitId: produit.id
})),
But it's just a blind guess.

get type in withlatestFrom

I have the following selector
export const featureAdapter: EntityAdapter<IRoute> = createEntityAdapter<IRoute>({
selectId: model => model.routeId,
});
export interface State extends EntityState<IRoute> {
selectedRouteId: string;
selectedPointId: string;
}
export const selectAllEntities: (state: object) => Dictionary<IRoute> = featureAdapter.getSelectors(selectRouteState).selectEntities;
export const selectedR: MemoizedSelector<object, string> = createSelector(selectRouteState, getSelectedRoute);
export const selectedRoute: MemoizedSelector<object, IRoute> = createSelector(
selectAllEntities,
selectedR,
(entities, id) => entities[id]
);
that return something of type IRoute
In my effect I use a withlatest from
onAction$ = this.actions$.pipe(
ofType<featureActions.onAction>(featureActions.ActionTypes.onAction),
concatMap(action =>
of(action).pipe(
withLatestFrom(this.store$.pipe(select(RoutesStoreSelectors.getById(), {routeId: action.payload.routeId}))),
)
),
switchMap(([action, route]) => {})
)
inside the
switchMap(([action, route])
the route variable, is of type any. but it should be of type IRoute
how can I make it work correctly ?
I can't see why TypeScript cannot infer the type, but you can always explicitly specify the type.
onAction$ = this.actions$.pipe(
ofType<featureActions.onAction>(featureActions.ActionTypes.onAction),
concatMap(action =>
of(action).pipe(
withLatestFrom(this.store$.pipe(select(RoutesStoreSelectors.getById(), {routeId: action.payload.routeId}))),
)
),
switchMap(([action, route]: [Action, IRoute]) => {})
)
I am not sure why you need get hold of dispatched action in switchMap, but you can do the following to get the route of type IRoute -
onAction$ = this.actions$.pipe(
ofType<featureActions.onAction>(featureActions.ActionTypes.onAction),
switchMap(action =>
combineLatest(of(action), this.store$.pipe(select(RoutesStoreSelectors.getById(), {routeId: action.payload.routeId})))
),
switchMap(([action, route]) => {})
)
Having code like this will ensure to have the action of type of the dispatched action and route is of type IRoute.
I hope it helps.

NGRX - combine selectors with props

How do I combine reducers, when one of them needs props?
I have following model:
interface Device {
id: string;
data: IDeviceData;
}
and DeviceReducer that looks as follow:
import { EntityState, EntityAdapter, createEntityAdapter } from '#ngrx/entity';
import { Device } from '../model/device';
import { SubnetBrowserApiActions } from 'src/app/explorer/actions';
export interface State extends EntityState<Device> { }
export const adapter: EntityAdapter<Device> = createEntityAdapter<Device>();
export const initialState: State = adapter.getInitialState();
export function reducer(
state = initialState,
action:
| SubnetBrowserApiActions.SubnetBrowserApiActionsUnion
): State {
switch (action.type) {
case SubnetBrowserApiActions.SubnetBrowserApiActionTypes.LoadEntriesSucces: {
return adapter.upsertMany(action.payload.entries, state);
}
default: {
return state;
}
}
}
const {
selectAll,
} = adapter.getSelectors();
export const getAllDevices = selectAll;
In my other reducer, when I want to select devices using an array of ids I use this code:
export const getVisibleDrives = createSelector(
[fromRoot.getAllDevices, getVisibleDrivesSerialNumbers],
(devices, visibleDevicesIds) =>
devices.filter((device) => onlineDevicesIds.includes(device.serialNumber)),
);
This code is very repetitive, so I'd like to add add parametrized selector that will return just these drives, that have id in array that I pass as prop. What I tried to do looks as follows:
Additional selector in DeviceReduced
export const getDrivesWithIds = (ids: string[]) => createSelector(
getAllDevices,
devices => devices.filter(device => ids.includes(device.id))
);
And then combine them in the following way:
export const getVisibleDrives = createSelector(
getVisibleDrivesSerialNumbers,
(ids) => fromRoot.getDrivesWithIds
);
Issue here is that the returned type of this selector is
(ids: string[]) => MemoizedSelector<State, Device[]>
Which makes it impossible for me to do anything useful with this selector. As an example I'd like to filter this list by keyword, and I am not able to use filter method on it:
Example usage
export const getFilteredVisibleDrives = createSelector(
[getVisibleDrives, getKeywordFilterValue],
(visibleDrives, keywordFilter) => {
return visibleDrives
.filter(drive => // in this line there is an error: Property 'filter' does not exist on type '(ids: string[]) => MemoizedSelector<State, Device[]>'
drive.ipAddress.toLowerCase().includes(keywordFilter.toLowerCase()) ||
drive.type.toLowerCase().includes(keywordFilter.toLowerCase()) ||
drive.userText.toLowerCase().includes(keywordFilter.toLowerCase())
);
},
);
See my post NgRx: Parameterized selectors
for more info.
Update: NgRx v13+
Selector with props are deprecated, use selector factories instead:
Selector:
export const getCount = (props: {id: number, multiply:number}) =>
createSelector(
(state) => state.counter[props.id],
(counter) => counter * props.multiply
);
Component:
this.counter2 = this.store.pipe(
select(fromRoot.getCount({ id: 'counter2', multiply: 2 })
);
this.counter4 = this.store.pipe(
select(fromRoot.getCount({ id: 'counter4', multiply: 4 })
);
Deprecated
Selector:
export const getCount = () =>
createSelector(
(state, props) => state.counter[props.id],
(counter, props) => counter * props.multiply
);
Component:
this.counter2 = this.store.pipe(
select(fromRoot.getCount(), { id: 'counter2', multiply: 2 })
);
this.counter4 = this.store.pipe(
select(fromRoot.getCount(), { id: 'counter4', multiply: 4 })
);

How to pass parameters to the result Function of a createSelector (reselect)

My redux store has two independent slices A and B,
I need a memorized selector which returns something derived from B, only if A changes.
For example
const getSliceA = (state) => state.A
export const getSliceB = createSelector(
getSliceA,
(a) => { return MyDerive(state.B) }
)
My problem is how to send the state or state.B to the resultFunc.
const compareBySliceA = (prevSate: RootState, newState: RootState) => {
// This is just an example you can compare inner of Slice A
if (newState.SliceA === prevState.SliceB) {
return true
}
return false;
};
const getDerivedSliceB (state: RootState): List<any> =>
state.SliceB.filter(ElementB => ElementB.visible)
const createComparatorSelector = createSelectorCreator(
defaultMemoize,
compareBySliceA,
);
export const myDeepSelector = createComparatorSelector(
(state: RootState) => state,
(state: RootState): List<any> => getDerivedSliceB(state),
);
Both compareBySliceA and getDerivedSliceB needs the common parent, that is in above example State is the parnet of SliceA and SliceB.

Resources