0

So I am simply trying to write a component and service that from one view takes the user input and passes it to an api for validation. The problem is that in my component, it's saying that the service essentially has no method login and is coming up undefined. However I've checked and rechecked following Angular.io's documentation very closely but can't get anything to work.

LoginComponent.ts

import { Component, OnInit } from '@angular/core';
import { UserService } from '../../../services/user.service';

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

  constructor(private userService: UserService) {
    console.log('userService', userService);
  }

  ngOnInit() {}

  handleSubmit(data) {
    // https://api-test.sarahlawrence.edu:82/
    this.userService.login(data)
      .subscribe(user => console.log('user', user));
  }
}

user.service.ts

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, of } from 'rxjs/index';
import { catchError, map, tap } from 'rxjs/internal/operators';

const httpOptions = {
  headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};

interface CredsInterface {
  username: string;
  password: string;
};

interface LoggedIn {
  token: string;
}

@Injectable({
  providedIn: 'root'
})
export class UserService {

  private apiUrl = '<apiUrl>';

  constructor(
    private http: HttpClient
  ) { }

  login (creds: CredsInterface): Observable<any> {
    console.log('UserService.login()', creds);

    return this.http.post<any>(`${this.apiUrl}/signin`, creds, {})
      .pipe(
        tap((loggedIn: LoggedIn) => {
          console.log(`Login: ${loggedIn}`);
        }),
        catchError(this.handleError('login()', []))
      );
  }

  /**
   * Handle Http operation that failed.
   * Let the app continue.
   * @param operation - name of the operation that failed
   * @param result - optional value to return as the observable result
   */
  private handleError<T> (operation = 'operation', result?: T) {
    return (error: any): Observable<T> => {

      // TODO: send the error to remote logging infrastructure
      console.error(error); // log to console instead

      // TODO: better job of transforming error for user consumption
      console.log(`${operation} failed: ${error.message}`);

      // Let the app keep running by returning an empty result.
      return of(result as T);
    };
  }
}

I don't understand why I get this error:

Error

So I logged the service out to see the object and weirdly the method is being placed in the prototype:

Prototype

I don't get it, am I doing something wrong?

georgeawg
  • 48,608
  • 13
  • 72
  • 95
Tom Bird
  • 999
  • 3
  • 17
  • 31
  • Do you have a stackblitz? – mwilson Jun 18 '18 at 22:25
  • @mwilson I just went and created one working on getting stuff put into there: https://stackblitz.com/edit/angular-service-issues?file=src/app/components/views/login/login.component.ts – Tom Bird Jun 18 '18 at 22:34
  • I think you forgot to save your changes. It's showing a bunch of errors for missing modules. Probably just a path issue. – mwilson Jun 18 '18 at 22:42
  • @mwilson No thats because there's no easy way to pull all the files into the stack blitz – Tom Bird Jun 18 '18 at 22:47
  • That could be part of the problem. The code you have is basically the starter code so it may be easier to recreate a simple example in stackblitz to see what the issue is. From looking at it, it looks like there's quite a few errors in setup. Notably, it looks like you are referencing a `form-login` component but none is found. On that same component, you are trying to access a property called `onHandleSubmit` that doesn't exist. More than likely what is happening is that the prefix got changed and it's trying to run that directive and can't find it and therefore you're geting the error. – mwilson Jun 18 '18 at 22:55
  • Here is a super basic example of using an angular service and interacting with it through any component. I believe this is what you are trying to do: https://stackblitz.com/edit/angular-7vhjtf?file=src%2Fapp%2Fapp.component.ts – mwilson Jun 18 '18 at 23:02

1 Answers1

4

How do you call that handleSubmit method?

The error says that it can't read login property of undefined which means that this.userService is undefined. The fact that login method is inside prototype is okay. Remember that gets are deep and sets are shallow

I think that you call handleSubmit with some tricky way which makes this to refer other object than you think it is.


I've just saw stackblitz. You pass reference to your function using [onHandleSubmit]="handleSubmit" but when it's executed this is not your LoginComponent anymore.

Add this to component constructor

this.handleSubmit = this.handleSubmit.bind(this)

For more details see this post: Angular pass callback function to child component as @Input

Kamil Chlebek
  • 159
  • 1
  • 6
  • You were right, I basically have a login view and a login form, that way I can reuse the login form in other places. Adding the `.bind(this)` solved my issue! Thanks so much, I'm used to reactjs where I can do: `onHandleSubmit={() => { handleSubmit() }}` Which allows the correct scope the way you are talking about. Thanks for the help!!!! – Tom Bird Jun 18 '18 at 22:53