2

I am getting the following errors, trying to update a solution to angular 6.

ERROR in ../app/AuthService.ts
      TS2339: Property 'of' does not exist on type 'typeof Observable'.
ERROR in ../app/AuthService.ts
      TS2339: Property 'map' does not exist on type 'Observable<any>'.
ERROR in ../app/AuthService.ts
      TS2339: Property 'map' does not exist on type 'Observable<boolean>'.
ERROR in       TS2339: Property 'map' does not exist on type 'Observable<any>'.

I am using the latest version of rxjs, and the imports (at least on paper) seem correct?

"rxjs": "^6.2.0",

My file is as follows:

import { Injectable } from "@angular/core";
import { NGXLogger } from "ngx-logger";
import { CanActivate, Router } from "@angular/router";
import { HttpClient, HttpHeaders } from "@angular/common/http";
import { Observable } from "rxjs";
import { StorageService } from "./lib/storage/StorageService";
import { map, catchError } from "rxjs/operators";
import { of } from "rxjs";
import * as _ from "lodash";

/**
 * class that represents the access token
 * returned from /connect/token (IdentityServer 4)
 */
export class Token {
    // ReSharper disable once InconsistentNaming
    access_token: string;

    // the user email.
    email: string;
}

@Injectable()
export class AuthService {
    /**
     * the access token.
     */
    token: Token;

    constructor(
        private readonly http: HttpClient,
        private readonly logger: NGXLogger,
        private readonly storage: StorageService
    ) {
    }

    /**
     * return true if the user has a valid token.
     */
    isLoggedIn(): Observable<boolean> {
        return this
            .loadToken()
            .map(_ => this.token && this.token.access_token.length > 0)
            .catch(e => {
                this.logger.debug(e);
                this.token = null;
                return Observable.of(false);
            });
    }

    logout() {
        this.logger.info("logging out");
        this.storage.clear("token");
    }

    /**
     * login, using the supplied email and password.
     * @param email the email address.
     * @param password the password.
     */
    login(email: string, password: string): Promise<Token> {
        this.logger.info(`user ${email} attempting login`);
        const login = {
            username: email,
            password: password,
            grant_type: "password",
            scope: "api",
            client_id: "api",
            client_secret: "secret"
        };
        const headers: HttpHeaders = new HttpHeaders().set("Content-Type", "application/x-www-form-urlencoded");
        const post = this.http.post<Token>(
            "connect/token",
            this.encodeObjectToParams(login),
            { headers: headers });
        return post
            .toPromise()
            .then((_: Token) => this.token = this.saveToken(_));
    }

    register(email: string, password: string, confirmPassword: string): Observable<any> {
        this.logger.info(`user ${email || "<null>"} registration request`);
        const uri = "api/register";
        const registration = {
            email,
            password,
            confirmPassword
        };
        return this.http
            .post<any>(
                uri,
                registration)
            .map(_ => _)
            .catch(e => {
                this.logger.debug(`exception :: ${uri} :: `, e);
                return Observable.throw(e.error);
            });
    }

    /**
     * encode the supplied object to a set of form variables.
     * @param instance the object to encode.
     */
    private encodeObjectToParams(instance: any): string {
        return Object.keys(instance)
            .map(key => encodeURIComponent(key) + "=" + encodeURIComponent(instance[key]))
            .join("&");
    }

    /**
     * save the supplied token
     */
    private saveToken = (token: Token): Token => this.storage.save("token", token);

    /**
     * attempt to load the token from local storage,
     * and test that it is valid.
     */
    private loadToken() {
        this.logger.debug("loading 'token'");
        const token = this.storage.load<Token>("token");
        return this.testToken(token)
            .map(_ => this.token = token);
    }

    /**
     * test that the supplied login token works.
     * @param token the token to test.
     */
    isTokenEmailValid = token => this.http
            .get<any>("api/user")
            .map((response: any) => response !== null && response.isAuthenticated);
    testTokenThrottle = _.throttle(token => {
        this.logger.info(`testing token :: ${JSON.stringify(token)}`);
        return this.isTokenEmailValid(token);
    }, 300000);
    private testToken(token: Token): Observable<boolean> {
        const valid = this.testTokenThrottle(token) || this.isTokenEmailValid(token);
        return valid;
    }
}

/**
 * Authorisation guard, to ensure that a user
 * not logged in is redirected to the login page.
 */
@Injectable()
export class AuthGuard implements CanActivate {

    constructor(
        private readonly authService: AuthService,
        private readonly logger: NGXLogger,
        private readonly router: Router) { }

    canActivate(): Observable<boolean> {
        this.logger.debug("authorisation guard testing login status");
        return this.authService.isLoggedIn().map(_ => {
            this.logger.info(`authorisation guard :: login status ${_}`);
            if (_)
                return true;
            this.router.navigate(["login"]);
            return false;
        }).catch(_ => {
            this.logger.info(`authorisation guard :: login error ${_}`);
            this.router.navigate(["login"]);
            return Observable.of(false);
        });
    }
}
gdoron
  • 147,333
  • 58
  • 291
  • 367
Jim
  • 14,952
  • 15
  • 80
  • 167
  • 1
    You should be using rxjs operators with pipe and `of` like a standalone function – yurzui Jun 03 '18 at 06:55
  • 1
    As you are trying to update to angular 6, RxJS v6 has major changes from v5 so use following commands to automatically refactor TypeScript code npm install -g rxjs-tslint rxjs-5-to-6-migrate -p src/tsconfig.app.json Refer this link for details https://stackoverflow.com/questions/48970553/want-to-upgrade-project-from-angular-v5-to-angular-v6/49474334#49474334 – Ashish Jain Jun 03 '18 at 07:19

2 Answers2

3

From RxJs 6.0 you've to use pipe() operator which takes infinte number of other operators that will apply the Observable .

The error message Property 'map' does not exist on type 'Observable<any>' is meaningful, map is not exists in Observable.

You can't chain Observable.map(...).catch(...).

The new syntax is Observable.pipe( map(...), catchError(...) )

Example:

import { map, catchError } from "rxjs/operators";
import { of } from "rxjs";


isLoggedIn(): Observable<boolean> {
    return this
       .loadToken()
       .pipe(
           map(_ => this.token && this.token.access_token.length > 0),
           catchError(e => {
               this.logger.debug(e);
               this.token = null;
               return of(false);
             })
        )
 }

One Refernece: https://www.academind.com/learn/javascript/rxjs-6-what-changed/#operators-update-path

Ritwick Dey
  • 18,464
  • 3
  • 24
  • 37
1
// rxjs 5    
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/map'

OR

import { of } from 'rxjs/observable/of';
import { map } from 'rxjs/operator/map'

AND

// rxjs 6 alpha
import { Observable } from 'rxjs';
import { of } from 'rxjs';
import { map } from 'rxjs/operators';
Fateme Fazli
  • 11,582
  • 2
  • 31
  • 48