Search code examples
angular-materialangular8angular-reactive-formsangular-servicesangular-forms

EXCEPTION in Angular 8: Can't resolve all parameters Angular 8


I've built a app in Angular 8, but I have encountered a strange issue where I cannot inject a service into one of my components. Basically i want to show all my login/register means under one page without routing by just managing fields. But this error is coming up again and again.

Uncaught Error: Can't resolve all parameters for AuthTestComponent: ([object Object], [object Object], ?).

My auth.service.ts file:-

import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';
import { Router } from '@angular/router'
@Injectable({
  providedIn: 'root'
})
export class AuthService {
  authUrl = "API"
  constructor(private http: HttpClient, private router: Router) { }

  registerUser(email: string, password: string) {
    return this.http.post<any>(this.authUrl + "user-register", {
      email: email,
      password: password,
      // c_password: password,
      token: true
    }).pipe(catchError(this.handleError))
  }

  LoginUser(email: string, password: string) {
    return this.http.post<any>(this.authUrl + "user-login", {
      email: email,
      password: password,
      access_token: true
    }).pipe(catchError(this.handleError))
  }

  loggedIn() {
    return !!localStorage.getItem('access_token')
  }

  getToken() {
    return localStorage.getItem('access_token')
  }

  logoutUser() {
    localStorage.removeItem('access_token');
    this.router.navigate(['login'])
  }

  private handleError(errorRes: HttpErrorResponse) {
    let errorMessage = "An Unkown error occured!";
    if (!errorRes.error.error.errors || !errorRes.error.error.email[0]) {
      return throwError(errorMessage)
    }
    switch (errorRes.error.error.email[0]) {
      case 'The email has already been taken.':
        errorMessage = 'The email already exists!'
        break;
    }
    // switch (errorRes.error.error[0]) {
    //   case 'Password or Email not matched':
    //     errorMessage = 'Please Sign Up to continue!'
    //   case 'INVALID_PASSWORD':
    //     errorMessage = 'This password is not correct.'
    // }
    return throwError(errorMessage);
  }

  sendPasswordResetLink(email: string) {
    return this.http.post<any>(this.authUrl + "updatepassword", {
      email: email
    }).pipe(catchError(this.handleError))
  }
}

My component file:-

import { Component, Inject } from '@angular/core';
import { FormGroup, FormControl, Validators } from '@angular/forms';
// import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { $animations } from './login-animations';
import { $providers } from './login-providers';
import { $pages } from './login-pages';
import { from } from 'rxjs';
import { AuthService } from '../services/auth/auth.service';
import { Router } from '@angular/router';

export type loginAction = 'register' | 'signIn' | 'forgotPassword' | 'signOut';

@Component({
  selector: 'app-auth-test',
  templateUrl: './auth-test.component.html',
  styleUrls: ['./auth-test.component.css'],
  animations: $animations
})

export class AuthTestComponent {
  readonly providers = $providers;
  private pages = $pages;
  private page: loginAction;
  // private code: string;
  readonly form: FormGroup;
  // private password2: FormControl;
  private email: FormControl;
  private password: FormControl;
  public error = null;
  // private newPassword: FormControl;
  isLoading = false;
  public progress = false;

  constructor(private auth: AuthService, private router: Router, private action: loginAction) {
    // Form controls
    // this.password2 = new FormControl(null, Validators.required);
    this.email = new FormControl(null, [Validators.required, Validators.email]);
    this.password = new FormControl(null, Validators.required);
    // this.newPassword = new FormControl(null, Validators.required);

    // Empty form group
    this.form = new FormGroup({});

    // Populates the form according to the page
    this.switchPage(this.page = action);
  }
  get currentPage() { return this.pages[this.page || 'signIn']; }

  private switchPage(page: loginAction) {

    // Removes all the controls from the form group
    Object.keys(this.form.controls).forEach(control => {
      this.form.removeControl(control);
    });

    // Add the relevant controls to the form according to selected page
    switch (this.page = page) {

      case 'register':
        this.form.addControl('email', this.email);
        this.form.addControl('password', this.password);
        // this.form.addControl('password2', this.password2);
        break;

      default:
      // case 'signIn':
      //   this.form.addControl('email', this.email);
      //   this.form.addControl('password', this.password);
      //   break;

      // case 'forgotPassword':
      //   this.form.addControl('email', this.email);
      //   break;
      /*
            case 'resetPassword':
            this.form.addControl('newPassword', this.newPassword);
            break;
      */
    }

  }
  // private showError(error: string) {

  //   this.error = error;
  //   this.progress = false;
  //   setTimeout(() => this.error = null, 5000);
  // }

  public activate(action: loginAction) {

    this.progress = true;

    switch (action) {

      default:
      // case 'signIn':
      //   this.signIn(this.email.value, this.password.value);
      //   break;

      case 'register':
        this.registerNew(this.email.value, this.password.value);
        break;

      // case 'forgotPassword':
      //   this.forgotPassword(this.email.value);
      //   break;
      /*
            case 'resetPassword':
            this.resetPassword( this.code, this.newPassword.value );
            break;
      */
    }
  }
  private registerNew(email: string, password: string) {
    // Registering a new user with a email/password
    this.auth.registerUser(email, password).subscribe(
      res => {
        console.log(res);
        localStorage.setItem('token', res.token)
        this.isLoading = false;
        this.router.navigate(['login']);
      },
      errorMessage => {
        console.log(errorMessage);
        this.error = errorMessage;
        this.isLoading = false;
      }
    );
  }
}

