首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >您能在不调用signalR中的方法的情况下在服务中注入角10+中的app.component吗?

您能在不调用signalR中的方法的情况下在服务中注入角10+中的app.component吗?
EN

Stack Overflow用户
提问于 2021-06-18 22:02:09
回答 1查看 1.4K关注 0票数 3

因此,我遵循了一些教程,在C#中设置集线器后,常见的主题之一是调用类似于以下内容的服务:

代码语言:javascript
复制
private hubConnection: signalR.HubConnection = new signalR.HubConnectionBuilder()
.withUrl(`${environment.hub}contacts`)
.build();

constructor(private http: HttpClient) { 
}

public startConnection = () => 
  this.hubConnection
    .start()
    .then(() => console.log('Hub Connection started'))
    .catch(err => console.log('Error while starting connection: ' + err))
    

public addTransferContactDataListener = () => 
    this.hubConnection.on('transfercontactdata', (data) => {
        this.contacts = data.result // where the magic happens on the push
        console.log(data, 'Hub data transfer occuring');
    });

我担心的是,如果您试图在构造函数中注入private hubConnection: signalR.HubConnection,它就会爆炸。即使您设置了连接生成器。这很重要,因为如果我想要四个或更多的页面,而这些页面都是subscribe的呢?

我一直在做的是在app.component.ts中设置服务,然后调用startConnectionaddTransferContactDataListener的方法。但这似乎是错误的。我试图让它在模块中被注入,但是它一直失败,说它没有provider或其他东西。虽然它是可以注射使用的,但您仍然必须调用它,在我的实践中,构造函数有时似乎被忽略了。

已经有人钻研如何在注入和重用它时调用和设置它了?像'A‘或'B’视图这样的调用signalR服务,并且任何一个都可以让它自动运行构造函数或某些参数一次。我可能做错了什么。就像我说的,起作用了。但是我必须在app.component.ts中调用这些方法,这样做就感觉不对,而且很脆弱。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-06-25 10:58:04

SignalR在角度上的应用

我们的目标是创建一个服务,它充当角连接和signalR连接之间的中介。这种方法意味着我们需要解决两个普遍的问题。

  • 与SignalR连接
  • 设计一种与我们的应用程序的其余部分接口的方法

角接口

我们的目标是能够在我们的应用程序中的任何地方注入我们的服务,并且当我们的signalR中心发生对我们的服务感兴趣的事件时,我们的组件会做出适当的反应。

提供服务

由于我们的服务可能在任何地方使用,并且具有处理connectionHub的生命周期逻辑,如果试图打开非封闭连接,该逻辑将失败,因此我们可以使用的唯一注入器如下:

  • app.module.ts
  • @Injectable({providedIn: 'root'})

最简单的解决方案是使用@Injectable({providedIn: 'root'})装饰器,但是对于具有细微的内部生命周期(如SignalR服务)的服务,我更愿意公开一个公共API,这样我们就只能公开对我们的团队来说安全的方法。

公共接口

首先,让我们创建一个接口,我们可以使用它使SignalR服务在我们的应用程序的其余部分中可用。然而,由于我们不能提供角度的接口,所以我们使用抽象类代替。

公共SignalR接口

代码语言:javascript
复制
import {Observable} from 'rxjs';
import {Injectable} from '@angular/core';

@Injectable()
export abstract class SignalRService {
  abstract getDataStream(): Observable<any>
}

我们SignalR服务的开始

代码语言:javascript
复制
import {Subject, Observable} from 'rxjs';
import {SignalRService} from './signalr.service';
import {Injectable} from '@angular/core';

@Injectable()
export class PrivateSignalRService extends SignalRService {
  private _signalEvent: Subject<any>;

  constructor() {
    super();
    this._signalEvent = new Subject<any>();
  }

  getDataStream(): Observable<any> {
    return this._signalEvent.asObservable();
  }
}

现在我们有了注射的基本装置。我们描述了我们希望在抽象类中对哪些服务可用的公共接口,将它作为接口处理,并在PrivateSignalRService中实现我们的逻辑。

现在,我们所要做的就是告诉角喷射器在我们要求一个PrivateSignalRService时提供一个SignalRService

app.module.ts

代码语言:javascript
复制
@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule
  ],
  providers: [{
    provide: SignalRService,
    useClass: PrivateSignalRService
  }],
  bootstrap: [AppComponent]
})
export class AppModule {
  // inject signal directly in module if you want to start connecting immediately.
  constructor(private signal: SignalRService) {
  }
}

将SignalR事件映射到主题事件

我们现在能够在应用程序中注入我们的SignalRService,但是我们的HubConnection可能会随着时间的推移而增长,而且可能不是每个事件都与每个组件相关,所以我们创建了一个过滤器。

首先,我们创建一个枚举,它表示我们可能期望接收到的每个不同的HubConnection事件。

signal-event-type.ts

代码语言:javascript
复制
export enum SignalEventType {
  EVENT_ONE,
  EVENT_TWO
}

