NGRX Selectors
Adding a first selector
In this part of the lab, you'll add a selector that queries all the flights that are not on a defined negative list.
-
Open the file
flight-booking.reducer.ts
and add a propertynegativeList
to yourState
:export interface State { flights: Flight[]; negativeList: number[]; } export const initialState: State = { flights: [], negativeList: [3], };
For the sake of simplicity, this example defines a default value for the negative list to filter the flight with the id 3.
-
In your
+state
folder, create a fileflight-booking.selectors.ts
and enter the following lines. If it already exists, update it as follows:import { createSelector } from '@ngrx/store'; import { FlightBookingAppState } from './flight-booking.reducer'; export const selectFlights = (s: FlightBookingAppState) => s.flightBooking.flights; export const negativeList = (s: FlightBookingAppState) => s.flightBooking.negativeList; export const selectedFilteredFlights = createSelector(selectFlights, negativeList, (flights, negativeList) => flights.filter((f) => !negativeList.includes(f.id)) );
-
In your
flight-search.component.ts
, use the selector when fetching data from the store:this.flights$ = this.store.select(selectedFilteredFlights);
-
Test your application.
Bonus: Using feature selectors *
To get rid of your FlightBookingAppState type, you can use a feature selector pointing to the branch of your feature:
// Create feature selector
export const selectFlightBooking = createFeatureSelector<State>('flightBooking');
// Use feature selector to get data from feature branch
export const selectFlights = createSelector(selectFlightBooking, s => s.flights);
export const negativeList = createSelector(selectFlightBooking, s => s.negativeList);
[...]
Bonus: Using parameterized selectors *
You can pass a property object to a selector when calling it. This object is assigned to a further parameter in your selectors projection function.
-
In your
flight-booking.selectors.ts
file, add the following selector:export const selectFlightsWithProps = (props: { blackList: number[] }) => createSelector(selectFlights, (flights) => flights.filter((f) => !props.blackList.includes(f.id)));
Please note that the projector get an additional
props
parameter. It points to a dynamic object. -
Open the file
flight-search.component.ts
and fetch data with this selector:this.flights$ = this.store.select(selectFlightsWithProps({ blackList: [3] }));
-
Test your solution.
Bonus: Compose complex component selector **
You use more complex selectors that reuse present selectors and compose a customized result that can be used in a concrete use case implemented in one of your smart components.
-
In your
flight-booking.reducer.ts
file, add the following state definition and initial state:export interface State { flights: Flight[]; // NEW: passenger: Record< number, { id: number; name: string; firstName: string; } >; bookings: { passengerId: number; flightId: number; }[]; user: { name: string; passengerId: number; }; }
export const initialState: State = { flights: [], // NEW: passenger: { 1: { id: 1, name: 'Smith', firstName: 'Anne' }, }, bookings: [ { passengerId: 1, flightId: 3 }, { passengerId: 1, flightId: 5 }, ], user: { name: 'anne.smith', passengerId: 1 }, };
-
Open the file
flight-booking.selectors.ts
and implement all necessary selectors to select the new state properties.Show code
export const selectPassengers = createSelector(selectFlightBookingState, (state) => state.passenger); export const selectBookings = createSelector(selectFlightBookingState, (state) => state.bookings); export const selectUser = createSelector(selectFlightBookingState, (state) => state.user);
-
Define a new selector
selectActiveUserFlights
that returns only those flights that the active user has booked.Show code
export const selectActiveUserFlights = createSelector( // Selectors: selectFlights, selectBookings, selectUser, // Projector: (flights, bookings, user) => { const activeUserPassengerId = user.passengerId; const activeUserFlightIds = bookings .filter((b) => b.passengerId === activeUserPassengerId) .map((b) => b.flightId); const activeUserFlights = flights.filter((f) => activeUserFlightIds.includes(f.id)); return activeUserFlights; } );
-
Try out your new selector
selectActiveUserFlights
by using it in theflight-search.component.ts
. -
Test your solution.