Reputation: 115
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?
Upvotes: 1
Views: 239
Reputation: 8467
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(...){}
}
Upvotes: 0
Reputation: 1852
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();
Upvotes: 1