1

Is it even possible to let a service call an component Method?

myapp.component

export class MyAppComponent {
  public value;
  ...
  public setValue(payload){
    this.value = payload;
  }
}

myapp.service

@Injectable()
export class MyAppService {
  private myAppComponent: MyAppComponent;
  private apiClientService: ApiClientService

  // ...
    After i make an PUT http call, the body from the response is my new "value"
  // ...

    putValue(payload: JSON){

    return this.apiClientService.putAPIObject(payload).then((response) => {
      this.myAppComponent.setValue(response);
    }); 
  }
}

This results in an ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'setValue' of undefined.

Can someone explain what im doing wrong? Thanks in advance.

EDIT: Since people complain about my approach, im totally fine to start from scratch if someone can explain me what is the best way to handle this problem.

I get values from an api, change them and i put them back to the api. I dont want to make a get call again, so i get the new data i want in the response of the Put call.

The call goes from component --> component service --> apiclient service

I guess the problem is that i have an extra service between the start and end point.

EDIT 2: I tried to avoid the component service and maked it work for me with only component --> apiclient service

Even this soultion is working for me at the moment I kind of dislike it, because I have to Copy and Paste a lot of code for the Same "Operation" with other objects from my api. For example I maked it work for the Picture Component, but I also need this for my Movie Component. Usally its a bad thing if I write the same code often in a project, or not?

sHamann
  • 789
  • 3
  • 10
  • 36
  • Possible, yes, but how are you getting a reference to the component? – stealththeninja Nov 21 '17 at 16:31
  • @stealththeninja doesnt `private myAppComponent: MyAppComponent;` do the trick? – sHamann Nov 21 '17 at 16:33
  • No, I am not sure why you think it would be injected into your service? – Igor Nov 21 '17 at 16:35
  • what is the utility of calling the method in the service ? as @stealththeninja said where is the reference of your component ? so you can see changes in the front... – Mohamed Ali RACHID Nov 21 '17 at 16:36
  • i think what you have to do is the opposite , you have to call the service's method `putValue` from your component , then call `setValue` – Mohamed Ali RACHID Nov 21 '17 at 16:42
  • Im calling from my component an method from the component service, which also calls an method from the apiclientservice. I Already tried to pass the response from the ApiClientService to my component, which resolve in the following error `ZoneAwarePromise {__zone_symbol__state: null, __zone_symbol__value: Array[0]}`. If i catch the response on my component service it works. So i tried to send the Respone to my component over my service – sHamann Nov 21 '17 at 16:51
  • I added more Details to the question if someone is interested to have a look on it again. – sHamann Nov 21 '17 at 17:57
  • You've edited your question to the point where it's no longer clear what you're asking. Are you still trying to get an update an api call to the component? – stealththeninja Nov 21 '17 at 20:56

1 Answers1

4

There are at least a couple ways to solve this, but hopefully this gives you a start. Open to feedback and corrections.

Use an Observable

Let the service own knowledge of the value changes and emit changes. The component listens to an EventEmitter on1 the service to react to value changes. (See also: Creating and returning Observable from Angular 2 Service)

MyAppService

import { Subject } from 'rxjs/Subject';

@Injectable()
export class MyAppService {
  private valueSource = new Subject<any>();
  public valueUpdate$ = this.valueSource.asObservable();

  putValue(payload: JSON){
    return this.apiClientService.putAPIObject(payload).then((response) => {
      /** here **/
      this.valueUpdate$.next(response);
    }); 
  }
}

MyAppComponent

export class MyAppComponent {
  public value;
  private valueSubscription;

  constructor(private _myAppService: MyAppService) {}

  ngOnInit() {
    /** and here **/
    this._myAppService.valueUpdate$.subscribe((p) => this.setValue(p));
  }
  ...
  public setValue(payload){
    this.value = payload;
  }
}

Register the component

Answering the original question, the idea is to register the component with the service so that it can call the component as needed. You could pull a references through dependency injection but wouldn't recommend it (e.g. what if your original component reference is destroyed?)

MyAppService

@Injectable()
export class MyAppService {
  private myAppComponent: MyAppComponent;

  /** here **/
  registerMyApp(myApp: MyAppComponent) {
    this.myAppComponent = myApp;
  }

  putValue(payload: JSON){
    return this.apiClientService.putAPIObject(payload).then((response) => {
      this.myAppComponent.setValue(response);
    }); 
  }
}

MyAppComponent

export class MyAppComponent {
  public value;

  /** and here **/
  constructor(myAppService: MyAppService) {
    myAppService.registerMyApp(this);
  }
  ...
  public setValue(payload){
    this.value = payload;
  }
}
  1. Thanks AJT_82 for noting that Angular does not want developers using EventEmitters on the service: What is the proper use of an EventEmitter?.
stealththeninja
  • 3,576
  • 1
  • 26
  • 44