【问题标题】:Angular 11 Error trying to diff observableAngular 11 尝试区分可观察的错误
【发布时间】:2021-01-13 01:49:12
【问题描述】:

我订阅了 SignalR 服务,我想将代码推送显示为 Clarity 数据网格。

这是我来自 SignalR 的活动:

export interface TickerMessage {
  type: string;
  tradeId: number;
  sequence: number;
  time: Date;
  productId: number;
  price: number;
  side: string;
  lastSize: number;
  bestBid: number;
  bestAsk: number;
}

这是我的服务:

  constructor() {
    this.retries = 3;
    this.hub = new signalR.HubConnectionBuilder()
      .withUrl('https://localhost:5001/stream/ticker')
      .withAutomaticReconnect()
      .build();
  }

  public async load() {
    await this hub.start();
  }

  public getMessagesAsObservable(): Observable<TickerMessage[]> {
    let subj = new Subject<TickerMessage[]>();
    this.hub.stream<TickerMessage[]>(this.tickFuncName).subscribe(subj);
    return subj.asObservable();
  }

这是我的商店:

@Injectable({
  providedIn: 'root'
})
export class TickerStore {
  private _ticker: BehaviorSubject<Array<TickerMessage>> = new BehaviorSubject<TickerMessage[]>([]);
  private _tickerSvc: TickerService;

  constructor() {
    this._tickerSvc = new TickerService();
    this._ticker.asObservable();
  }

  public async load() {
    await this._tickerSvc.load();
    this._tickerSvc.getMessagesAsObservable().subscribe(res => {
      this._ticker.next(res);
      console.log(res);
    }, err => {
      console.log('error retrieving ticker feed: ' + err);
    });
  }

  get ticks() {
    return this._ticker.asObservable();
  }
}

这是我的组件:

@Component({
  selector: 'app-ticker',
  templateUrl: './feed.component.html',
  styleUrls: ['./feed.component.css'],
  providers: [TickerService],
  changeDetection: ChangeDetectionStrategy.Default
})
export class FeedComponent implements OnInit, OnDestroy {

  productMap: IProductTypeMap = ProductMap;
  productOrderSide: IProductTypeMap = ProductOrderSide;
  btcMarketFilter = new BtcMarketFilterComponent();
  feed: TickerMessage[];

  constructor(public tickerStore: TickerStore) {
    this.feed = [];
    this.tickerStore.load();
  }

  async ngOnInit() {
    await this.tickerStore.ticks.subscribe(res => {
      this.feed = res;
    });
  }

  ngOnDestroy(): void {
  }
}

这是我的模板:

<clr-dg-row *ngFor="let m of tickerStore.ticks | async">
  <clr-dg-cell>{{m.time | date:'longTime'}}</clr-dg-cell>
  <clr-dg-cell>{{m.sequence}}</clr-dg-cell>
  <clr-dg-cell>{{m.tradeId}}</clr-dg-cell>
  <clr-dg-cell>{{productMap[m.productId]}}</clr-dg-cell>
  <clr-dg-cell>{{m.price | currency:'USD'}}</clr-dg-cell>
  <clr-dg-cell>{{productOrderSide[m.side]}}</clr-dg-cell>
  <clr-dg-cell>{{m.lastSize}}</clr-dg-cell>
  <clr-dg-cell>{{m.bestBid | currency:'USD'}}</clr-dg-cell>
  <clr-dg-cell>{{m.bestAsk | currency:'USD'}}</clr-dg-cell>
</clr-dg-row>

这是我得到的错误:

Error trying to diff '[object Object]'. Only arrays and iterables are allowed

我尝试tickerStore.ticks 到组件中的一个数组,然后订阅该数组,但这似乎不起作用。如何从消息流中创建一个可观察的数组,以便创建一个代码提要?

