Joseph M Njuguna
Joseph M Njuguna

Reputation: 547

Execute method only once after all changes have happened in Observable in Angular ReplaySubject

I have created a service which checks for changes in parent component and send notifications to the child component.

Below is the simple service.

import { Injectable } from '@angular/core';
import { ReplaySubject } from 'rxjs/ReplaySubject';
import { CloudDate } from './cloudDate';
import { Clouding } from './clouding';

@Injectable()
export class CloudingService {
    // Observable string sources
    private cloudingAnnouncedSource = new ReplaySubject<Clouding>(1);
    private cloudingConfirmedSource = new ReplaySubject<Clouding>(1);

    // Observable string streams
    cloudingAnnounced$ = this.cloudingAnnouncedSource.asObservable();
    cloudingConfirmed$ = this.cloudingConfirmedSource.asObservable();

    // Service message commands
    announceClouding(clouding: Clouding) {
        this.cloudingAnnouncedSource.next(clouding);
    }

    confirmClouding(clouding: Clouding) {
        this.cloudingConfirmedSource.next(clouding);
    }
}

Clouding class looks like this:

export class Clouding {
    cameraName: string;
    cloudDate: string;
    cameraType: string;
}

Now in the parent component, this class is initialized in the constructor and its variables will change depending on different methods. Example:

// In constructor
this.clouding = new Clouding();

// A method
getCameras(): void {
    this.clouding.cameraName = this.currentCloudName;
}

//Another method
getCloudDates(): void {
    this.clouding.cloudDate = this.currentCloudDate.cloudDate;
}

The variables this.currentCloudName and this.currentCloudDate.cloudDate will change dynamically depending on button clicks.

When the buttons are clicked, I do:

this.cloudingService.announceClouding(this.clouding);

In child component, I do this to get the new value of clouding.

import { Component, OnDestroy, Input, OnInit } from '@angular/core';
import { Clouding} from './clouding';
import { CloudingService } from './clouding.service';
import { Subscription } from 'rxjs/Subscription';

@Component({
    selector: 'app-images',
    templateUrl: './images.component.html',
    styleUrls: ['./images.component.css']
})

export class ImagesComponent implements OnDestroy, OnInit {
    title = 'Images';
    @Input()
    clouding: Clouding;
    subscription: Subscription;

    constructor(
        private cloudingService: CloudingService
    ) {
    }

    ngOnInit(): void {
        this.subscription = 
           this.cloudingService.cloudingAnnounced$.subscribe(
            clouding => {
                this.clouding = clouding;
                console.log(this.clouding);
            },
            // The 2nd callback handles errors.
            (err) => console.error(err),
            // The 3rd callback handles the "complete" event.
            () => {
            }
        );
        this.cloudingService.confirmClouding(this.clouding);
    }

    ngOnDestroy() {
        // prevent memory leak when component destroyed
        this.subscription.unsubscribe();
    }
}

Now on the console, I get the below when I click on a button in the parent:

Clouding {cameraName: "Benjamin2", cloudDate: "2017-08-26"}
Clouding {cameraName: "Benjamin2", cloudDate: "2017-08-24"}

My question is, is there a way to make console print only the last change i.e.

Clouding {cameraName: "Benjamin2", cloudDate: "2017-08-24"}

and ignore the first change that occurred. I dont want to do execute a method everytime the object changes, just execute after all changes have been subscribed.

Hope the question is clear.

Upvotes: 0

Views: 552

Answers (1)

Richard Matsen
Richard Matsen

Reputation: 23483

It sounds like you need a third button to trigger the announce, take this off the name-setting button and date-setting button and put it on the new button.

this.cloudingService.announceClouding(this.clouding);

Upvotes: 1

Related Questions