HTML form file -:

     <div [@vanish]="page">
     <h1 class="mat-title">{{ currentPage.title }}</h1>
     <p class="mat-small">{{ currentPage.message }}</p>
 </div>

 <form [formGroup]="form" fxLayout="column" fxLayoutAlign="space-around stretch" fxLayoutGap="10px"
     (ngSubmit)="activate(page)" *ngIf="page !== 'promptEmail' && page !== 'verifyEmail' && page !== 'recoverEmail'">

     <!-- ERROR MESSAGE -->
     <mat-error *ngIf="error" @inflate>{{ error }}</mat-error>

     <!-- NAME
     <mat-form-field appearance="legacy" *ngIf="form.contains('confirm')" @inflate>
         <mat-label>Full name</mat-label>
         <input matInput formControlName="confirm">
         <mat-error *ngIf="form.controls.confirm.errors?.required">
             Please specify your name here
         </mat-error>
     </mat-form-field> -->

     <!-- EMAIL -->
     <mat-form-field appearance="legacy" *ngIf="form.contains('email')" @inflate>
         <mat-label>Email</mat-label>
         <input matInput formControlName="email">
         <mat-error *ngIf="form.controls.email.errors?.required">
             Please specify an email address
         </mat-error>
         <mat-error *ngIf="form.controls.email.errors?.email">
             Ooops! it looks like this is not a valid email
         </mat-error>
     </mat-form-field>

     <!-- PASSWORD -->
     <mat-form-field appearance="legacy" *ngIf="form.contains('password')" @inflate>
         <mat-label>Password</mat-label>
         <input matInput [type]="hidePassword ? 'password' : 'text'" formControlName="password">
         <mat-icon matSuffix (click)="hidePassword = !hidePassword">
             {{ hidePassword ? 'visibility_off' : 'visibility'}}
         </mat-icon>
         <mat-error *ngIf="form.controls.password.errors?.required">
             A password is required
         </mat-error>

     </mat-form-field>

     <!-- confirm PASSWORD -->
     <mat-form-field appearance="legacy" *ngIf="form.contains('password')" @inflate>
         <mat-label>Password</mat-label>
         <input matInput [type]="hidePassword ? 'password' : 'text'" formControlName="password2">
         <mat-icon matSuffix (click)="hidePassword = !hidePassword">
             {{ hidePassword ? 'visibility_off' : 'visibility'}}
         </mat-icon>
         <mat-error *ngIf="form.controls.password2.errors?.required">
             A password is required
         </mat-error>
         <mat-hint class="mat-link" align="end" (click)="switchPage('forgotPassword')" *ngIf="page == 'signIn'"
             @inflate>Forgot password?</mat-hint>
     </mat-form-field>



     <!-- NEW EMAIL -->
     <!-- <mat-form-field appearance="legacy" *ngIf="form.contains('newEmail')" @inflate>
         <mat-label>New email</mat-label>
         <input matInput formControlName="newEmail">
         <mat-error *ngIf="form.controls.newEmail.errors?.required">
             A new email is required
         </mat-error>
         <mat-error *ngIf="form.controls.newEmail.errors?.email">
             This email looks wrong
         </mat-error>
     </mat-form-field> -->

     <!-- NEW PASSWORD -->
     <!-- <mat-form-field appearance="legacy" *ngIf="form.contains('newPassword')" @inflate>
         <mat-label>New password</mat-label>
         <input matInput formControlName="newPassword">
         <mat-error *ngIf="form.controls.newPassword.errors?.required">
             A new password is required
         </mat-error>
     </mat-form-field> -->

     <!-- ACTION BUTTON -->
     <button mat-stroked-button color="primary" type="submit" [disabled]="!form.valid" class="btn">
         {{ currentPage.caption }}
     </button>

     <mat-progress-bar *ngIf="progress" mode="indeterminate" @inflate></mat-progress-bar>

 </form>

 <p class="mat-small" *ngIf="page == 'signIn'">
     Are you a new user? <span class="mat-link" (click)="switchPage('register')">Register</span>
 </p>

 <p class="mat-small" *ngIf="page === 'register' || page === 'forgotPassword'">
     Already have login and password? <span class="mat-link" (click)="switchPage('signIn')">Sign-in</span>
 </p>

 <!-- SIGN-IN PROVIDERS -->
 <div fxLayout="column" fxLayoutAlign="center center" *ngIf="page == 'signIn'" @inflate>

     <p class="mat-small">or sign-in with:</p>

     <div fxLayout="row wrap" fxLayoutAlign="center center" fxLayoutGap="10px">

         <button mat-icon-button *ngFor="let p of providers" (click)="signInWith(p.name)">
             <mat-icon class="providers" [fontSet]="p.icon.split(':')[0]" [fontIcon]="p.icon.split(':')[1]"
                 [ngStyle]="{ color: p.color }">
             </mat-icon>
         </button>
     </div>
 </div>

Solution

  • The error trace provides a clue about what's going on: ([object Object], [object Object], ?). In Angular, it's objects all the way down (even a module is just some syntatical sugar around an object).

    Since the first items in the error messages are objects, we can assume that dependency injection was successful for first two services referenced in the constructor. The ? in the error message (the third position) indicates there may be an issue with injecting the final dependency, i.e., private action: loginAction.

    The Angular guide on dependency injection (DI) provides some context for how it operates by calling out that "dependencies are services or objects". The error you're seeing might be caused by trying to inject a non-injectable entity, i.e., a string.

    It might be helpful to see how the component is implemented in your code, e.g., the corresponding HTML. Aside from the InjectionToken example provided in one of the other responses, you might also investigate whether an Input property (see Angular Input on the component allows you to pass the necessary page action).