简体   繁体   中英

Angular5 How to use Http Observable

I have a problem with HTTP requests on Angular5. I have two components (" LoginComponent ", " WellcomeComponent ") and one service (" AuthService ").

I am using auth servise to handle HTTP requests , and also this service is a bridge between two components.

I can do get and post requests from service , but can't use results properly, because of Observable things. I am not an experienced Angular developer so can't find a solution.

So now , I am explaining step by step. I will write all my code here, and all steps are signed by ..Step_ text in code, so you can follow all steps by this sign.


LoginComponent

import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { AuthService } from '../../auth.service';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html'
})
export class LoginComponent {

    loginUserData = {}

    constructor(
        private _auth: AuthService,
        public router:Router ){
            //SOMETHING
        }

    //CLICK FORM SUBMIT BUTTON
    public onSubmit(values:Object):void {

        //SET USER DATA WITH FORM VALUES
        console.log("LoginStep_1 - Hello")

        this._auth.getToken(this.loginUserData)
        // CHECK TOKEN

        // IF VERIFIED
            console.log('LoginStep_2 - Token Verified');

            this._auth.setUserData(this.loginUserData['username']);
            console.log('LoginStep_3 - SetUserData Finished');

            this.router.navigate(['/wellcome'])
            console.log("LoginStep_4 - Navigated");
    }

}

AuthService

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router';

@Injectable()
export class AuthService {

    private _tokenUrl = "http://localhost:8000/api/auth/token/";
    private getUserInfoUrl = "http://127.0.0.1:8000/api/userInfo/?username="
    private userInfo : any;

    constructor(
        private http: HttpClient,
        private _router: Router
    ) { }

    getToken(user){
        console.log("getToken_Step1 - Hello");

        this.http.post<any>(this._tokenUrl, user)
        .subscribe(
            res => {

                //getToken_Step2 : SET TOKEN SOMEWHERE
                console.log("getToken_Step2 - Token : ", res.token);

            },
            err => console.log(err)
        );

        console.log("getToken_Step3 - Bye");
    }

    setUserData(userName){
        console.log("setUserData_Step1 - Hello");

        this.http.get<any>(this.getUserInfoUrl+userName)
        .subscribe(
            res=>{

                //setUserData_Step2 : SET USERINFO SOMEWHERE
                console.log("setUserData_Step2 - User Info : ", res);

            },
            err=>{
                console.log(err);
            }
        )

        console.log("setUserData_Step3 - Bye");
    }

    getUserData(){
        console.log("getUserData_Step1 - Hello");
        return this.userInfo;
    }

}

WellcomeComponent

import { Component, OnInit } from '@angular/core';
import { AuthService } from '../../../auth.service';

@Component({
  selector: 'app-wellcome',
  templateUrl: './wellcome.component.html',
  styleUrls: ['./wellcome.component.scss'],
})
export class WellcomeComponent implements OnInit {

    constructor(private _auth: AuthService){ }

    ngOnInit() {
        console.log("Wellcome_Step1 - Hello");
        let userInfo = this._auth.getUserData();
        console.log("Wellcome_Step2 - Show User Info");
    }

}

