简体   繁体   中英

ERROR TypeError: You provided 'null' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable

I searched everywhere for an answer but I couldn't get out this issue. I found that many people are having this issues with Observables in Angular but usually it was just a syntax error. I tried many things (I will list them below) but I'm confused. I don't know why it does like this.

ERROR:

ERROR TypeError: You provided 'null' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.

That's not a functionality error. In fact, it works perfectly with my expectations, but it throws an error to the console.

EXPECTATION:
It's just an ngIf directive that manages the view of a menu content. If the user is logged, it should view certain menu-items (for ex. 'My profile', etc...), if not, it should only see 'Login' and 'Signup' buttons. It actually works this kind of system, but I receive a console error.

WHAT I TRIED:
Googling around I tried many things. The one listed below are only few of them, but at some point I was just trying without knowing really how to solve the problem.

In the first try (which I will not list here because it was pretty long), I tried to switchMap the angular fire auth state to get the logged status, then I setted this.appUser$ to this.userService.get(user.uid), to return user data from the database. This will be the final result, but after getting the error I simplified by a lot the code to find out what can be the issue with that, hoping that it was easier. In fact, I didn't solved anything in 3 days of tries.

  1. navbar.component.html

<mat-menu #userMenu="matMenu">
  <ng-template matMenuContent >
    <div class="menuContent" *ngIf="(appUser$ | async) as user; else notLoggedMenuitems">
      <div mat-menu-item>
        <div class="row no-gutters ml-2 mr-2">
          <div class="col-auto"><img [src]="user.photoURL" class="user-profile-img" alt="Profile image"></div>
          <div class="col"><p class="mat-body-strong mb-0 ml-3" style="line-height: 350%;"><b>{{ user.displayName }}</b></p></div>
        </div>
      </div>
      <mat-divider class="mt-2"></mat-divider>
      <div mat-menu-item>...</div>
      <div mat-menu-item>...</div>
      <mat-divider></mat-divider>
      <div mat-menu-item (click)="logout()">Logout</div>
    </div>

    <ng-template #notLoggedMenuitems>
      <div mat-menu-item routerLink="/auth/login">Login</div>
      <div mat-menu-item routerLink="/auth/signup">Signup</div>
    </ng-template>
  </ng-template>
</mat-menu>
  1. navbar.component.ts

export class NavbarComponent implements OnInit {

  appUser$: Observable<firebase.User>;

  constructor(private auth: AngularFireAuth) {
    this.appUser$ = this.auth.user;
  }

  ngOnInit(): any {
  }

  logout(): void {
    this.auth.signOut();
  }

}
  1. navbar.component.html
    I changed the ngIf directive to the one below, removing the else block. I tried even with other operators like " ! " and so on, but nothing changed...

*ngIf="(appUser$ | async)"
  1. navbar.component.ts

export class NavbarComponent implements OnInit {

  appUser$: Observable<firebase.User> = this.auth.user;

  constructor(private auth: AngularFireAuth) {}

  ngOnInit(): any {
  }

  logout(): void {
    this.auth.signOut();
  }

}

CONCLUSION:
How can I do this? I don't know why typescript throws this error.

UPDATE 1:
Updated navbar.component.html

<mat-menu #userMenu="matMenu">
  <ng-template matMenuContent >
    <div class="menuContent" *ngIf="(appUser$ | async) as user; else notLoggedMenuitems">
      <div mat-menu-item>
        <div class="row no-gutters ml-2 mr-2">
          <div class="col-auto"><img [src]="user.details.imgUrl" class="user-profile-img" alt="Profile image"></div>
          <div class="col"><p class="mat-body-strong mb-0 ml-3" style="line-height: 350%;"><b>{{ user.name }}</b></p></div>
        </div>
      </div>
      <mat-divider class="mt-2"></mat-divider>
      <ng-container *ngIf="user.roles.admin">
        <div mat-menu-item><mat-icon>local_bar</mat-icon>Manage Drinks</div>
          <div mat-menu-item><mat-icon>note_add</mat-icon>Add Drinks</div>
          <mat-divider></mat-divider>
      </ng-container>
      <div mat-menu-item><mat-icon>settings</mat-icon>My Profile</div>
      <div mat-menu-item><mat-icon>wine_bar</mat-icon>My Drinks</div>
      <div mat-menu-item><mat-icon>favorite</mat-icon>Wishlist</div>
      <mat-divider></mat-divider>
      <div mat-menu-item (click)="logout()">Logout</div>
    </div>

    <ng-template #notLoggedMenuitems>
      <div mat-menu-item routerLink="/auth/login">Login</div>
      <div mat-menu-item routerLink="/auth/signup">Signup</div>
    </ng-template>
  </ng-template>
</mat-menu>

Updated navbar.component.ts

import { Component, OnInit } from '@angular/core';
import { AngularFireAuth } from '@angular/fire/auth';
import { Observable } from 'rxjs';

import { AuthenticationService } from './../../../auth/authentication/authentication.service';
import { User } from './../../../auth/authentication/models/user.model';



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

  appUser$: Observable<User>;

  constructor(private authService: AuthenticationService, private auth: AngularFireAuth) {
  }

  ngOnInit(): any {
    this.appUser$ = this.authService.user$;
    this.appUser$.subscribe(res => console.log('in app user: ', res));
  }

  logout(): void {
    this.auth.signOut();
  }

}

Updated authentication.service.ts

private initUser(): void {
  this.user$ = this.afAuth.authState.pipe(
    switchMap(user => {
      if (user) {
        return this.db.doc$<User>(`users/${user.uid}`);
      } else {
        return undefined;
      }
    })
  );
}

UPDATE 2:
Updated authentication.service.ts

private initUser(): void {
  this.user$ = this.afAuth.authState.pipe(
    switchMap(user => {
      if (user) {
        return this.db.doc$<User>(`users/${user.uid}`);
      } else {
        return new Observable<User>();
      }
    })
  );
}

Now I don't see the error anymore. But the user image doesn't shows well... I don't know if that's a good method to do things though.

I think you almost got it.
What is not correct is the way you handle your user$ in your service. You should always return an observable in a switchMap operator.
To handle the case where there's no user login, you can use of() . It's an observable that emits the value passed in param and complete right after that.
Regarding the database your using, are you using firestore or the realtime database ? To request your data, in both cases, you need to end your call with valueChanges to return an observable.
I asume you are using firestore since you called the doc method:

private initUser(): void {
  this.user$ = this.afAuth.authState.pipe(
    switchMap(user => {
      if (user) {
        // return an observable<User>
        return this.db.doc<User>(`users/${user.uid}`).valueChanges();
      } else {
        return of(null);
      }
    })
  );
}

Try this, it should work.

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.

Related Question TypeError: You provided an invalid object where a stream was expected. You can provide an Observable, Promise, Array, or Iterable TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable ERROR TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable in Angular Services TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable when deploying angular Angular5 : TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable. at <Jasmine> @ngrx/effects gives TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable Angular: You provided an invalid object where a stream was expected. You can provide an Observable, Promise, Array, or Iterable xplat You provided an invalid object where a stream was expected. You can provide an Observable, Promise, Array, or Iterable
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM