如何使用Angular 7应用程序连接SignalR [英] How to hook up SignalR with an Angular 7 application

查看:89
本文介绍了如何使用Angular 7应用程序连接SignalR的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我根本无法弄清楚如何从Angular建立信号发送器连接.

https://docs.microsoft.com/zh-cn/aspnet/signalr/overview/getting-started/tutorial-getting-started-with-signalr-and-mvc

我在vs2017中的现有.Net 4.6解决方案中添加了一个新的SignalR 2.4.0项目.

我还有一个Angular 7应用程序,我已经通过npm install @aspnet/signalr

向其中添加了SignalR程序包

现在,我正在尝试在客户端和服务器之间建立简单的连接,但无法确定如何建立初始连接.

我的前端不断抛出异常:

 core.js:15714 ERROR Error: Uncaught (in promise): Error: Cannot send data if the connection is not in the 'Connected' State.

错误:如果连接未处于已连接"状态,则无法发送数据.

在前端搜索组件中,我添加了一些测试字段:

 <mat-form-field>
    <input matInput placeholder="message" [(ngModel)]="message">
</mat-form-field>
<button mat-button type="button" (click)="sendMessageToServer()"><span>Send</span></button>            
<p *ngFor="let m of messages">{{m}}</p> 

在我的ts文件中:

 // import other components/services here..
import { HubConnection, HubConnectionBuilder} from '@aspnet/signalr';

@Component({
  selector: 'app-my-search',
  templateUrl: './my-search.component.html',
  styleUrls: ['./my-search.component.scss']
})
export class MySearchComponent implements OnInit {

public hubConnection: HubConnection;
  public messages: string[] = [];
  public message: string;

   constructor() { }
   
   
  ngOnInit() {
   
    // SIGNALR MESSAGE HUB
    let builder = new HubConnectionBuilder();
    this.hubConnection = builder.withUrl('/SynBroadcastHub/BroadcastMessage').build();  // see startup.cs
    this.hubConnection.on('notifyUser', (message) => {
      this.messages.push(message);
      console.log(message);
    });
    this.hubConnection.start();
  }

  // signalr, send msg from client
  sendMessageToServer() {
    this.hubConnection.invoke('MessageToServer', this.message);
    this.message = '';
  }


} 

在c#端,我添加了SignalR Hub Class (v2)文件BroadcastHub.cs

 using Microsoft.AspNet.SignalR;

namespace SynBroadcastHub
{
    public class BroadcastHub : Hub
    {        
        /// Message to client 
        public void BroadcastMessage(string data)
        {
            Clients.Caller.notifyUser(data);
        }
    
        
        /// Message from client application; broadcast to all clients if requested.                
        public void MessageToServer(string data, bool notifyAllClients = false)
        {
            if (notifyAllClients)
            {
                Clients.All.NotifyAllClients(data);
            }
        }
    }
} 

以及Startup.cs文件:

 using Microsoft.Owin;
using Microsoft.AspNet.SignalR;
using Owin;

[assembly: OwinStartup(typeof(SynBroadcastHub.Startup))]

namespace SynBroadcastHub
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            HubConfiguration cfg = new HubConfiguration();
            app.MapSignalR<PersistentConnection>("BroadcastHub/BroadcastMessage");
            app.MapSignalR(cfg);
            app.MapSignalR();

            //app.MapSignalR<NotifyHub>("notify"); ???
        }
         public override Task OnDisconnected(bool stopCalled)
        {
            return Clients.All.leave(Context.ConnectionId, System.DateTime.Now.ToString());
        }

        public override Task OnConnected()
        {
            return Clients.All.joined(Context.ConnectionId, DateTime.Now.ToString());
        }

        public override Task OnReconnected()
        {
            return Clients.All.rejoined(Context.ConnectionId, DateTime.Now.ToString());
        }
    }
} 

解决方案

我花了两天的时间才弄清楚这件事.我终于让它工作了,这些是我必须要做的几件事:

1)您注意到使用@aspnet/signalr软件包对于.Net框架是不正确的,这是正确的.您需要signalr软件包(npm install signalr).

2)这是整个过程中最关键的部分. SignalR依赖于jQuery.您必须在包含Signalr脚本的之前包含jQuery.在angular.json文件中scripts部分下,您需要包括:

"./node_modules/jquery/dist/jquery.js", "./node_modules/signalr/jquery.signalR.js"

以该确切顺序排列.在您的项目启动时,它将首先加载jQuery,然后加载signalR脚本.

