I have an observable like
imageOptions$: Observable<BoundImagesToProject[]> = this.imagesService
.getBoundImages({ projectId: this.projectId })
.pipe(map((images) => (images.data)));
and in the template I use it like
<div class="form-field input-group">
<label for="image">Image</label>
<mat-select id="image" class="select--full-width" placeholder="Image" formControlName="image">
<mat-option *ngFor="let image of imageOptions$ | async" [value]="image.imageId">
{{ image.name }}
</mat-option>
</mat-select>
<mat-error *ngIf="createVmForm.get('image').getError('required') && createVmForm.get('image').dirty"
>Field is required</mat-error
>
</div>
Now I want to use the imagesOptions$ observable in the TS file like
this.imageChangeSubscription = this.createVmForm.get('image').valueChanges.subscribe((value) => {
this.selectedImageVolumeSize = this.imagesOptions$ // get array value and match id and get the size.
If it was an array it would be like
this.selectedImageVolumeSize = this.images.find((image) => image.imageId === value).size;
I want to do it without subscribing to imageOptions$ observable in the imageChangeSubscription to avoid subscribtion in side subscription and without using an extra property in the TS file
Any way to do it?
You can use switchMap() to avoid nested subscriptions. But you should update imagesOptions$ so it can share its latest value with multiple subscribers using the shareReplay() operator.
imageOptions$: Observable<BoundImagesToProject[]> = this.imagesService
.getBoundImages({ projectId: this.projectId })
.pipe(
map(({data}) => data),
shareReplay(1)
);
Then in your subscription, grab the latest value from imageOptions$ to find your size.
this.imageChangeSubscription = this.createVmForm
.get('image').valueChanges
.pipe(
switchMap(value => this.imagesOptions$
.pipe(
map(images => images.find(({imageId}) => imageId===value ))
)
)
).subscribe();
I like the answer that @Joshua McCarthy provided but you don't need to subscribe at all and you can get a "cleaner" (depending from the point of view of the consumer) version.
Kudos for the statement about the shareReplay(1) to avoid extra side-effects (which they are usually API calls, resulting in undesired network consumption). I highly suggest you apply it to your image options observable.
then I would proceed to create another observable:
import { combineLatest, shareReplay } from 'rxjs/operators'
@Component({...})
export class YourComponent {
...
...
selectedImageVolumeSize$ = combineLatest([this.createVmForm['image'].valueChanges, this.imagesOptions$]).pipe(
map(([selectedImageId, imageOptions] => imageOptions.find(item => item.id === selectedImageId)
)
constructor(...){}
}