If you look codes, there are a lot of console.log("Step.. , I am explaining what I want to do by using them.

In a short way, I can say I want to see them at this order in console screen :

  1. LoginStep_1 - Hello : This step will work by clicking a submit button on a form
  2. getToken_Step1 - Hello : Go to auth service
  3. getToken_Step2 - Token : Get token from an API and write it localStorage
  4. getToken_Step3 - Bye
  5. LoginStep_2 - Token Verified : then come back and verify this token.
  6. setUserData_Step1 - Hello
  7. setUserData_Step2 - User Info : Get user info from an API and write it localStorage
  8. setUserData_Step3 - Bye
  9. LoginStep_3 - SetUserData Finished : I got token, verified it , got user info and wrote it. So I can go navigate now.
  10. Wellcome_Step1 - Hello : Navigated from Login
  11. getUserData_Step1 - Hello : get all info that written by LoginComponent
  12. Wellcome_Step2 - Show User Info : Do what You want with user info

These steps are what I want to see on console log screen but, I can't see them with right order. My console output is very different from my expect. Because I don't know how to use angular 5 observable , so can't handle returned value from API.

My console output something like that :

  1. LoginStep_1- Hello
  2. LoginStep_3 - SetUserData Finished
  3. getToken_Step1 - Hello
  4. getToken_Step2 - Token

... Here, why this order ? How can I correct it ?


So now , How can I do this ? I want to take token and user info by LoginComponent and use them from WellCome component.

As I said, I am not an experienced coder, so maybe solution is very simple but couldn't find.

Thank you for your helps.

Because your requests to backend is asynchronous, it will take time to execute. Therefore, Angular will move on to other tasks: console.log('LoginStep_2 - Token Verified'); and console.log('LoginStep_3 - SetUserData Finished'); before the getToken() and setUserData() finished.

You can move subscribe to component

In your AuthService

    getToken(user){
        console.log("getToken_Step1 - Hello");

        return this.http.post<any>(this._tokenUrl, user)
        // subscribe moves to component
    }

In your component

this._auth.getToken(this.loginUserData)
  .subscribe(
    res => {

      //getToken_Step2 : SET TOKEN SOMEWHERE
      console.log("getToken_Step2 - Token : ", res.token);
    },
    err => console.log(err)
  )

Basically, you should implement your function call to return either promise or observable (aka async). The AuthService is something like this (adapted from your question):

import {Injectable} from '@angular/core';
import {HttpClient, HttpHeaders} from '@angular/common/http';

import {Observable, of} from 'rxjs';

@Injectable()
export class AuthService {
  constructor(private http: HttpClient) {}

  // private _tokenUrl = "http://localhost:8000/api/auth/token/";

  // This is just a mock URL that returns a response (200 OK)
  private _tokenUrl =
      "https://bikewise.org:443/api/v2/incidents?page=1&proximity_square=100";
  // private getUserInfoUrl = "http://127.0.0.1:8000/api/userInfo/?username=";
  private userInfo: any;

  // can improve with getter/setter instead of exposing directly
  private isLoggedIn: boolean = false;

  isLoggedInObservable: Observable<boolean>;

  getToken(user): Promise<any> {
    console.log("getToken_Step1 - Hello");

    return new Promise(resolve => {

      // this.http.post<any>(this._tokenUrl, user)
      // fake request to make sure always get a response
      this.http.get<any>(this._tokenUrl)
          .subscribe(
              res => {

                // getToken_Step2 : SET TOKEN SOMEWHERE
                console.log("getToken_Step2 - Token : ", "token_1234");

                console.log("getToken_Step3 - Bye");
                // resolve(res.token);
                resolve("token1234");

              },
              err => {
                console.log(err);

                throw new Error('Unauthorized...');

              });
    })
  }

  setUserData(userName) {
    console.log("setUserData_Step1 - Hello");

    // If this is a real request, make sure to implement a promise/observable
    // return
    // this.http.get<any>(this.getUserInfoUrl + userName)

    // setUserData_Step2 : SET USERINFO SOMEWHERE
    console.log("setUserData_Step2 - User Info : ");

    console.log("setUserData_Step3 - Bye");
    this.isLoggedIn = true;
    this.isLoggedInObservable = of(true);
  }

  getUserData() {
    console.log("getUserData_Step1 - Hello");
    return this.userInfo;
  }

  logOut() {
    this.isLoggedInObservable = of(false);
    this.isLoggedIn = false;
  }

  isLogged(): boolean { return this.isLoggedIn; }
}

You also need to setup a AuthGuard for your routing to track whether user is logged in or not.

Here is a working example stackblitz . Open console to track status

Furthermore, you can always cache your user login status in browser localStorage to get better user experience. Again, this is adapted from your code. There are many things can be improved from your code. Let me know if you have further questions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM