johan
johan

Reputation: 1012

Angular local scope not available inside observable subscription

I have been struggling with this now for a while and tried many solutions.

Inside my subscribe I cant access ChangeDetectorRef or the NgZone to force an update on my scope.

Why doesn't anything work? Is there any other Angular aware way of doing HTTP requests?

The login component

import {ChangeDetectionStrategy, ChangeDetectorRef, Component, NgZone, OnInit} from '@angular/core';
import {LoginService} from './login.service';
import {Observable} from 'rxjs/Observable';

@Component({
    selector: 'app-login',
    templateUrl: './login.component.html',
    styleUrls: ['./login.component.css'],
    providers: [LoginService]
})
export class LoginComponent implements OnInit {
    zone: NgZone;
    username = '';
    password = '';
    showerror = false;
    errormsg = '';
    rs = {};
    results: any;


    constructor(private _loginService: LoginService, private ref: ChangeDetectorRef) {

    }

    refr = function () {
        console.log('refresh');
        console.log(this.errormsg); // is empty even after HTTP call
        this.ref.markForCheck();
        console.log(this.results);
    };

    login = function () {
        console.log(this.username, this.password);

        this._loginService.login(this.username, this.password).subscribe(function (data) {

            console.log('log me in');
            console.log(data); // the correct data is logged here
            this.errormsg = 'Invalid Username and Password'; // the text is never updated
            this.zone.run(); // zone has no method run
            this.ref.markForCheck(); // ref has no method markForCheck
        });

        // this.results = this._loginService.login(this.username, this.password);
        // this.results.subscribe(rs => this.rs = rs);   
    };

    ngOnInit() {

    }

}

The login service

import {Injectable} from '@angular/core';
import {CONFIG} from '../config';
import {Http, Response} from '@angular/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/catch';

const loginUrl: string = CONFIG.apiserv + 'api.login.php';

@Injectable()
export class LoginService {
    private http;
    constructor(http: Http) {
        this.http = http;
    }

    login = function (username, password): Observable<Response> {
        console.log('login clicked');
        let url = new URLSearchParams();
        url.append('username', username);
        url.append('password', password);
        const fullurl = loginUrl + '?' + url.toString();

        return this.http.get(fullurl)
            .map((response: Response) => response.json())
            .do(data => console.log('All: ' + JSON.stringify(data)))
            .catch(this.handleError);
    };
    handleError = function (error: Response) {
        console.log(error);
        return Observable.throw(error.json().error || 'Server error');
    };
}

Upvotes: 1

Views: 2976

Answers (2)

martin
martin

Reputation: 96939

If you want to bind this to a newly crated scope you need to use =>.

this._loginService.login(this.username, this.password).subscribe(data => {

See: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions

Otherwise, you could assign the this to a variable but it's not necessary if you're using TypeScript or ES6.

var that = this;
this._loginService.login(this.username, this.password).subscribe(function (data) {
    that.zone.run();
    ...

Upvotes: 3

Pengyy
Pengyy

Reputation: 38191

since you are using this, you should use arrow function:

this._loginService.login(this.username, this.password).subscribe(data => {

        console.log('log me in');
        console.log(data); // the correct data is logged here
        this.errormsg = 'Invalid Username and Password'; // the text is never updated
        this.zone.run(); 
        this.ref.markForCheck(); 
    });

Upvotes: 4

Related Questions