What steps can I take to guarantee that the observer receives the latest value immediately upon subscribing?

In my Angular 2 and Typescript project, I am utilizing rxjs. The goal is to share a common web-resource (referred to as a "project" in the app) among multiple components. To achieve this, I implemented a service that provides an observable to be shared by all clients:

/**
 * Provided to clients for subscription.
 */
private _observable : Observable<Project>;

/**
 * Used for emitting events to clients.
 */
private _observer : Observer<Project>;

constructor(private _http: Http) {
    // Create the observable and observer once to be shared with all subscribers.
    this._observable = Observable.create( (obs : Observer<Project>) => {
        this._observer = obs;
    });
}

Clients can simply reference the _observable and subscribe to it.

/**
 * Returns an observable always pointing to the active project.
 */
get ActiveProject() : Observable<Project> {
    return (this._observable);
}

When a component wants to load a specific project, it triggers the following method:

/**
 * @param id The id of the project to set for all subscribers
 */
setActiveProject(id : string) {
    // Prevents project changes during ongoing requests
    if (this._httpRequest) {
        throw { "err" : "HTTP request in progress" };
    }

    this._httpRequest = this._http.get('/api/project/' + id)
        .catch(this.handleError)
        .map(res => new Project(res.json()));

    this._httpRequest.subscribe(res => {
        // Cache the project
        this._cachedProject = res;
        // Indicates no more requests are pending
        this._httpRequest = null;
        // Notifies subscribers about the change
        this._observer.next(this._cachedProject)

        console.log("Retrieved project");
    });
}

This code performs an HTTP request, transforms the JSON data into an instance, and uses this._observer.next() to inform all subscribers about the update.

If a subscription occurs after the initial HTTP request, the subscriber won't receive any data until a new request is sent. I've learned about a caching or replay mechanism in rxjs that addresses this issue, but I'm unsure how to implement it.

tl;dr: How can I ensure that a call to subscribe on the observer initially receives the most recent value?

Extra question: Did removing the observer from the observable in the constructor essentially create a subject?

Answer №1

So, this is how the BehaviorSubject works in action.

import { BehaviorSubject } from 'rxjs/subject/BehaviorSubject';
...
obs = new BehaviorSubject(4);
obs.subscribe(); // outputs 4
obs.next(3); // outputs 3
obs.subscribe(); // outputs 3

Answer №2

I typically use shareReplay(1) to achieve this functionality. When using this operator with a parameter of 1, the latest emitted value is stored in a buffer so that it can be immediately passed on to any new subscriber. For more information, refer to the documentation.

var interval = Rx.Observable.interval(1000);

var source = interval
    .take(4)
    .doAction(function (x) {
        console.log('Side effect');
    });

var published = source
    .shareReplay(3);

published.subscribe(createObserver('SourceA'));
published.subscribe(createObserver('SourceB'));

// Creating a third subscription after the previous two subscriptions have
// completed. Notice that no side effects result from this subscription,
// because the notifications are cached and replayed.
Rx.Observable
    .return(true)
    .delay(6000)
    .flatMap(published)
    .subscribe(createObserver('SourceC'));

function createObserver(tag) {
    return Rx.Observer.create(
        function (x) {
            console.log('Next: ' + tag + x);
        },
        function (err) {
            console.log('Error: ' + err);
        },
        function () {
            console.log('Completed');
        });
}

// => Side effect
// => Next: SourceA0
// => Next: SourceB0
// => Side effect
// => Next: SourceA1
// => Next: SourceB1
// => Side effect
// => Next: SourceA2
// => Next: SourceB2
// => Side effect
// => Next: SourceA3
// => Next: SourceB3
// => Completed
// => Completed
// => Next: SourceC1
// => Next: SourceC2
// => Next: SourceC3
// => Completed

Extra question: By "pulling the observer out of the observable" (in the constructor), have I essentially created a subject?

I am not sure what you mean by that, but no. A subject is both an observer and an observable and have specific semantics. It is not enough to 'pull the observer out of the observable' as you say. For subjects semantics, have a look here : What are the semantics of different RxJS subjects?

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Having trouble with Vue's $route.push method not working when invoked from a method?

I am currently in the process of creating a search bar for my application using the vue-bootstrap-typeahead autocomplete library. For those unfamiliar, when an option is selected from the suggestions list, it triggers the @hit event which passes the result ...

I'm having trouble with the calculator, unable to identify the issue (Typescript)

I'm struggling with programming a calculator for my class. I followed the instructions from our lesson, but it's not functioning properly and I can't pinpoint the issue. I just need a hint on where the problem might be located. The calculat ...

Image Box is effortlessly moved around by dragging and dropping

