I'm having 2 services in my angular frontend, one for API calls and one for sharing data two different components. So the second service is using the API service.
If I only use the API service and subscribe my Observables inside my component, everything works fine(view in xxx.component.html).
So if I declare the two services as providers inside app.module and inject the API Service inside the sharing service, it won't work any more.
Using debugger, I always get variable "tmp" not defined in settings.service.ts
I know, I could do this also with also with @Input for child components, but i think using a service is a much nicer way also for decoupling components.
Any suggestions :)?
Below my Code:
api.service.ts
export class ApiService {
API_URL = 'https://localhost:44381/api/v1';
constructor(private httpClient: HttpClient) { }
/** GET settings from API*/
getSettings(): Observable<Setting[]> {
return this.httpClient.get<Setting[]>(this.API_URL + '/settings')
.pipe(
catchError(this.handleError('getSettings', [])));
}
}
settings.service.ts
export class SettingsService {
tmp: Setting[];
constructor(private apiService: ApiService) { }
getSettings(): void {
this.apiService.getSettings()
.subscribe(settings =>
this.tmp = settings);
}
getData(): Setting[] {
this.getSettings();
return this.tmp;
}
}
settings.component.ts
export class SettingsComponent implements OnInit {
settings: Setting[];
constructor(private settingService: SettingsService) { }
// Load Setting while starting
ngOnInit() {
// this.getSettings();
this.settings = this.settingService.getData();
}
// old code when using only API service which works..
/*getSettings(): void {
this.apiService.getSettings()
.subscribe(settings => this.settings = settings);
}*/
}
settings.component.hmtl
<div>
<table>
<tr>
<th>keyname</th>
<th>value</th>
<th>defaultValue</th>
<th align="right">text</th>
</tr>
<tr *ngFor="let s of settings">
<td>{{s.keyName}}</td>
<td>{{s.wert}}</td>
<td>{{s.defaultValue}}</td>
<td align="right">{{s.description}}</td>
</tr>
</table>
</div>
Your problem resides into async functionality. The
subscribe
method is used to wait for an async operation. That means that when you execute a particular request, for example a remote request, you don't want to stop the entire flow execution waiting for the response. But rather you want to make the request and execute all other code in the meanwhile that the server is processing your request and is sending it's response.So let's analyze what is the flow of this code execution
this.getSettings()
executes a request and subscribe for it's responsethis.tmp
, that is still undefinedthis.tmp
variableIf you have splitted your logic into 2 services to manage syncronism problems, consider to use Subject
Possible solution with Subject
Service
Component
As you can see
SettingsComponent
subscribe tosettings$
Observable
(the$
is a convention forObservable
), then it ask for data to the service. When the service receives response data push them intosettingsSource
, it'sObservable
version (settings$
) receives data and all subscribed components get advised