3

What is the proper method to assign a local object from an http response observable?

I want to assign myUser object to the response that I get from http, which is of the same type. Here is my code:

import 'rxjs/add/observable/from';
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';

import { Injectable } from '@angular/core';
import { Headers, Http, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';

import { UpdateUser } from '../_models/updateuser';


@Injectable()
export class FormService {

    private myUser : UpdateUser;

    constructor(private http: Http) {
    }

    getUser(id: number): Observable<UpdateUser> {
        const _url = 'https://192.168.1.2:4444/api/auth/getuser/' + id;
        const headers = new Headers();
        headers.append('X-User', sessionStorage.getItem('username'));
        headers.append('X-Token', sessionStorage.getItem('token'));
        headers.append('X-AccessTime', sessionStorage.getItem('AccessTime'));
        headers.append('Content-Type', 'application/json');
        const options = new RequestOptions({ headers: headers });

        return this.http.get(_url, options)
            .map(response => {
                const responseAsObject = response.json();
                this.myUser = responseAsObject; //<--- Here
                console.log(this.myUser);
                return responseAsObject;
            });
    }  



     logUser(){
      console.log(this.myUser) //<--- undefined if called formService.logUser() from child components.
}
    }

Logging this.myUser within the observable nets a positive result, however when I try to access the local variable from a child component that is provided by this service, it returns undefined, such as this.formService.logUser(); if used in a child component, returns undefined.

What is the proper method to broadcast an object from a service to a child component? I may be having async issues as the child component is being initialized before the service even gets the object from the http.

UPDATED QUESTION: Child Component

import { FormService } from '../../../../_services/form.service';
import { UpdateUser } from '../_models/updateuser';

@Component({
  selector: 'update-form',
  templateUrl: './updateForm.html',
  providers: [FormService],
})

export class UpdateFormComponent implements OnInit{


  private localUser : UpdateUser;

  ngOnDestroy() {
  }

  constructor(
    private formService: FormService,
  ) {}

  ngOnInit() {

    this.formService.logUser(); //undefined
    this.formService.myUser = this.localUser; //Assign something like this, but with an observable..
  }
}

So my end game is only to get the service to broadcast myUser for the child component to receive it and then display its data members to a form. But any time I assigned myUser to localUser it appears as undefined.

Mustafa
  • 177
  • 6
  • 20
  • Do `const that = this;` before the `return this.http.get(_url, options)` and replace `this.myUser = responseAsObject;` with `that.myUser = responseAsObject;` – Kunal Dethe Feb 12 '18 at 14:08
  • do I apply any changes to logUser() method? – Mustafa Feb 12 '18 at 14:10
  • No changes required. – Kunal Dethe Feb 12 '18 at 14:12
  • The problem is caused by the asyncronous behavior, not the `this` error. Please provide the code for the 'child component' – Boris Lobanov Feb 12 '18 at 14:23
  • @BorisLobanov, updated question with child component – Mustafa Feb 12 '18 at 14:30
  • You should note that `Http` is deprecated in favour of [`HttpClient`](https://angular.io/guide/http) which changes the way you write service headers, and assign returns, as well. – msanford Feb 12 '18 at 14:32
  • @msanford how would I update my code to use HttpClient? – Mustafa Feb 12 '18 at 14:33
  • @M.Itani Click the link I provided for HttpClient and read the documentation, which is complete with examples. Also see https://stackoverflow.com/questions/45129790/difference-between-http-and-httpclient-in-angular-4 – msanford Feb 12 '18 at 14:33
  • Possible duplicate of [HTTP call using Observable not working](https://stackoverflow.com/questions/48128062/http-call-using-observable-not-working) – msanford Feb 12 '18 at 14:35

1 Answers1

2

You can try to use a BehaviorSubject to store the user object:

In your service:

private myUser = new BehaviorSubject<UpdateUser>(null);

getUser() {
    // ...
    return this.http.get(_url, options)
        .map(response => {
            const responseAsObject = response.json();
            this.myUser.next(responseAsObject); // change the value!
            console.log(this.myUser);
            return responseAsObject;
        });
}

logUser() {
    console.log(this.myUser.getValue());
}

In your component:

export class UpdateFormComponent implements OnInit{
  private localUser : UpdateUser;

  constructor(
    private formService: FormService,
  ) {}

    ngOnInit() {
        this.formService.myUser
            .filter(user => user !== null)
            .subscribe(user => {
                this.formService.logUser(); // also 'user' here is the user object from the service, you can do whatever you want with it
        });  
    }
}

Also, very important: you want the components to use the same instance of the service, so remove it from the providers from both components and add it to the providers of some higher level component, so that they share one instance.

Boris Lobanov
  • 2,409
  • 1
  • 11
  • 19
  • this would work but I'm using getUser to get a User object from http using an ID (number) passed from the template. In Parent component: this.formService.getUser(event.data.USER_ID).subscribe, which would mean I have to pass event.data.USER_ID to the child component as well. My main issue is passing a variable to the child component to begin with.. – Mustafa Feb 12 '18 at 14:53
  • Well, the `myUser` property is not going to be initialised until you call the observable. You can pass it from parent to child or use Subject to store the user. But in any case, you will need a subscription in the child component. – Boris Lobanov Feb 12 '18 at 14:58
  • how can I use a subject in this case to store the user object? – Mustafa Feb 12 '18 at 15:21
  • I can safely say you grasped what I'm looking for, but the subject is being logged and not the object itself. https://i.imgur.com/fSEL2mp.jpg – Mustafa Feb 12 '18 at 16:04
  • it returned null inside oninit – Mustafa Feb 12 '18 at 16:12
  • added the filter operator – Boris Lobanov Feb 12 '18 at 16:14
  • now its not logging at all, maybe theres an async issue? – Mustafa Feb 12 '18 at 16:24
  • Is the request working properly? The component is subscribed to the service so as soon as the value changes in the BehaviorSubject it should trigger the subscribe again. Note that we are no longer calling subscribe of the `getUser` method in this component, I'm assuming you are calling it somewhere else. – Boris Lobanov Feb 12 '18 at 16:28
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/164984/discussion-between-m-itani-and-boris-lobanov). – Mustafa Feb 12 '18 at 16:29