- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
如何在 Angular 2 中使用 SignalR?
如何在从 SignalR 接收数据时手动运行变化检测?
最佳答案
我最近写了一篇文章,演示了一种使用“ channel /事件”模型集成 Angular 2 和 SignalR 的方法:
https://blog.sstorie.com/integrating-angular-2-and-signalr-part-2-of-2/
我认为仅仅链接到另一个站点是不合适的,所以这里是公开 SignalR 的 Angular 2 服务的核心:
import {Injectable, Inject} from "angular2/core";
import Rx from "rxjs/Rx";
/**
* When SignalR runs it will add functions to the global $ variable
* that you use to create connections to the hub. However, in this
* class we won't want to depend on any global variables, so this
* class provides an abstraction away from using $ directly in here.
*/
export class SignalrWindow extends Window {
$: any;
}
export enum ConnectionState {
Connecting = 1,
Connected = 2,
Reconnecting = 3,
Disconnected = 4
}
export class ChannelConfig {
url: string;
hubName: string;
channel: string;
}
export class ChannelEvent {
Name: string;
ChannelName: string;
Timestamp: Date;
Data: any;
Json: string;
constructor() {
this.Timestamp = new Date();
}
}
class ChannelSubject {
channel: string;
subject: Rx.Subject<ChannelEvent>;
}
/**
* ChannelService is a wrapper around the functionality that SignalR
* provides to expose the ideas of channels and events. With this service
* you can subscribe to specific channels (or groups in signalr speak) and
* use observables to react to specific events sent out on those channels.
*/
@Injectable()
export class ChannelService {
/**
* starting$ is an observable available to know if the signalr
* connection is ready or not. On a successful connection this
* stream will emit a value.
*/
starting$: Rx.Observable<any>;
/**
* connectionState$ provides the current state of the underlying
* connection as an observable stream.
*/
connectionState$: Rx.Observable<ConnectionState>;
/**
* error$ provides a stream of any error messages that occur on the
* SignalR connection
*/
error$: Rx.Observable<string>;
// These are used to feed the public observables
//
private connectionStateSubject = new Rx.Subject<ConnectionState>();
private startingSubject = new Rx.Subject<any>();
private errorSubject = new Rx.Subject<any>();
// These are used to track the internal SignalR state
//
private hubConnection: any;
private hubProxy: any;
// An internal array to track what channel subscriptions exist
//
private subjects = new Array<ChannelSubject>();
constructor(
@Inject(SignalrWindow) private window: SignalrWindow,
@Inject("channel.config") private channelConfig: ChannelConfig
) {
if (this.window.$ === undefined || this.window.$.hubConnection === undefined) {
throw new Error("The variable '$' or the .hubConnection() function are not defined...please check the SignalR scripts have been loaded properly");
}
// Set up our observables
//
this.connectionState$ = this.connectionStateSubject.asObservable();
this.error$ = this.errorSubject.asObservable();
this.starting$ = this.startingSubject.asObservable();
this.hubConnection = this.window.$.hubConnection();
this.hubConnection.url = channelConfig.url;
this.hubProxy = this.hubConnection.createHubProxy(channelConfig.hubName);
// Define handlers for the connection state events
//
this.hubConnection.stateChanged((state: any) => {
let newState = ConnectionState.Connecting;
switch (state.newState) {
case this.window.$.signalR.connectionState.connecting:
newState = ConnectionState.Connecting;
break;
case this.window.$.signalR.connectionState.connected:
newState = ConnectionState.Connected;
break;
case this.window.$.signalR.connectionState.reconnecting:
newState = ConnectionState.Reconnecting;
break;
case this.window.$.signalR.connectionState.disconnected:
newState = ConnectionState.Disconnected;
break;
}
// Push the new state on our subject
//
this.connectionStateSubject.next(newState);
});
// Define handlers for any errors
//
this.hubConnection.error((error: any) => {
// Push the error on our subject
//
this.errorSubject.next(error);
});
this.hubProxy.on("onEvent", (channel: string, ev: ChannelEvent) => {
//console.log(`onEvent - ${channel} channel`, ev);
// This method acts like a broker for incoming messages. We
// check the interal array of subjects to see if one exists
// for the channel this came in on, and then emit the event
// on it. Otherwise we ignore the message.
//
let channelSub = this.subjects.find((x: ChannelSubject) => {
return x.channel === channel;
}) as ChannelSubject;
// If we found a subject then emit the event on it
//
if (channelSub !== undefined) {
return channelSub.subject.next(ev);
}
});
}
/**
* Start the SignalR connection. The starting$ stream will emit an
* event if the connection is established, otherwise it will emit an
* error.
*/
start(): void {
// Now we only want the connection started once, so we have a special
// starting$ observable that clients can subscribe to know know if
// if the startup sequence is done.
//
// If we just mapped the start() promise to an observable, then any time
// a client subscried to it the start sequence would be triggered
// again since it's a cold observable.
//
this.hubConnection.start()
.done(() => {
this.startingSubject.next();
})
.fail((error: any) => {
this.startingSubject.error(error);
});
}
/**
* Get an observable that will contain the data associated with a specific
* channel
* */
sub(channel: string): Rx.Observable<ChannelEvent> {
// Try to find an observable that we already created for the requested
// channel
//
let channelSub = this.subjects.find((x: ChannelSubject) => {
return x.channel === channel;
}) as ChannelSubject;
// If we already have one for this event, then just return it
//
if (channelSub !== undefined) {
console.log(`Found existing observable for ${channel} channel`)
return channelSub.subject.asObservable();
}
//
// If we're here then we don't already have the observable to provide the
// caller, so we need to call the server method to join the channel
// and then create an observable that the caller can use to received
// messages.
//
// Now we just create our internal object so we can track this subject
// in case someone else wants it too
//
channelSub = new ChannelSubject();
channelSub.channel = channel;
channelSub.subject = new Rx.Subject<ChannelEvent>();
this.subjects.push(channelSub);
// Now SignalR is asynchronous, so we need to ensure the connection is
// established before we call any server methods. So we'll subscribe to
// the starting$ stream since that won't emit a value until the connection
// is ready
//
this.starting$.subscribe(() => {
this.hubProxy.invoke("Subscribe", channel)
.done(() => {
console.log(`Successfully subscribed to ${channel} channel`);
})
.fail((error: any) => {
channelSub.subject.error(error);
});
},
(error: any) => {
channelSub.subject.error(error);
});
return channelSub.subject.asObservable();
}
// Not quite sure how to handle this (if at all) since there could be
// more than 1 caller subscribed to an observable we created
//
// unsubscribe(channel: string): Rx.Observable<any> {
// this.observables = this.observables.filter((x: ChannelObservable) => {
// return x.channel === channel;
// });
// }
/** publish provides a way for calles to emit events on any channel. In a
* production app the server would ensure that only authorized clients can
* actually emit the message, but here we're not concerned about that.
*/
publish(ev: ChannelEvent): void {
this.hubProxy.invoke("Publish", ev);
}
}
然后一个组件可以通过订阅(不是在 rxjs 意义上......)一个特定的 channel 来使用这个服务,并对发出的特定事件使用react:
import {Component, OnInit, Input} from "angular2/core";
import {Http, Response} from "angular2/http";
import Rx from "rxjs/Rx";
import {ChannelService, ChannelEvent} from "./services/channel.service";
class StatusEvent {
State: string;
PercentComplete: number;
}
@Component({
selector: 'task',
template: `
<div>
<h4>Task component bound to '{{eventName}}'</h4>
</div>
<div class="commands">
<textarea
class="console"
cols="50"
rows="15"
disabled
[value]="messages"></textarea>
<div class="commands__input">
<button (click)="callApi()">Call API</button>
</div>
</div>
`
})
export class TaskComponent implements OnInit {
@Input() eventName: string;
@Input() apiUrl: string;
messages = "";
private channel = "tasks";
constructor(
private http: Http,
private channelService: ChannelService
) {
}
ngOnInit() {
// Get an observable for events emitted on this channel
//
this.channelService.sub(this.channel).subscribe(
(x: ChannelEvent) => {
switch (x.Name) {
case this.eventName: { this.appendStatusUpdate(x); }
}
},
(error: any) => {
console.warn("Attempt to join channel failed!", error);
}
)
}
private appendStatusUpdate(ev: ChannelEvent): void {
// Just prepend this to the messages string shown in the textarea
//
let date = new Date();
switch (ev.Data.State) {
case "starting": {
this.messages = `${date.toLocaleTimeString()} : starting\n` + this.messages;
break;
}
case "complete": {
this.messages = `${date.toLocaleTimeString()} : complete\n` + this.messages;
break;
}
default: {
this.messages = `${date.toLocaleTimeString()} : ${ev.Data.State} : ${ev.Data.PercentComplete} % complete\n` + this.messages;
}
}
}
callApi() {
this.http.get(this.apiUrl)
.map((res: Response) => res.json())
.subscribe((message: string) => { console.log(message); });
}
}
我尝试将 SignalR 概念映射到可观察对象中,但我仍在学习如何有效地使用 RxJS。无论如何,我希望这有助于说明这在 Angular 2 应用程序的上下文中是如何工作的。
关于signalr - 如何在 Angular 2 中使用 SignalR?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34982075/
我正在尝试在项目中学习和添加 Angular 国际化。我只能理解 Angular 文档 (https://angular.io/guide/i18n-overview) 的编译时翻译。 我需要这样的东
在我的 Angular 应用程序中,基于登录用户,我想通过显示/隐藏不同的菜单项或允许/禁止某些路由来授予或限制功能。 目前成功登录后,我的 .NET Core API 会返回一个 JWT token
我是 Angular 的新手,目前我已经看过 angular.io 网站提供的一些示例。但是在component decorator在文档中的解释,它指出 Angular components are
这里是service employee-service.service.ts的代码 import { Injectable } from '@angular/core'; import { HttpC
我目前正在使用@angular/http URLSearchParams 类来检索 URL 参数。在 Angular 5 中,注意到这已被弃用,但我没有看到以我当前使用的方式替换 URLSearchP
我目前正在使用@angular/http URLSearchParams 类来检索 URL 参数。在 Angular 5 中,注意到这已被弃用,但我没有看到以我当前使用的方式替换 URLSearchP
如何正确安装 PUG/JADE 到 Angular 2 或更高版本 这样在工作和 AOT 和 JiT 的同时 工作单元和集成测试 并且在创建每个新组件时不会受到太多影响 最佳答案 我看到了很多解决方案
我的 Angular 12 应用程序中有一些通用组件,我计划将其创建为一个 Angular 库,以便其他应用程序也可以使用它。我们有一些应用程序在较低版本的 angular(例如 angular 8/
tl;dr; ng build 删除了包含我编译的自定义库的/dist 文件夹。这会使我项目代码中对该库的所有引用无效,从而导致 ng build 最终失败。我做错了什么? 我关注了documenta
我正在将一些“遗留”(非 typescript )js 库导入到我的 Angular SPA 中。 通常我只是从 cdn 添加一个负载到 index.html 就像: 在 Angular 分量中我只
我有这个 angular 应用程序,它基本上使用了库的概念。 我有 2 个名为 的库Lib1 和 lib2 根据他们所服务的微服务分组。 现在我将这些库导入主应用程序,即 应用1 事情一直到现在。 现
我在我的项目中启用了 angular Universal。我现在想完全删除它。我试图删除以下文件 /server.ts /webpack.server.config.js /src/tsconfig.
我已经有一个 AuthService 在登录时对用户进行身份验证,并且 AuthGuard 在未登录的情况下阻止访问。 某些页面我通过 UserProfile/Role 限制访问,但现在我需要阻止页面
我正在尝试使用 angular、TypeORM、SQLite 和其他组件作为 webpack 构建 Electron 应用程序。 我从在 GitHub 上找到的示例开始我的开发:https://git
我在从 Angular 8 更新到 9 并运行时遇到以下错误 ng 更新@angular/material: Package "@angular/flex-layout" has an incompa
我正在尝试使用 Angular 9,我想创建一个项目,然后创建一个库项目并开始向其中添加我想稍后在 GitHub 上发布的通用模块,并在我的本地使用这些库项目。 相关依赖如下: Angular CLI
我正在尝试使用 Angular 9,我想创建一个项目,然后创建一个库项目并开始向其中添加我想稍后在 GitHub 上发布的通用模块,并在我的本地使用这些库项目。 相关依赖如下: Angular CLI
我正在我的 h1 元素“之前”创建一个小的程式化三 Angular 形图案,但我无法正确地圆 Angular 。右上角没问题,但其他两个有剪裁问题。 这是输出以及形状的放大图像: 使用的代码如下: h
我有一个 Angular 元素,带有自定义标记名 - fancy-button。如何将 fancy-button 嵌入 Angular 应用程序? 我已经尝试了以下方法,但都没有用 - 在 index
我已将我的项目从 angular 5.2.9 升级到 angular 6.0.0-rc.5。 除了包路径中的几个快速 RxJS 修复外,一切看起来都不错。(此链接非常有用:Want to upgrad
我是一名优秀的程序员,十分优秀!