Encountering an "Undefined" error while assigning an Observable within a map function in TypeScript

I'm currently diving into the world of observables and facing some challenges along the way.

Within my save() function, I call initialize() to retrieve certain Id's. In the initialize function, I am setting an Observable using a map method (payload) which shows the correct values when logged to the console. I have attempted switching from map to subscribe without success.

TLDR; How can I properly assign a value to a pre-hoisted variable within a map/subscribe function?

Related question: What is the appropriate type of observable to use for serializing from an object?

save(post: Post, draftId?: number, postId?: number): Observable<any> {
    let payload: Observable<any>;
    if (!postId) {
        post.state = post.state ? post.state : 'unsaved';
        this.initialize(post)
            .subscribe((res) => {  //error here... cannot run 
                console.log('Waypoint: posts.service.ts:36');
                payload = res;
            });

    } else if (draftId && postId) {
        post.state = 'autosaved'
        return this.updateDraft(post, draftId);
    };
    return payload;
}

initialize(post: Post): Observable<any> {
    let resultPost: number;
    let resultDraft: number;
    var payload: Observable<any>;
    this.createPost(post)
        .subscribe(res => {
        resultPost = res.id;
        post.postId = res.id
        this.createDraft(post)
            .map(res => {
                resultDraft = res.id;
                let _payload = {
                    postId: resultPost,
                    draftId: resultDraft
                };
                console.log('Waypoint: posts.service.ts:62');
                payload = Observable.create(_payload); // looks good!
            }, (error) => { console.log(error) }).subscribe();

        });
    console.log(payload); //undefined :(
    return payload;
}

Answer №1

It seems that your approach to logic is structured in the following way

  1. Initially, a Post is created using the createPost method which returns an Observable
  2. You then subscribe to createPost and retrieve the Post id
  3. The obtained Post id is utilized to generate a Draft through the createDraft method, which also returns an Observable
  4. Subsequently, you subscribe to createDraft and obtain the Draft id
  5. Finally, both the Post id and Draft id are combined to populate a payload variable that is used for further actions

If this understanding is correct, it may be beneficial to aim for a structure resembling the following

let postId;
let draftId;
this.createPost(post)
    .map(res => {postId = res.id})
    .mergeMap(() => this.createDraft(post))  
    .map(res => {draftId = res.id})
    .map(() => [postId, draftId])
    .subscribe(postDraftIds => payload = postDraftIds)

