Assuming i have the following rxjs pipe:
start$.pip(
map((id)=> {}), //I want to save the "id" value to be used in the end of the pipe
map(...),
switchMap(...),
map(...),
switchMap(...),
map(...),
switchMap(...),
switchMap(...)//I need the original "id" value here
).subscribe()
Is there a way to keep the 'id" value throughout the pip so it can be used at the end of the pipe?
Motivation: It comes often in NGRX effects where i want to use the original payload data of the triggering source action for generating the new action.
We will calculate the volume of a 6-meter length pipe, with an inner diameter equal to 15 centimeters. The pipe is used to transport water. Let's put these data into the calculator to find the volume of water in the pipe, as well as its mass. First, enter the pipe's diameter: inner diameter = 15 cm. Then, type in its length: length = 6 m.
It can be rearranged in a few ways, but this way is nice because it has the variables we can measure. It says that the head loss (in other words the drop in pressure from one end of a pipe to the other) is a function of the flow rate, and the diameter, length, and roughness of the pipe.
The water passes through a flow meter and valve, past some pressure gauges, through the sample pipe in question, and finally through a showerhead. I picked a showerhead since, for many of us, it’s the most tangible and immediate connection we have to pressure problems in plumbing.
We expect the pressure drop to be 1 over (⅔)^4.9 or about 7 times higher than the original pipe. At 0.3 gpm, the pressure drop is 3 inches. That’s about 6 times the original. At 0.6 gpm, the pressure drop is 7.5 inches, about 7 times the original. And at 0.9 gpm, we’re off the scale.
I think the correct way is to make another closure
const processId = (id) => observableOf(id)
.pipe(
map(() => { ... }),
map(...),
switchMap(...),
map(...),
switchMap(...),
map(...),
switchMap(...),
switchMap(...) // Use id here
);
const getPipe = () => start$
.pipe(switchMap(processId));
Storing local variable as a side effect in getPipe
is OK, but it can break if start$
Observable emits more values.
The closure seems correct, would like to add a detail gotcha, I boiled down to a base case:
Say you want to get to the end of a stream, and based on something that happened earlier, do this or that. So you use the closure pattern, start with a flag of false, and later in the pipe, set it to true. At the end of the pipe, do this or that depending on the flag. Works nicely.
Problem is, after the first time, if that value got set to true, depending on your use (say, an NGRX effect, which is where I ran into this) it'll be true forever (no surprise, the closure persists) unless something else sets it back to the default (false).
Basic form/fix:
stream$ = ( ( my_flag = false ) => {
return ( otherStream$
.variousOperators ( ... )
.do ( x => {
my_flag = some_condition_of_x ? true : false
} )
.map ( x => my_flag ? a : b )
.do ( x => {
my_flag = false; // reset
} )
.map ( x => x )
);
} ) ();
Had a pretty tricky case involving some NGRX effects that was greatly (greatly) simplified by this.
The way suggested by @René Winkler is the right way for short pipes.
If the pipe is long though it can be tedious and somehow can make the code less readable.
One alternative approach can be to create a function (or method) where you define id
as a local variable that you set within the first map
so that you can use it at your convenience all along the chain of operators, something like
getLongPipe(id) {
let _id;
return start$.pipe(
map((id)=> {_id = id; ....}), //I want to save the "id" value to be used in the end of the pipe
map(...),
switchMap(...),
map(...),
switchMap(...),
map(...),
switchMap(...),
switchMap(...)//Here you can use _id
)
}
getLongPipe(id).subscribe()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With