【问题讨论】:

  • 在您的组件中,您在订阅对象上使用await,这有什么作用吗?订阅不是异步的。此外,在getMessagesAsObservable() 中,您可以只使用share() 运算符而不是创建整个主题以进行多播(我假设这就是您使用主题的原因)。
  • 我认为我在订阅对象上等待是一种习惯(默认情况下我是 C# 开发人员),IDE 说它没有做任何事情,所以我将其删除。共享运算符是对我现有的优化吗?

标签: javascript angular rxjs signalr observable


【解决方案1】:
<clr-dg-row *ngFor="let m of tickerStore.ticks | json | async">
  <clr-dg-cell>{{m.time | date:'longTime'}}</clr-dg-cell>
  <clr-dg-cell>{{m.sequence}}</clr-dg-cell>
  <clr-dg-cell>{{m.tradeId}}</clr-dg-cell>
  <clr-dg-cell>{{productMap[m.productId]}}</clr-dg-cell>
  <clr-dg-cell>{{m.price | currency:'USD'}}</clr-dg-cell>
  <clr-dg-cell>{{productOrderSide[m.side]}}</clr-dg-cell>
  <clr-dg-cell>{{m.lastSize}}</clr-dg-cell>
  <clr-dg-cell>{{m.bestBid | currency:'USD'}}</clr-dg-cell>
  <clr-dg-cell>{{m.bestAsk | currency:'USD'}}</clr-dg-cell>
</clr-dg-row>

试试这个可能对你有帮助(json管道)

【讨论】:

    【解决方案2】:

    所以我发现了问题,而且问题很多。首先,我需要将我的TickerService 转换为实现IStreamSubscriber&lt;TickerMessage&gt;。这使我实现了所需的方法,因此我可以将类的实例订阅到 SignalR 流。一旦我这样做了,我就能够将TickerService.next() 功能连接到我的Subject&lt;Array&lt;TickerMessage&gt;&gt;,而不是 Observable。一旦我连接了主题,其余的就开始工作了。

    服务:

    export class TickerService implements IStreamSubscriber<TickerMessage> {
    
      connectionEstablished = false;
      retries: number;
      tickSource: Subject<Array<TickerMessage>>;
      private hub: HubConnection;
      private maxSize = 500;
      private readonly ticks: TickerMessage[];
      private readonly tickFuncName: string = 'StreamTicks';
    
      constructor() {
        // build the hub.
        this.retries = 3;
        this.hub = new signalR.HubConnectionBuilder()
          .withUrl('https://localhost:5001/stream/ticker')
          .withAutomaticReconnect()
          .build();
    
        // build/populate the ticker
        console.log('attempting to load existing ticker feed');
        const ticks = sessionStorage.getItem('tickerFeed');
        if (typeof ticks != 'undefined' && ticks) {
          this.ticks = JSON.parse(ticks);
        } else {
          console.log('existing ticker feed does not exist, will populate');
          this.ticks = [];
        }
    
        // build the ticker source.
        this.tickSource = new Subject<Array<TickerMessage>>();
      }
    
      public async load() {
        await this.hub
          .start()
          .then(() => {
            console.log(`SignalR connection success! connectionId: ${this.hub.connectionId} `);
          })
          .catch((error) => {
            console.log(`SignalR connection error: ${error}`);
          }).finally(() => {
            this.connectionEstablished = true;
            console.log(`connected to ${this.hub.baseUrl}`);
          });
        this.hub.stream<TickerMessage>(this.tickFuncName).subscribe(this);
      }
    
      public complete(): void {
        console.log('ticker streaming complete');
      }
    
      public error(err: any): void {
        console.log('error streaming from server: ' + err);
      }
    
      public next(value: TickerMessage): void {
        if (this.ticks.length === this.maxSize) {
          this.ticks.pop();
          this.ticks.unshift(value);
        } else {
          this.ticks.unshift(value);
        }
    
        this.tickSource.next(this.ticks);
        sessionStorage.setItem('tickerFeed', JSON.stringify(this.ticks));
      }
    }
    

    商店:

    @Injectable({
      providedIn: 'root'
    })
    export class TickerStore {
      ticks$: Subject<Array<TickerMessage>>;
      private _tickerSvc: TickerService;
    
      constructor() {
        this._tickerSvc = new TickerService();
        this.ticks$ = this._tickerSvc.tickSource;
      }
    
      public async load() {
        await this._tickerSvc.load();
      }
    }
    

    组件:

    @Component({
      selector: 'app-ticker',
      templateUrl: './feed.component.html',
      styleUrls: ['./feed.component.css'],
    })
    export class FeedComponent implements OnInit {
    
      productMap: IProductTypeMap = ProductMap;
      productOrderSide: IProductTypeMap = ProductOrderSide;
      btcMarketFilter = new BtcMarketFilterComponent();
      tickerStore: TickerStore;
    
      constructor(public tStore: TickerStore) {
        this.tickerStore = tStore;
      }
    
      async ngOnInit(): Promise<void> {
        await this.tickerStore.load();
      }
    }
    

    模板:

    <ng-container id="allFeedTable" class="limit-height" *ngIf="(tickerStore.ticks$ | async)?.length; else loading">
      <clr-datagrid class="table table-noborder datagrid-compact">
        <clr-dg-column>Time</clr-dg-column>
        <clr-dg-column>Sequence</clr-dg-column>
        <clr-dg-column>Trade ID</clr-dg-column>
        <clr-dg-column>Market</clr-dg-column>
        <clr-dg-column>Price</clr-dg-column>
        <clr-dg-column>Side</clr-dg-column>
        <clr-dg-column>Last Size</clr-dg-column>
        <clr-dg-column>Best Bid</clr-dg-column>
        <clr-dg-column>Best Ask</clr-dg-column>
        <clr-dg-row *clrDgItems="let m of tickerStore.ticks$ | async">
          <clr-dg-cell>{{m.time | date:'longTime'}}</clr-dg-cell>
          <clr-dg-cell>{{m.sequence}}</clr-dg-cell>
          <clr-dg-cell>{{m.tradeId}}</clr-dg-cell>
          <clr-dg-cell>{{productMap[m.productId]}}</clr-dg-cell>
          <clr-dg-cell>{{m.price | currency:'USD'}}</clr-dg-cell>
          <clr-dg-cell>{{productOrderSide[m.side]}}</clr-dg-cell>
          <clr-dg-cell>{{m.lastSize}}</clr-dg-cell>
          <clr-dg-cell>{{m.bestBid | currency:'USD'}}</clr-dg-cell>
          <clr-dg-cell>{{m.bestAsk | currency:'USD'}}</clr-dg-cell>
        </clr-dg-row>
        <clr-dg-footer>
          <clr-dg-pagination #pagination [clrDgPageSize]="25">
            <clr-dg-page-size [clrPageSizeOptions]="[25,50,100,250,500]">Updates per page</clr-dg-page-size>
            {{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} of {{pagination.totalItems}} updates
          </clr-dg-pagination>
        </clr-dg-footer>
      </clr-datagrid>
    </ng-container>
    <ng-template #loading>
        <span class="spinner spinner-inverse">
          Loading...
        </span>
    </ng-template>
    

    【讨论】:

      猜你喜欢
      • 2018-11-18
      • 2017-05-12
      • 2017-05-17
      • 2018-02-11
      • 2018-08-12
      • 2017-04-06
      • 2016-08-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多