许多其他stackover流回答了该问题,以回答此错误:

jQuery was not found. Please ensure jQuery is referenced before the SignalR client JavaScript file

告诉您要在其中使用jQuery的组件中编写import * as $ from "jquery".但是,这样做是正确的.原因是,根据这篇关于全球脚本的 的有角度的文章,使用import语法会将其包含在module负载中,并将其放入通过运行ng build命令创建的vendor.js文件中.这是一个问题的原因是因为jQuery将首先从您的angular.json中加载,然后将signalR加载,然后vendor.js中的模块将重新加载jQuery并从signalR取消附加刚刚附加到jQuery的所有事件.

3)既然您注意到使用的是.net Core版本的signalr,则在实例化角度组件中的新HubConnection时将无法访问HubConnectionBuilder.

相反,当signaler脚本执行后,它将在代码中将其他事件附加到$. 注意:如果您从ts文件中在构建或编译时出错,请确保已包含npm中的@types/signalr@types/jquery

要建立新的集线器连接,请使用$.hubConnection("your-url-here/signalr").运行时,它将连接到服务器的集线器. 注意:我将此结果存储为我的角度分量中一个名为hubConnection的变量

在具有Hub类的服务器代码(.cs文件)上,您需要在类名称上方添加:[HubName("YourHubName")].因此,在您的情况下,您的.cs文件在顶部看起来像这样:

[HubName("Broadcast")]    
public class BroadcastHub : Hub

您很可能必须在.cs文件顶部添加以下内容: using Microsoft.AspNet.SignalR.Hubs;

然后在Angular Component中设置代理以附加到服务器上的该Hub 实例化新的hubConnection之后的下一行,写:

this.hubConnection.createHubProxy("yourHubName");.

您的情况是this.hubConnection.createHubProxy("broadcast");

完成代理后,可以附加侦听器以侦听服务器发出的事件,或者可以从角度组件调用服务器功能.

我遵循了这个示例,以了解如何设置呼叫事件和监听服务器事件.是的,它是角度2,但是在我的角度7应用程序中,信号器的所有功能仍然相同.

简短答案:使用proxy.on('eventname') 收听服务器中的事件,并使用proxy.invoke('eventname') 调用从集线器上的集线器上的功能.

>

最后,cs文件中有一些注意事项.在我的Startup.cs中,我唯一用于映射信号器的是app.MapSignalR().我没有像您一样设置其他有关其他属性的详细信息,但这可能是引起某些问题的另一个原因?

I simply cannot figure out how to make a signalr connection from Angular.

Using the following tutorial at https://docs.microsoft.com/en-us/aspnet/signalr/overview/getting-started/tutorial-getting-started-with-signalr-and-mvc

I've added a new SignalR 2.4.0 project to an existing .Net 4.6 solution in vs2017.

I also have an Angular 7 application to which I've added the SignalR package via npm install @aspnet/signalr

Now I'm trying to hook up a simple connection between client and server, but can't figure out how to establish the initial connection.

My front end keeps throwing an exception:

 core.js:15714 ERROR Error: Uncaught (in promise): Error: Cannot send data if the connection is not in the 'Connected' State.

Error: Cannot send data if the connection is not in the 'Connected' State.

In my front end search component, I've added some fields for testing:

<mat-form-field>
    <input matInput placeholder="message" [(ngModel)]="message">
</mat-form-field>
<button mat-button type="button" (click)="sendMessageToServer()"><span>Send</span></button>            
<p *ngFor="let m of messages">{{m}}</p>

And in my ts file :

// import other components/services here..
import { HubConnection, HubConnectionBuilder} from '@aspnet/signalr';

@Component({
  selector: 'app-my-search',
  templateUrl: './my-search.component.html',
  styleUrls: ['./my-search.component.scss']
})
export class MySearchComponent implements OnInit {

public hubConnection: HubConnection;
  public messages: string[] = [];
  public message: string;

   constructor() { }
   
   
  ngOnInit() {
   
    // SIGNALR MESSAGE HUB
    let builder = new HubConnectionBuilder();
    this.hubConnection = builder.withUrl('/SynBroadcastHub/BroadcastMessage').build();  // see startup.cs
    this.hubConnection.on('notifyUser', (message) => {
      this.messages.push(message);
      console.log(message);
    });
    this.hubConnection.start();
  }

  // signalr, send msg from client
  sendMessageToServer() {
    this.hubConnection.invoke('MessageToServer', this.message);
    this.message = '';
  }


}

