0

I have the following function to return data from an Angular 9 service:

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

constructor(private http: HttpClient) { }

public getFunds(): Observable<any> {
  // could do a loader here, or in an interceptor - same for the post, put and delete
  let data = this.http.get<any>("https://localhost:44337/security/").pipe(   
    map(response => response)
  );
return data;

} }

The service returns data as expected. I call it like this:

export class AppComponent {   
funds$!: Observable<any>; // "!" turns off check to make sure vars are initialized, not good tho
constructor(private securityService: SecurityService) {}

ngOnInit() {
    this.funds$ = this.securityService.getFunds();
    this.securityService.getFunds().subscribe(f => (this.funds$ = f));
  }
}

In my html, I try to use the data like this:

 <tr *ngFor="let f of funds$">
  <td>{{ f.id }}</td>
  <td>{{ f.name }}</td>
  <td>{{ f.assets }}</td>
</tr>

I get this error: TS2322: Type 'Observable' is not assignable to type 'any[] | Iterable | (Iterable & any[]) | (any[] & Iterable) | null | undefined'. Type 'Observable' is not assignable to type 'any[] & Iterable'. Type 'Observable' is not assignable to type 'any[]'.

This used to work a few years ago. Evidently Angular 9 changed something...

Scott
  • 2,456
  • 3
  • 32
  • 54

3 Answers3

1

Use async pipe and change your template to :-

<ng-container *ngIf="funds$">
    <tr *ngFor="let f of funds$|async">
      <td>{{ f.id }}</td>
      <td>{{ f.name }}</td>
      <td>{{ f.assets }}</td>
    </tr>
</ng-container>
Aakash Garg
  • 10,649
  • 2
  • 7
  • 25
  • Well, I get data back from the service, but nothing on the page. I see this error in the console: Error: InvalidPipeArgument: '[object Object],[object Object]' for pipe 'AsyncPipe'. Do I perhaps need to change the service in someway? I'm looking into the error now in any case. Thanks. – Scott Apr 30 '21 at 16:39
  • The service is called twice. The first returns data, but the second does not. Same results, same error. :( – Scott Apr 30 '21 at 16:53
  • remove seconds statement from ngoninit i.e. :- this.securityService.getFunds().subscribe(f => (this.funds$ = f)); or keep your original template and remove the first statement from ngoninit. – Aakash Garg Apr 30 '21 at 16:55
1

When we use an async pipe in the HTML template, we do not have to subscribe to the observable being referred in the async pipe. The async pipe subscribes it for us behind the scenes. Hence, in the component, we need to only build the observable.

Also, before accessing the async pipe data, one needs to make sure that the stream is available using *ngIf.

Going on these lines, there are a couple of changes -

In your component -

export class AppComponent {   
  funds$: Observable<any>;
  constructor(private securityService: SecurityService) {}

  ngOnInit() {
      this.funds$ = this.securityService.getFunds(); // Only this line is enough
  }
}

In your HTML -

<ng-container *ngIf="funds$">
    <tr *ngFor="let f of funds$|async">
      <td>{{ f.id }}</td>
      <td>{{ f.name }}</td>
      <td>{{ f.assets }}</td>
    </tr>
</ng-container>
Krantisinh
  • 1,579
  • 13
  • 16
0

As you are subscribing to observable which return by getFunds method which eventually returns the data. So no need to define funds$ as Observable. Below code might help you.

funds: any[] = [];

ngOnInit() {
    this.securityService.getFunds().subscribe(f => (this.funds = f));
  }
}

in HTML use funds variable within ngFor.

Pujan Shah
  • 775
  • 10
  • 15