I want to call a client side method from my SignalR hub class only once, when the client first loads up the webpage. How would I go about doing this?
In your BlahBlahHub class, you have access to all of the connection methods that you do on the Client. Hint: Look at the base Hub class.
With that being said, here's what the code would look like:
Hub
[HubName("messageHub")]
public class MessageHub : Hub
{
public override Task OnConnected()
{
Clients.Caller.alertUser(88);
return base.OnConnected();
}
}
Client
var sender = $.connection.messageHub;
$.connection.hub.start().done(function () {
}).fail(function (reason) {
console.log("SignalR connection failed: " + reason);
});
sender.client.alertUser = function (test) {
alert(test);
};
Related
Below is the code I wrote for SignalR implementation based on ASP.Net documentation and I use manual proxy creation method. I Could see only negotiate happening and received a Connection id.
I can't see OnConnected method in my hub gets executed when I start connection. According to the note section in the document I have attached event handler before I call start method
SignalR Hub
public class MyTestHub: Hub
{
private static Dictionary<int, List<string>> userConnections
= new Dictionary<int, List<string>>();
public override Task OnConnected()
{
RegisterUserConnectionInMap();
return base.OnConnected();
}
}
Startup.cs
app.Map(
"/signalr",
map =>
{
var hubConfiguration = new HubConfiguration { EnableDetailedErrors = true};
map.RunSignalR(hubConfiguration);
});
Javascript Client Code
var connection = $.hubConnection();
var contosoChatHubProxy = connection.createHubProxy('MyTestHub');
contosoChatHubProxy.on('addContosoChatMessageToPage', function(userName:any, message:any) {
console.log(userName + ' ' + message);
});
connection.start()
.done(function(){ console.log('Now connected, connection ID=' + connection.id); })
.fail(function(){ console.log('Could not connect'); });
Note section in documentation
Normally you register event handlers before calling the start method
to establish the connection. If you want to register some event
handlers after establishing the connection, you can do that, but you
must register at least one of your event handler(s) before calling the
start method. One reason for this is that there can be many Hubs in an
application, but you wouldn't want to trigger the OnConnected event on
every Hub if you are only going to use to one of them. When the
connection is established, the presence of a client method on a Hub's
proxy is what tells SignalR to trigger the OnConnected event. If you
don't register any event handlers before calling the start method, you
will be able to invoke methods on the Hub, but the Hub's OnConnected
method won't be called and no client methods will be invoked from the
server.
I could not figure out what I miss for past two days.
UPDATE:
Even I tried with auto generated proxy class by including <script src="~/SignalR/hubs" with the following client code. Still OnConnected Not fired
var contosoChatHubProxy = $.connection.myTestHub;
contosoChatHubProxy.client.addContosoChatMessageToPage = function (name, message) {
console.log(userName + ' ' + message);
};
$.connection.hub.start()
.done(function(){ console.log('Now connected, connection ID=' + $.connection.hub.id); })
.fail(function(){ console.log('Could not Connect!'); });
Console Log after connectton
I have ended with the below solution. Hope it will help some one.
declare var $: any;
#Injectable()
export class CityChangeNotifier {
constructor(private appService: AppService, private router: Router) {
this.connection = $.hubConnection();
this.CityChangeHub = this.connection.createHubProxy('CityChangeNotificationHub');
this.CityChangeHub
.on('CityUpdatedByServer', (newLocation:any, connectionId:string) => this.onCityUpdatedByServer(newLocation, connectionId));
this.connection.transportConnectTimeout = 10000;
this.startConnection();
}
private startConnection(): void {
let that = this;
this.connection.start()
.done((connection: any) => { that.connectionId = connection.id; })
.fail(() => { });
}
}
The following code works fine in IIS Express, but failed in IIS10.
The weird thing is serverside method can successfully be invoked, however clientside method can't.
JavaScript
var hub = $.connection.liveRoomHub;
hub.client.addMessageToPage = function(data){
debugger;//here, this method never gets invoked
console.log(JSON.stringify(data));
};
$.connection.hub.start()
.done(function() {
hub.server.join('room1')
.done(function(){
debugger; //code can run into here
hub.server.sendMessage('user','test','room1');
})
});
C#
public class LiveRoomHub : Microsoft.AspNet.SignalR.Hub
{
public ILogger Logger { get; set; }
public async Task SendMessage(string name, string message, string roomName)
{
await Clients.Group(roomName)
.addMessageToPage(new
{
Name = name,
Message = message
});
Logger.Info($"{name}send msg:{message}in room:{roomName},");//logged
}
public async Task Join(string roomName)
{
await Groups.Add(Context.ConnectionId, roomName);
Logger.Info($"{Context.ConnectionId} enter room: {roomName}");//logged
}
}
All right, problem solved.
I'm using aspnetboilerplate, and abp.signalr.js automatically calls the hub connection before my JavaScript code is loaded.
Obviously, at that time, my hub.client.addMessageToPage isn't registered yet.
That's the common Connection started before subscriptions are added error.
If I have several hubs, and connect a single JavaScript client to all of them, will the context ConnectionID be the same between them?
Interesting question. I didn't know the answer, so I tested it using this example by changing it a bit.
The Hub classes:
public class ChatHub : Hub {
public void Send(string name, string message) {
string cid = Context.ConnectionId;
Clients.All.sendMessage(name, message);
}
}
public class ChatHub2 : Hub
{
public void Send(string name, string message)
{
string cid = Context.ConnectionId;
Clients.All.sendMessage(name, message);
}
}
The page.html connecting to the hubs:
var chat = $.connection.chatHub;
var chat2 = $.connection.chatHub2;
$.connection.hub.start().done(function () {
// Call the Send method on the hub.
chat.server.send('Me', 'Message to 1');
chat2.server.send('Me', 'Message to 2');
});
I set breakpoints on the Hub methods and both are called, and Context.ConnectionId are the same. That's what I was expecting. Give it a try!
It makes sense, it supposed to use the same connection to send the message over.
I seem to have an issue with SignalR's JS Client Hub.
The problem is that the 'on' handler does not seem to work - it generates no error but doesn't receive any signals sent by the server.
The code below shows an extract where I call the server (using the invoke) which works fine - then on the server I call back to acceptHubData which should be picked up on the client but isn't.
My objective is when navigating to pages that each page will open a connection to a specific hub and releases this connection when the user moves to another page!!
EDIT: using the following code snippet works but I wonder why the code further below using the 'on' event doesn't work!
var superHub = $.connection.mySuperHub;
superHub.client.acceptHubData = function (data) {
$('<li>hello there' + data + '</li>').prependTo($('#ul1'))
}
$.connection.hub.start().done(function () {
$('<li>done phase 1</li>').prependTo($('#ul1'))
});
Any help would be much appreciated!
This is the client code (in js)
$(document).ready(function () {
var myHub;
try {
var connection = $.hubConnection();
connection.start().done(function () {
myHub = connection.createHubProxy("mySuperHub");
myHub.on('acceptHubData', function (data) {
alert(data); // THIS IS NOT CALLED!
});
myHub.invoke('AcceptSignal', "hello from the client2");
});
}
catch (e) {
alert(e.message);
}
});
This is the Server code:
[HubName("mySuperHub")]
public class MyHub : Hub
{
private readonly HubEngine _hubEngine;
public MyHub() : this(HubEngine.Instance) { }
public MyHub(HubEngine hubEngine)
{
_hubEngine = hubEngine;
}
public void AcceptSignal(string msg)
{
Clients.Caller.acceptHubData("hi");
Clients.All.acceptHubData("hi");
}
}
You can still use the on method to add events for JS client hub method calls in the latest version of SignalR, but if you do not add any event listeners to a hubProxy before calling hubConnection.start(), you will not be subscribed to the hub. SignalR subscribes to the hubs you have event handlers for when the hubConnection starts. If you are not subscribed to your hub, adding any events to that hub after start() won't work.
If you add at least one event listener to the hub before start(), even if it doesn't do anything, you can then add any additional event handlers you want to the hub using on after start() and your handlers will be called.
It doesn't matter if you add an event using hubProxy.on('eventName', function (... or autogeneratedHubProxy.client.eventName = function (... before you call start(), but only on will successfully add event listeners after start() is called.
Not sure which version of SignalR you are using, but I have had more success using the following syntax on my server:
var context = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
context.Clients.All.acceptHubData("hello");
and on my clients:
myHub.client.acceptHubData = function (data) {
console.log(data);
}
I want to send some data from server to all connected clients using hubs after a specific interval. How can I accomplish this using signalr hubs.
Spin up the System.Threading.Timer, and from it's callback broadcast the message using specific hub.
Global.asax:
private Timer timer;
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.MapHubs("~/signalr2");
timer = new Timer(TimerCallback(timerCallback), null, Timeout.Infinite, 1000);
}
}
Check the “Broadcasting over a Hub from outside of a Hub” section in SignalR wiki page.
Use ReactiveExtensions and then setup an Observable.Interval call. Then reactive will automatically call the lambda which can broadcast to your clients.
I have stumbled upon this post by Jason Roberts => http://dontcodetired.com/blog/post/Using-Server-Side-Timers-and-SignalR-in-ASPNET-MVC-Applications.aspx
He uses IRegisteredObject and HostingEnvironment.RegisterObject then a System.Threading.Timer in the class that does the work, I haven't tried it myself, but it looks exactly the sort of thing.
Just add
Thread.Sleep(5000);
in your send Method.
Ex:
public void Send(string name, string message)
{
Thread.Sleep(5000);
//call the broadcast message to upadate the clients.
Clients.All.broadcastMessage(name, message);
}
Hope it helps.
Edit
The following code renders the current time for every 5 seconds.
Here is script for it:
<script type="text/javascript">
$(function () {
$.connection.hub.logging = true;
$.connection.hub.start();
// Declare a proxy to reference the hub.
var chat = $.connection.chatHub;
//Appending the responce from the server to the discussion id
chat.client.currentTime = function (time) {
$('#discussion').append("<br/>" + time + "<br/>");
};
// Start the connection.
$.connection.hub.start().done(function () {
//Call the server side method for every 5 seconds
setInterval(function () {
var date = new Date();
chat.client.currentTime(date.toString());
}, 5000);
});
});
</script>
<div id="discussion"></div>
And on the HubClass write the following:
public class ChatHub: Hub
{
public void currentTime(string date)
{
Clients.All.broadCastTime(date);
}
}