Angular sharing data to Components using service f

2019-08-01 17:34发布

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>

1条回答
一纸荒年 Trace。
2楼-- · 2019-08-01 17:39

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

getSettings(): void {
       this.apiService.getSettings()
         .subscribe(settings =>
           this.tmp = settings);
   }

getData(): Setting[] {
       this.getSettings();
       return this.tmp;
   }
  1. this.getSettings() executes a request and subscribe for it's response
  2. then while it's still waiting for subscribed response you return immediately this.tmp, that is still undefined
  3. after a time your subscribed request ends, you obtain your response and assign that value to your this.tmp variable

If you have splitted your logic into 2 services to manage syncronism problems, consider to use Subject

Possible solution with Subject

Service

export class SettingsService {

   API_URL = 'https://localhost:44381/api/v1';

   settingsSource = new Subject<Setting>();
   settings$ = this.settingsSource.asObservable();

   constructor(private httpClient: HttpClient) { }

   getSettings(): void {
       this.apiService.getSettings().subscribe(settings =>
           this.settingsSource.next(settings);
       );
   }

   getData(): Setting[] {
       this.getSettings();
       return this.tmp;
   }

   get result$(): Observable<Setting> {
      return this.settings$;
   }
}

Component

export class SettingsComponent implements OnInit {

    subscription: Subscription;
    settings: Setting[];

    constructor(private settingService: SettingsService) { }

    // Load Setting while starting
    ngOnInit() {

        // subscribe for settings
        this.resSubscription = this.searchService.result$.subscribe(result => {
            this.resList = result.result;
        });

        // request settings
        this.settings = this.settingService.getData();
    }
}

As you can see SettingsComponent subscribe to settings$ Observable (the $ is a convention for Observable), then it ask for data to the service. When the service receives response data push them into settingsSource, it's Observable version (settings$) receives data and all subscribed components get advised

查看更多
登录 后发表回答