接下来,我们需要一个接口,这样我们就知道在调用getDataStream时会期望什么了。

信号事件

代码语言:javascript
复制
import {SignalEventType} from './signal-event-type';

export interface SignalEvent<TDataShape> {
  type: SignalEventType,
  data: TDataShape
}

更改公共抽象类接口的签名

SignalRService

代码语言:javascript
复制
@Injectable()
export abstract class SignalRService {
  abstract getDataStream<TDataShape>(...filterValues: SignalEventType[]): Observable<SignalEvent<TDataShape>>
}

PrivateSignalRService

代码语言:javascript
复制
@Injectable()
export class PrivateSignalRService extends SignalRService {
  private _signalEvent: BehaviorSubject<SignalEvent<any>>;

  constructor() {
    super();
    this._signalEvent = new BehaviorSubject<any>(null);
  }

  getDataStream<TDataShape>(...filterValues: SignalEventType[]): Observable<SignalEvent<TDataShape>> {
    return this._signalEvent.asObservable().pipe(filter(event => filterValues.some(f => f === event.type)));
  }

}

与SignalR连接

注意:本例使用@aspnet/signalr包。

观察到对PrivateSignalRService的以下更改

代码语言:javascript
复制
@Injectable()
export class PrivateSignalRService extends SignalRService {
  private _signalEvent: Subject<SignalEvent<any>>;
  private _openConnection: boolean = false;
  private _isInitializing: boolean = false;
  private _hubConnection!: HubConnection;

  constructor() {
    super();
    this._signalEvent = new Subject<any>();
    this._isInitializing = true;
    this._initializeSignalR();
  }

  getDataStream<TDataShape>(...filterValues: SignalEventType[]): Observable<SignalEvent<TDataShape>> {
    this._ensureConnection();
    return this._signalEvent.asObservable().pipe(filter(event => filterValues.some(f => f === event.type)));
  }

  private _ensureConnection() {
    if (this._openConnection || this._isInitializing) return;
    this._initializeSignalR();
  }

  private _initializeSignalR() {
    this._hubConnection = new HubConnectionBuilder()
      .withUrl('https://localhost:5001/signalHub')
      .build();
    this._hubConnection.start()
      .then(_ => {
        this._openConnection = true;
        this._isInitializing = false;
        this._setupSignalREvents()
      })
      .catch(error => {
        console.warn(error);
        this._hubConnection.stop().then(_ => {
          this._openConnection = false;
        })
      });

  }

  private _setupSignalREvents() {
    this._hubConnection.on('MessageHelloWorld', (data) => {
      // map or transform your data as appropriate here:
      this._onMessage({type: SignalEventType.EVENT_ONE, data})
    })
    this._hubConnection.on('MessageNumberArray', (data) => {
      // map or transform your data as appropriate here:
      const {numbers} = data;
      this._onMessage({type: SignalEventType.EVENT_TWO, data: numbers})
    })
    this._hubConnection.onclose((e) => this._openConnection = false);
  }

  private _onMessage<TDataShape>(payload: SignalEvent<TDataShape>) {
    this._signalEvent.next(payload);
  }

}

现在,当您第一次请求getDataStream时,如果没有打开的连接,该服务将尝试创建一个signalR连接,因此您不再需要在AppModule构造函数中注入服务。

侦听组件中的事件

  • 示例-一个对EVENT_ONE感兴趣
  • 示例-2对EVENT_TWO感兴趣

example-one.component.ts

代码语言:javascript
复制
@Component({
  selector: 'app-example-one',
  template: `<p>Example One Component</p>`
})

export class ExampleOneComponent implements OnInit, OnDestroy {
  subscription!: Subscription;
  constructor(private signal: SignalRService) {
  }

  ngOnInit(): void {
    this.subscription = this.signal.getDataStream<string>(SignalEventType.EVENT_ONE).subscribe(message => {
      console.log(message.data);
    })
  }
  
  ngOnDestroy() {
    this.subscription.unsubscribe();
  }

}

example-two.component.ts

代码语言:javascript
复制
@Component({
  selector: 'app-example-two',
  template: `<p>Example Two Component</p>`
})
export class ExampleTwoComponent implements OnInit, OnDestroy {
  subscription!: Subscription;

  constructor(private signal: SignalRService) {
  }

  ngOnInit(): void {
    this.subscription = this.signal.getDataStream<string[]>(SignalEventType.EVENT_TWO).subscribe(message => {
      message.data.forEach(m => console.log(m));
    })
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }

}

ExampleOneComponentExampleTwoComponent现在只在HubConnection中接收的事件是每个组件的适当类型时才接收事件。

最后说明

此示例代码没有健壮的错误处理,只演示了我们所采取的将signalR与error集成的一般方法。

您还需要确定管理本地持久化的策略,因为主题将只显示任何新传入的消息,当您在应用程序中导航时,这些消息会重置您的组件。

来源

我在这里包括了一个小的MVP设置

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68041867

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档