使用服务将 Angular 数据共享给组件失败 [英] Angular sharing data to Components using service fails

查看:24
本文介绍了使用服务将 Angular 数据共享给组件失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的 Angular 前端有 2 个服务,一个用于 API 调用,另一个用于共享两个不同组件的数据.所以第二个服务是使用 API 服务.

如果我只使用 API 服务并在我的组件中订阅我的 Observables,一切正常(在 xxx.component.html 中查看).

因此,如果我在 app.module 中将这两个服务声明为提供者并将 API 服务注入到共享服务中,它将不再起作用.

使用调试器,我总是得到 settings.service.ts 中未定义的变量tmp"

我知道,我也可以使用 @Input 来实现子组件,但我认为使用服务也是解耦组件的更好方法.

有什么建议:)?

在我的代码下方:

api.service.ts

导出类 ApiService {API_URL = 'https://localhost:44381/api/v1';构造函数(私有httpClient:HttpClient){}/** 从 API 获取设置 */getSettings(): Observable{返回 this.httpClient.get(this.API_URL + '/settings').管道(catchError(this.handleError('getSettings', [])));}}

settings.service.ts

导出类 SettingsService {tmp: 设置[];构造函数(私有 apiService:ApiService){}getSettings(): void {this.apiService.getSettings().订阅(设置=>this.tmp = 设置);}getData(): 设置[] {this.getSettings();返回 this.tmp;}}

settings.component.ts

导出类 SettingsComponent 实现 OnInit {设置:设置[];构造函数(私有设置服务:设置服务){}//启动时加载设置ngOnInit() {//this.getSettings();this.settings = this.settingService.getData();}//仅使用有效的 API 服务时的旧代码../*getSettings(): void {this.apiService.getSettings().subscribe(settings => this.settings = settings);}*/}

settings.component.hmtl

<表格><tr><th>keyname</th><th>值</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>

解决方案

您的问题在于异步功能.subscribe 方法用于等待异步操作.这意味着当您执行特定请求(例如远程请求)时,您不想停止整个流程执行以等待响应.而是您希望在服务器正在处理您的请求并发送其响应的同时发出请求并执行所有其他代码.

那么我们来分析一下这段代码执行的流程是什么

getSettings(): void {this.apiService.getSettings().订阅(设置=>this.tmp = 设置);}getData(): 设置[] {this.getSettings();返回 this.tmp;}

  1. this.getSettings() 执行请求并订阅它回复
  2. 然后当它仍在等待订阅的响应时,您立即返回this.tmp,这仍然是未定义的
  3. 在您订阅的请求结束一段时间后,您获得响应并将该值分配给您的 this.tmp 变量

如果您已将逻辑拆分为 2 个服务来管理同步问题,请考虑使用 Subject

主题的可能解决方案

服务

导出类 SettingsService {API_URL = 'https://localhost:44381/api/v1';settingsSource = 新主题<设置>();settings$ = this.settingsSource.asObservable();构造函数(私有httpClient:HttpClient){}getSettings(): void {this.apiService.getSettings().subscribe(settings =>this.settingsSource.next(设置););}getData(): 设置[] {this.getSettings();返回 this.tmp;}get result$(): Observable{返回 this.settings$;}}

组件

导出类 SettingsComponent 实现 OnInit {订阅:订阅;设置:设置[];构造函数(私有设置服务:设置服务){}//启动时加载设置ngOnInit() {//订阅设置this.resSubscription = this.searchService.result$.subscribe(result => {this.resList = result.result;});//请求设置this.settings = this.settingService.getData();}}

如你所见 SettingsComponent 订阅 settings$ Observable ($>Observable),然后它向服务请求数据.当服务收到响应数据时,将它们推送到 settingsSource 中,它的 Observable 版本 (settings$) 接收数据并通知所有订阅的组件

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

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

这篇关于使用服务将 Angular 数据共享给组件失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