1
votes

I'm new to observable and im having a super tough time trying to understand how it works!

I have some code that is fetching a JSON file for data but im not getting it on my ngOninit, I'm assuming that its because the code is asynchronous. I want to setup an observable to subscribe to the data once it gets in, but im completely new to observable.

Here is the code;

fetch('./assets/data/vendor.json').then(res => res.json())
      .then(data => {
        this.vendorData = data.vendors;
        this.vendorService.setVendors(this.vendorData);
      });
  }
  ngOnInit() {
    this.filteredVendor = this.vendorService.getVendors();
    this.isfiltered = true;
    console.log(this.filteredVendor)
  }

Any Ideas on how i can accomplish this and any good tutorials or lessons you can point me to for learning about Observables?

Thanks for your help.

2

2 Answers

3
votes

After your fetch method you seem to use .then suggesting you have a promise and not an observable. I suggest you convert your promise to an observable with the from method from Rxjs inside your service. Alternatively you could create a subject out of it.

Assuming this.vendorService.getVendors() returns an observable, you will have to subscribe to it like also @AliF50 suggested in his answer:

//Inside your get method in your service:

public getVendors(): Vendor[] {
  return from(fetch('./assets/data/vendor.json')).pipe(map(
    result => result.json()
  ));
}

Now you will get an observable from your call to getVendors().

// it is good practice to use a dollar sign at the end of a variable  when its value is an observable
public ngOnInit() {
  const vendors$ = this.vendorService.getVendors(); 
  vendors$.subscribe((vendors: Vendor[]) => {
    // The argument defined assuming the vendors observable resolves in an array of Vendor
    // this.filteredVendor will be set when the observable emits the result
    this.filteredVendor = vendors;
  })
}

Suggestion:

Because you are new with observables in Angular, I will suggest a solution that could be a nice improvement for your application:

I you use observables and Angular, you can also look into the resolve functionality of the router. Read more in the documentation here.

Create a VendorResolver for your vendors:

@Injectable({ providedIn: 'root' })
export class VendorResolver implements Resolve<Vendor> {
  constructor(private vendorService: VendorService) {}

  resolve(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<Vendor[]> {
    return this.vendorService.getVendors();
  }
}

Inside your VendorService:

public getVendors(): Observable<Vendor[]> {
  // TODO: get fetch method available in the service, not sure where it comes from 
  // create a fetch$ observable using RxJs from (needs to be imported)
  // from will convert your promise to an observable
  const fetch$ = from(fetch('./assets/data/vendor.json'));
  // pipe and map will map the observable result, return desired vendors from the map
  return fetch$.pipe(map(result => {
    const data = result.json();
    return data.vendors;
  }));
}

Now you can do in your route:

@NgModule({
  imports: [
    RouterModule.forRoot([
      {
        path: 'vendors',
        component: VendorComponent,
        resolve: {
          vendors: VendorResolver
        }
      }
    ])
  ],
  exports: [RouterModule]
})
export class AppRoutingModule {}

When routing to 'vendors' route the router will resolve your vendors using the VendorsResolver automatically. Be aware that if it doesn't resolve the routing will not complete.

Inside your VendorComponent you can access the vendors directly from the route snapshot data:

class VendorsComponent {
  constructor(private route: ActivatedRoute) {
  }

  public ngOnInit() {
    this.filteredVendor = this.route.snapshot.data.vendors;
  }
}

This makes the code in your component very minimal; no need to inject anything else than ActivatedRoute for resolving your vendors.

Note: code above was written here and simplified a bit (for example the component class is far from complete) and I skipped the import statements. I tried to be as accurate and complete as possible, but leave a comment if you have questions or run into issues then I can add some details where necessary.

1
votes

Try:

ngOnInit() {
  this.vendorService.getVendors().subscribe(
    venders => this.filteredVendor = venders;
    this.isFiltered = true;
    console.log(this.filteredVendor);
  );
}

As for as learning it, I would just keep practicing with it and looking up how to do things. These tutorials might be outdated but they were good for me (https://egghead.io/courses/introduction-to-reactive-programming?fbclid=IwAR1I2--NX82Lg1iCvzLIx4vPtEw_PIbWid7eqboBT9RWI_h0G2zfE3i6hDA) but the syntax could be a bit outdated but to learn the new syntax, it is not that difficult.

Think of observables as a stream of data that can be modified with operators. To learn it properly, you will have to get your hands dirty and keep trying to solve problems related with it. Continue posting on Stackoverflow when you're stuck or look at other people's solutions.