While working on my HTML code in Angular 9, I'm trying to figure out where I need to make changes to display image previews instead of broken images in the browser: <div cdkDropListGroup> <div class="box-container"> <h2>TABLE ...

JavaScript fails to display image slideshows upon loading

Currently, I am utilizing a slideshow feature in order to display any additional images that may be retrieved from the globalgiving api. However, there is an issue with the slideshow not appearing when the page is initially loaded or when opening a modal, ...

Is there an improved method for designing a schema?

Having 4 schemas in this example, namely Picture, Video, and Game, where each can have multiple Download instances. While this setup works well when searching downloads from the invoker side (Picture, Video, and Game), it becomes messy with multiple tables ...

The global variable in TypeScript is not specified; it is initialized within the declaration `declare global{}`

In my TypeScript code, I'm facing a challenge when trying to add a global scope variable. In JavaScript (NodeJS), this process is straightforward: // index.js globalThis.helloWorld = 'Hello World!'; require('./log.js') // log.js c ...

I'm struggling to make this background show up in a div

Anyone able to help me figure this out? I can't seem to get the achtergrond_homepage.png as a background in rounded corners. Edit: It seems like the gray color is always on top. Could it be controlled in the JavaScript part? This is the CSS: @ch ...

Tips on using the $.grep() method in jQuery to filter dynamic inputs

When using the "grep" function in jQuery to filter data, the code sample below works well for static conditions. However, if the "name" and "school" parameters have multiple values, how can we filter them? Let's say I receive the name and school from ...

Express Session Issue Preventing Ajax Call from Functioning

After testing the /temp route directly in the browser and then through an ajax call to the /test route, I noticed a difference in the session information. When accessed directly, the session retains the data added via the /temp route, but when called throu ...

Encountering a problem with AngularJS ui router templates

I have defined the following routes in my project: $stateProvider .state('access', { abstract: true, url: '/access', templateUrl: 'login.html' }) .state('access.signin', { ...

Data from HTML not being transferred by Angular Forms

I am facing an issue with transferring input data from HTML's <select> element to Angular Forms. Let's take a look at my code first. File Name: home-page.component.html <form [formGroup]="rForm" (ngSubmit)="addPaste(rForm.value)"> ...

Execute multiple events using jQuery's .trigger() method

As I develop a jQuery plugin, I am utilizing the .on and .trigger functions for my pub/sub system. One challenge I am facing is triggering multiple events in different scenarios with ease. My question is whether it is possible to trigger multiple events a ...

Transforming Form Input into Request Payload for an AJAX Call

I'm facing a challenge in submitting my form data through a POST AJAX request and haven't been able to come across any solutions so far. Due to the dynamic creation of the form based on database content, I can't simply fetch the values usin ...

You can use AJAX, JQuery, or JavaScript in PHP to upload a total of 7 files by utilizing 7 individual file input

My client has a unique request - they want to be able to upload a file in PHP without using the traditional <form> tag or a submit button. While I am familiar with file uploads in PHP, I am unsure of how to achieve this without utilizing the <for ...

Creating 3D models in three.js

Working with a 3D point cloud data in three.js, I have successfully added points to a Geometry using Vector3. Now I am looking to create surfaces from these points. for(var key in dt) { var hole = dt[key]; var pX = hole['x'] - planeMinX; var pY ...

ajax-jquery request declined

I have a jquery-ajax function that is being called multiple times with different IP addresses each time. This function makes a call to an action in the mvc4 controller responsible for executing a ping and returning the results. After analyzing the request ...

Having trouble with implementing the .addclass function in a dice roller project

I'm looking to have the element with id=die load initially, and then on a button click event labeled "click me," apply the corresponding CSS class such as 'die1,' 'die2,' and so forth. function roll() { var die = Math.floor(Ma ...

Error in Typescript stating that the property 'children' is not found on the imported interface of type 'IntrinsicAttributes & Props'

When I try to import an interface into my Card component and extend CardProps, a yarn build (Typescript 4.5.4) displays the following error: Type error: Type '{ children: Element[]; className: string; border: true; disabled: boolean; }' is not as ...

Encountering issues with connecting to the MongoDB server through Node.js

When working with MongoDB in Python, everything runs smoothly without any errors. However, when using Node.js, an error keeps popping up. Can someone please guide me on how to resolve this issue? jdcaovuwqxoqppwwqmjcawpwuaciwowjqwqhpaiwdoqi Below is the ...

Strategies for aligning tooltips with the locations of dragged elements

One of my projects involves a simple drag element example inspired by Angular documentation. The example features a button that can be dragged around within a container and comes with a tooltip. <div class="example-boundary"> <div ...