There exist alternative approaches utilizing Observable operators to achieve similar outcomes (such as employing switchMap instead of mergeMap - here's a helpful resource elucidating how switchMap operates), but I trust this explanation suffices

Answer №2

To address a few of your inquiries:

  1. The reason why console.log(payload); is returning undefined is most likely because this line of code is being executed before the subscribe on this.createDraft happens

  2. You seem to be using too many subscribes; ideally, there should only be one where you actually want to receive the data. In previous cases, consider using operators like map/flatMap to simplify the code structure. Here's an example:

    save(post: Post, draftId?: number, postId?: number): Observable<Payload> {
        if (!postId) {
            post.state = post.state ? post.state : 'unsaved';
            return this.initialize(post);
        } else if (draftId && postId) {
            post.state = 'autosaved'
            return this.updateDraft(post, draftId);
        }
    
        return Observable.empty(); 
    }
    
    initialize(post: Post): Observable<Payload> {
        let resultPost: number;
        return this.createPost(post)
            .flatMap(res => {
                resultPost = res.id;
                post.postId = res.id;
                return this.createDraft(post)
            }.map(res =>  new Payload(resultPost, res.id));
    }}
    

export class Payload { postId: number; draftId: number;

constructor(postId: number, draftId: number) {
    this.postId = postId;
    this.draftId = draftId;
}

}

You could further refactor the initialize function to something like this:

 initialize(post: Post): Observable<Payload> {
        return this.createPost(post)
            .flatMap(res => {
                return Observable.zip(res.id, this.createDraft(post))
            }).map(res =>  new Payload(res[0], res[1].id));
    }
  1. Avoid using any, opt for specific types instead

Please note that I have not tested this code; my suggestion is to experiment with the operators

// I'm not sure why Payload is not displaying correctly...

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

Step-by-step guide on creating a spy() function

const spawnedComponent = MockComponent; it('verifies that the Component is invoked', () =\>{ spawnedComponent.yourComponent() // invoking the your component for the first time spyOn(spawnedComponent, 'method').and.returnValue ...

Tips for effectively using ngOnChanges in Angular 2 to validate inputs without causing the 'Expression has changed after it was checked' error

I attempted to create my own custom component with basic validation using regex that can be passed as input to the component. There are two scenarios to consider: one where the form is initially empty (new item form) and another where data is already prese ...

Invoking a parent component's function using jQuery summernote

I'm having trouble calling a function from outside of a custom button component that I am adding to the ngx-summernote toolbar. The code I currently have is causing compilation errors. I've tried various methods, but they all result in an error s ...

Tips for keeping the information current from one generation to the next

Is there a way to pass data from a parent component to a child component using template binding (on ngInit), and then have the same updated data shared between them without involving a service or other methods? I experimented with the following template: ...

Issue with Socket.io: Data not received by the last user who connected

I have developed a Node.js and Express application with real-time drawing functionality on canvas using socket.io version 1.7.2. Users are divided into separate socket rooms to enable multiple teams to draw independently. However, there is an issue where t ...

Troubleshooting issues with Angular Material's basic mat-autocomplete functionality

After spending more than 72 hours trying to resolve this issue, I have hit a roadblock. Oddly enough, when I create a minimal working example in stackblitz, everything functions perfectly. The problem lies within a simple mat-autocomplete embedded within ...

Unable to Trigger Click Event on Div Element in Angular 9

Take a look at my HTML code snippet <mat-grid-list cols="3" rowHeight="150px"> <mat-grid-tile *ngFor="let tile of tiles;index as j;" [colspan]="tile.cols" [rowspan]="tile.rows" ...

Exploring a JSON object using PlaywrightWould you like to know how

Greetings! Here is a snippet of code that I have, which initiates an API call to a specific URL. const [response] = await Promise.all([ page.waitForResponse(res => res.status() ==200 && res.url() == & ...

The issue of footer overlapping the login form is observed on iOS devices while using Safari and Chrome

Unique ImageI am currently working on an Angular 8 project with Angular Material. I have successfully designed a fully functional login page. However, I am encountering a problem specifically on iOS devices such as iPhones and iPads, whether it is Safari o ...

Discovering the country associated with a country code using ngx-intl-tel-input

In my application, I am trying to implement a phone number field using this StackBlitz link. However, I have observed that it is not possible to search for a country by typing the country code (e.g., +231) in the country search dropdown. When I type a coun ...

Transforming res.json() into an Array of Objects

I am dealing with a Java webservice that outputs a list of Json objects with specific properties: public class Oferta { private int id; private String categoria; private String descricao_oferta; private String anunciante; private double valor; private boo ...

Tips for retrieving refreshed information following modifications via a POST request in Angular 2

I've scoured the web extensively, but I just can't seem to grasp how to retrieve updated data from the database. I'm currently learning Angular 2, and my predicament lies in fetching data from a table named 'branches' using PHP wit ...

Using "array_agg" in a "having clause" with Sequelize

I am facing a particular scenario with my database setup. I have three tables named computers, flags, and computerFlags that establish relationships between them. The structure of the computerFlags table is as follows: computerName | flagId computer1 | ...

When I define a type in TypeScript, it displays "any" instead

Imagine a scenario where we have a basic abstract class that represents a piece in a board game such as chess or checkers. export abstract class Piece<Tags, Move, Position = Vector2> { public constructor(public position: Position, public tags = nul ...

Struggling to launch on Vercel and encountering the error message, """is not allowed by Access-Control-Allow-Origin. Status code: 204""

Greetings! I trust you are doing well. Currently, I am engrossed in developing a full-stack application. The app runs smoothly on localhost without any issues. However, upon deploying both the server and front end on Vercel, a snag arose when attempting to ...

Karma Test Error: Disconnected due to lack of communication within a 60000 millisecond timeframe

Executing the "ng test" command for Unit Testing in an Angular project is resulting in some errors. Chrome Headless 120.0.6099.130 (Windows 10) ERROR Disconnected , because no message in 60000 ms. Chrome Headless 120.0.6099.130 (Windows 10): Executed 404 ...

The user type is not yet loaded from Firestore when the view is rendered

I am currently in the process of developing an Ionic - Angular application that allows hospital patients to submit requests to nursing staff, who can then view the assigned requests based on the patient's room. Nurses have access to all requests, whil ...

What is preventing me from using property null checking to narrow down types?

Why does TypeScript give an error when using property checking to narrow the type like this? function test2(value:{a:number}|{b:number}){ // `.a` underlined with: "Property a does not exist on type {b:number}" if(value.a != null){ ...

Ways to restrict the width of an Ionic application

I currently utilize Ionic for mobile devices such as phones and tablets. The appearance on phones is stunning. However, it appears too elongated on tablets. Anyone know how to limit the width of the app on all pages? https://i.sstatic.net/IJKP6.png ...

`Angular2 - exploring the complexities of function scope`

I'm facing a challenge while working on my Angular2 Sample with the http module. Here is a snippet from my component: app.loginComponent = ng.core.Component({ selector: 'login', templateUrl: 'app/login/login.html&ap ...