I got a problem with a service in Angular 16

303 Views Asked by At

I'm working with Angular 16 for a project that involves using balkan family tree , but I got stuck with a problem in a service

This is the function where it emits the value, is using HttpClient from Angular Core:

this.httpClient.get(url, { headers }).subscribe(
          {
            next: res => {
              console.log(res);
              this.apiResponse = res;
              this.cdr.detectChanges();
              this.zone.run(()=>{
                this.specimenData.$specimens.emit(res);
              });
              this.specimens = this.apiResponse;
            },
            error: err => {
              console.log(err);
            }
          }
        );

As you can see i tried to view the content of the response and its defined with the data i need to emit. I also tried to make Angular to detect the changes and do the emit action into the zone, but nothing works! I just got the undefined when i subscribe to the service in this function (this function is on another component):

this.specimenData.$specimens.subscribe(value => { this.data = value });
    if (this.data != undefined) {
      var specimens = [];
      const tree = document.getElementById('tree');
      if (tree) {
        var family = new FamilyTree(tree, {
          nodeBinding: {
            field_0: "name",
            img_0: "img"
          },
        });

        family.load([
          { id: 1, pids: [2], name: "Amber McKenzie", gender: "female", img: "https://cdn.balkan.app/shared/2.jpg" },
          { id: 2, pids: [1], name: "Ava Field", gender: "male", img: "https://cdn.balkan.app/shared/m30/5.jpg" },
          { id: 3, mid: 1, fid: 2, name: "Peter Stevens", gender: "male", img: "https://cdn.balkan.app/shared/m10/2.jpg" },
          { id: 4, mid: 1, fid: 2, name: "Savin Stevens", gender: "male", img: "https://cdn.balkan.app/shared/m10/1.jpg" },
          { id: 5, mid: 1, fid: 2, name: "Emma Stevens", gender: "female", img: "https://cdn.balkan.app/shared/w10/3.jpg" }
        ]);
      }
    } else {
      console.log("im showing from tree component"+this.data);
    }

As you can see when i pass the value from the subscribe method for the service it passes an undefined and the console shows the message in the else way. Is weird because I´m using another services in my app and they are working fine, even some of them are emiting an api response like this one, this is the code for the service in his file:

import { EventEmitter, Injectable } from '@angular/core';

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

  constructor() { }

  $specimens = new EventEmitter<any>();
}

I tried changing the 'Injectable' to 'inject' but didn't work, I also tried to add the service to the providers in NgModule but nothing, if someone have an idea or a clue of whats happening please tell me, if you need more info feel free to ask me for it.

1

There are 1 best solutions below

0
Andres2142 On

A couple of observations that could fix your problem. EventEmitter is used for communication between parent/child components not on services.

If you are handling the application state in a service, use subjects (Subject, BehaviorSubject, AsyncSubject, ReplaySubject) depending on the situation.

You shouldn't subscribe to observables in your service, there are some exceptions but try not to, instead, your service functions should return an observable so any component can react to any changes that might happen in the service (by using next()).

Avoid using var, stick with either let or const depending on if the variable will be changed later.

Use Angular for interacting with the DOM, don't use native javascript, you could make use of ViewChild

Since you are using Angular v16, you can make use of takeUntilDestroyed for handling the subscription if you need to subscribe to an observable manually.

Given these suggestions, you could refactor your current code as the following:

service

@Injectable({...})
export class SpecimensService {
  //Just for demo purposes, I am assuming the data is an array of type yourTypeGoesHere
  private _specimen$ = new BehaviorSubject<yourTypeGoesHere[]>([]);

  fetchSpecimens(): void {
    this._specimen$ = this.get(...);
  }

  getSpecimens(): Observable<yourTypeGoesHere[]> {
    return this._specimen$.asObservable();
  }
}

component HTML

<div #tree id="tree" class="...">
  ...
  ...
</div>

component

import { takeUntilDestroyed } from '@angular/core/rxjs-interop';

@Component({...})
export class MyComponent implements OnInit {
   // this is one way of taking reference from an HTML element
   // again, for demo purposes, I am using a div here
   @ViewChild('tree') tree!: ElementRef<HTMLDivElement>; 
   private _specimenService = inject(SpecimenService);

   ngOnInit(): void {
     // set the data to the service's subject
     this._specimenService.fetchSpecimen();
 
     // retrieve the data
     this._specimenService.getSpecimens()
      .pipe(takeUntilDestroyed())
      .subscribe((data) => {
       const specimens = [];
      
       if (this.tree) {
        //logic goes here...
        //...
       }
     });
   }
}