and on the c# side, I added a SignalR Hub Class (v2) file, BroadcastHub.cs

using Microsoft.AspNet.SignalR;

namespace SynBroadcastHub
{
    public class BroadcastHub : Hub
    {        
        /// Message to client 
        public void BroadcastMessage(string data)
        {
            Clients.Caller.notifyUser(data);
        }
    
        
        /// Message from client application; broadcast to all clients if requested.                
        public void MessageToServer(string data, bool notifyAllClients = false)
        {
            if (notifyAllClients)
            {
                Clients.All.NotifyAllClients(data);
            }
        }
    }
}

as well as a Startup.cs file :

using Microsoft.Owin;
using Microsoft.AspNet.SignalR;
using Owin;

[assembly: OwinStartup(typeof(SynBroadcastHub.Startup))]

namespace SynBroadcastHub
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            HubConfiguration cfg = new HubConfiguration();
            app.MapSignalR<PersistentConnection>("BroadcastHub/BroadcastMessage");
            app.MapSignalR(cfg);
            app.MapSignalR();

            //app.MapSignalR<NotifyHub>("notify"); ???
        }
         public override Task OnDisconnected(bool stopCalled)
        {
            return Clients.All.leave(Context.ConnectionId, System.DateTime.Now.ToString());
        }

        public override Task OnConnected()
        {
            return Clients.All.joined(Context.ConnectionId, DateTime.Now.ToString());
        }

        public override Task OnReconnected()
        {
            return Clients.All.rejoined(Context.ConnectionId, DateTime.Now.ToString());
        }
    }
}

解决方案

I just spent two days trying to figure out this same thing. I finally got it to work and these are the few things that i had to do:

1) You noted that using the @aspnet/signalr package was incorrect for .Net framework, and that is correct. You need the signalr package (npm install signalr).

2) This is the most critical part of the whole process. SignalR has a dependency on jQuery. You have to include jQuery before including the signalr script. In the angular.json file, under the scripts section, you need to include:

"./node_modules/jquery/dist/jquery.js", "./node_modules/signalr/jquery.signalR.js"

in that exact order. On start up of your project, it will load jQuery first, then the signalR script.

Many other stackover flow answers answering the question in reply to this error:

jQuery was not found. Please ensure jQuery is referenced before the SignalR client JavaScript file

tell you to write import * as $ from "jquery" in the component you are wanting to use jQuery in. However, it is NOT correct to do this. The reason is, according to this angular article about global scripts, using the import syntax will include it in the module load and put it in the vendor.js file that is created from running an ng build command. The reason this is a problem is because jQuery will get loaded first from your angular.json, then signalR will be loaded, then the module from the vendor.js will RELOAD jQuery and unattach all the events that were just attached to jQuery from signalR.

3) Since you noticed you were using the .Net Core version of signalr, you wont have access to the HubConnectionBuilder when trying to instantiate a new HubConnection in your angular component.

Instead, when the signalr script gets executed, it will attach additional events to the $ in your code. Note: if you get errors on build or in compile time from your ts files, make sure you've included the @types/signalr and @types/jquery from npm

To set up a new hub connection, use $.hubConnection("your-url-here/signalr"). This will attach to your server's Hub when running. Note: I stored the result from this as a variable called hubConnection in my angular component

On your server code (.cs file), where you have your Hub class, you will need to add above the class name: [HubName("YourHubName")]. So in your case your .cs file would look something like this at the top:

[HubName("Broadcast")]    
public class BroadcastHub : Hub

You will most likely have to include this at the top of your .cs file: using Microsoft.AspNet.SignalR.Hubs;

Then in your Angular Component you set up a proxy to attach to that Hub on your server The very next line after instantiating your new hubConnection, write:

this.hubConnection.createHubProxy("yourHubName");.

In your case, this.hubConnection.createHubProxy("broadcast");

After you have made your proxy, you can then attach listeners to listen to events emitted from the server, or you can invoke server functions from your angular components.

I followed this example here to learn how to set up calling events and listening to server events. Yes it is angular 2, but the functions from signalr all still work the same in my angular 7 app.

Short answer: use proxy.on('eventname') to listen to events from the server, and use proxy.invoke('eventname') to call functions on your Hub from your angular components.

Lastly, a few notes in your cs files. In my Startup.cs, the only thing i have for mapping signalr is app.MapSignalR(). I did not go into many details regarding other properties to set like you have done, but that may be another cause of some issues?

这篇关于如何使用Angular 7应用程序连接SignalR的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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