How to monitor SignalR ConnectionState in Angular 11 after SignalR connection is established continously - signalr

I am trying to find a way to monitor a SignalR connection continuously like say every minute or every 3 minutes.
Trying to find a way to check the connection state. Do I need to set up an observable that can be subscribed to. But then that would have to be in a Component or do I do that in the app.Module or app.component ?
Is it connection.OnClose();
the Angular Code in my SignalR Service class is:
Injectable({
providedIn: 'root'
})
export class SignalrService {
connection: signalR.HubConnection;
constructor() {
this.connection = new signalR.HubConnectionBuilder()
.withUrl(environment.hubAddress)
.build();
this.connect();
}
public connect() {
if (this.connection.state === signalR.HubConnectionState.Disconnected) {
this.connection.start().catch(err => console.log(err));
}
}
public getMessage(next) {
this.connection.on('SendMessage', (message) => {
next(message);
});
}
public disconnect() {
this.connection.stop();
}
}

I was able to solve this by checking connection reconnecting(), reconnected() and onClose()events:
constructor() {
this.connection = new signalR.HubConnectionBuilder()
.withUrl(environment.hubAddress)
.build();
this.connection.reconnecting() => {}
this.connection.reconnected() => {}
this.connection.onClose() => {}
this.connect();
}

Related

Can't unsubscribe from SignalR events

I have multiple Angular components that are subscribing to a SignalR hub connection. I have wrapped the SignalR hub connection object to an angular service, hubconnection.service.ts:
import { Injectable } from '#angular/core';
import { HubConnectionBuilder, HubConnection } from '#aspnet/signalr';
#Injectable()
export class HubConnectionService {
public MyHubConnection: HubConnection;
constructor() {
this.MyHubConnection = new HubConnectionBuilder()
.withUrl('/sensorHub')
.build();
this.MyHubConnection
.start()
.then(() => console.log('[HubConnectionService] Connection started'))
.catch(err => console.log('[HubConnectionService] Error while starting connection: ' + err));
}
}
Now I have a component from where I want to subscribe to the data received through the SignalR hub, to do this I import my SignalR wrapper, and in the constructor I call the .on() method. To unsubscribe from that event I call the .off() method in the destructor:
export class RawdataComponent implements OnInit {
constructor(private hubConnectionService: HubConnectionService) { }
ngOnDestroy() {
this.hubConnectionService.MyHubConnection.off('Broadcast');
}
ngOnInit() {
this.hubConnectionService.MyHubConnection.on('Broadcast',
function(sender, message) {
console.log('New message received: ' + message);
});
}
}
When I initially access the page that is described by this component, everything works as expected (console.log() is logging for every message I receive).
When I leave the page, MyHubConnection.off('Broadcast'); is called. Then after I access the previous page again, the constructor is called and another subscription is made, but the previous one is not closed, and I get two console.log() calls.
Where's the mistake I'm making? Shouldn't MyHubConnection.off('Broadcast'); close the current subscription when I leave the page?

Angular : Unable to pass API url in SignalR HubConnection

I have developed a project in Angular6. There is a requirement to develop section for live chat. For that I am using SignalR in my asp.net core Web Apiproject. Now I want to use this Web Api reference in my Angular project.
I am using this link.
But while providing the Web Api url in App.Component.ts, I am getting below error :
Constructor of class 'HubConnection' is private and only accessible
within the class declaration.
App Component.ts :
import { HubConnection } from '#aspnet/signalr';
import { Message } from 'primeng/api';
export class AppComponent implements OnInit {
public _hubConnection: HubConnection;
msgs: Message[] = [];
constructor() {
}
ngOnInit() {
this._hubConnection = new HubConnection('http://localhost:1874/notify'); // getting error on this line.
Edit : tried below code :-
Modified App.Component.ts :
import { HubConnection } from '#aspnet/signalr';
import * as signalR from '#aspnet/signalr';
import { Message } from 'primeng/api';
export class AppComponent implements OnInit {
public _hubConnection: HubConnection;
msgs: Message[] = [];
constructor() {
}
ngOnInit() {
this._hubConnection = new signalR.HubConnectionBuilder()
.withUrl('http://localhost:1874/notify')
.configureLogging(signalR.LogLevel.Information)
.build();
Error :
Failed to load
http://localhost:1874/notify/negotiate: Response to preflight request
doesn't pass access control check: The value of the
'Access-Control-Allow-Credentials' header in the response is '' which
must be 'true' when the request's credentials mode is 'include'.
Origin 'http://localhost:4200' is therefore not allowed access. The
credentials mode of requests initiated by the XMLHttpRequest is
controlled by the withCredentials attribute.
They changed the constructors for SignalR Client HubConnection to no longer allow you to pass the route in the client constructor and made the HubConnection constructor private to enforce the change. Instead, you need to use the HubConnectionBuilder as follows:
new HubConnectionBuilder().withUrl("/YOURROUTEHERE").build();
You will want to import the HubConnectionBuilder in your header as follows:
import { HubConnection, HubConnectionBuilder } from '#aspnet/signalr';
In case it helps, I also just posted an update to my RxSignalR samples for #aspnet/signalr 1.0.2, RxJs 5.5.6 and Angular 6. Check out the ReactiveChat component for an example.
In your server code, are you using CORS?
public void ConfigureServices(IServiceCollection services)
{
...
services.AddCors(options => options.AddPolicy("CorsPolicy", builder =>
{
builder.AllowAnyMethod()
.AllowAnyHeader()
.WithOrigins("http://localhost:4200")
.AllowCredentials();
}));
...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
app.UseCors("CorsPolicy");
app.UseSignalR(routes =>
{
// your routes
});
...
}
Try like this,
private hubConnection: signalr.HubConnection;
message: string;
ngOnInit() {
this.hubConnection = new signalr.HubConnection('');
this.hubConnection
.start()
.then(() => {
console.log('Connection started!');
this.hubConnection.on('ReceiveMessage', (message) => {
alert(message);
});
})
.catch(err => console.log('Error while establishing connection :('));
}

Using Signalr on Angular 5 and Asp.net WebApi

I am actually creating a chat with angular 5 and signalR on an ASP.NET Framework API. I followed the documentation but it's still not work. Here is my hub:
public class ChatHub : Hub
{
public void Hello()
{
Clients.All.hello();
}
}
Here is my startup class:
app.Map("/signalr", map =>
{
// Setup the CORS middleware to run before SignalR.
// By default this will allow all origins. You can
// configure the set of origins and/or http verbs by
// providing a cors options with a different policy.
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
EnableJSONP = true
};
// Run the SignalR pipeline. We're not using MapSignalR
// since this branch already runs under the "/signalr"
// path.
map.RunSignalR(hubConfiguration);
});
and here is my angular part which create the hubconnection:
ngOnInit() {
this._hubConnection = new HubConnection('http://localhost:58525/signalr/hubs');
this._hubConnection
.start()
.then(() => console.log('Connection started!'))
.catch(err => console.log('Error while establishing connection :( : ' + err));
this._hubConnection.on('send', data => {
console.log(data);
});
}
I get this error:
If your ASP.NET page runs on another server, then your URL looks not correct.
https://learn.microsoft.com/en-us/aspnet/signalr/overview/guide-to-the-api/hubs-api-guide-javascript-client#crossdomain
You have to connect to:
this._hubConnection = new HubConnection('http://localhost:58525/signalr');

Ionic2-Long Polling: killing http.get request while changing pages

My server is developed on Node.js. It is a long-polling service (e.g. chat): it gives the following API:
join() //listening for new events
align(fromId) //retrieving events from an id
send(data) //creating an event
The long-polling is implemented by the join(): it sends a request and the server answers when there is a new event.
Front end with Ionic2
There are 2 pages: Page1 and Page2. Where Page2 is the viewer of my events, where the long-polling communication is running.
So I start from Page1 and then I push() the second page Page2. Until now everything works fine; but if I pop() the Page2 and then push() again the Page2 then I can see that there is still running the join() of the previous instance of my Page2. This behaviour creates duplicated join(): if I push/pop Page2 many times I will have many long-polling communication with the server.
So I'm trying to find a way to kill the join() instance, which is a HTTP.get request, when leaving the page.
Let's see now my code.
This is the provider of my Ionic2 in charge of the communication with the server
import { Injectable } from '#angular/core';
import { Http, Headers } from '#angular/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
#Injectable()
export class MyProvider {
...
constructor(private http: Http) {
this.token_access = null;
this.token_room = null;
}
...
join(){
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('x-access-token',this.getToken());
return Observable.create(observer =>{
this.http.get('/localhost/chat/'+this.room,{headers : headers})
.map(res => res.json())
.subscribe(
data=>{
observer.next(data);
},
(err) =>{
observer.error(err);
}
);
})
}
send(message){
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('x-access-token',this.getToken());
headers.append('x-chat-token',this.getRoomToken());
return Observable.create(observer =>{
this.http.post('/localhost/chat/'+this.room+'/send', JSON.stringify({
event: message
}),{headers : headers})
.map(res => res.json())
.subscribe(
data=>{
observer.next(data);
},
(err) =>{
observer.error(err);
}
);
})
}
align(from){
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('x-access-token',this.getToken());
headers.append('x-chat-token',this.getRoomToken());
return Observable.create(observer =>{
this.http.post('/localhost/chat/'+this.room+'/align', JSON.stringify({
fromId: from
}),{headers : headers})
.map(res => res.json())
.subscribe(
data=>{
observer.next(data);
},
(err) =>{
observer.error(err);
}
);
})
}
}
The Page1 just push the Page2 with a button that calls the the following code (page1.ts):
...
export class Page1 {
...
constructor(public navCtrl: NavController, public myProviderService: MyProvider) {
}
.....
toPage2(){
this.navCtrl.push(Page2);
}
And my Page2 is implemented by the following code:
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import { MyProvider } from '../../providers/myprovider';
import { Event } from '../../components/event';
#Component({
selector: 'page-chat',
templateUrl: 'chat.html'
})
export class ChatPage {
eventsList: Array<Event>;
message: any;
last_event: any;
msg: any;
constructor(public navCtrl: NavController, public myProviderService: MyProvider) {
this.last_event = -1;
this.join();
this.eventsList= new Array();
}
join(){
this.myProviderService.join().subscribe(
(data)=>{
if(data.success){
this.last_event = this.last_event + 1;
if(this.last_event == data.event.id){
//up to now all events are correctly received
this.eventsList.push(data.event);
}else{
//some events are missing
this.last_event = this.last_event - 1;
this.align();
}
this.join();
}else{
this.message=data.message;
//TBD sleep....
//this.join();
}
},
(err) => {
this.message="Connectivity with server Lost...";
//TBD sleep....
//this.join();
});
}
align(){
this.myProviderService.align(this.last_event + 1).subscribe((data)=>{
if(data.success){
for (var i=0;i<data.events.length;i++) {
this.eventsList.push(new Event(data.events[i].id,data.events[i].data,data.events[i].user));
this.last_event = this.last_event + 1;
};
}else{
this.message=data.message;
}
},
(err) => {
this.message="Failure receiving messages";
});
}
send(): void{
this.myProviderService.send(this.msg).subscribe((data)=>{
if(data.success){
this.msg='';
}else this.message=data.message;
},
(err) => {
this.message="Error while authenticating";
})
}
ionViewDidLoad() {
}
ionViewDidEnter() {
}
}
So coming back to my question:
How can I kill the join() (kill the HTTP.get request) instance of my Page2 when this is not used, in order to prevent duplicated join()?
I think that if you have a provider that is globally added to Providers section of your app (meaning that it can act as a Singleton service), then you can use the following:
Every time Page 2 calls the join() method of your provider check a hasAlreadyJoined boolean variable in your provider.
This variable is set to true every time the join() method is called.
If the join() has not been called, call it and update the variable accordingly.
So, even though every time Page 2 calls the join() method of MyProvider, this method does the actual http request only if hasAlreadyJoined is false.
For you to be sure that every time a MyProvider instance is initiated it's variables are "static", the provider should be declared at the global Providers section of your app module file, not at the page's providers section.

Waiting for an answer from server on http request in Angular 2

I have a little problem with my Angular2 app. I want to get some data from server for my user login, but my code is going ahead and I have a lot of bugs with it. I want to wait for answer from server, then do something with my data.
This is my code:
import { Injectable } from '#angular/core';
import { Http, Response, Headers } from '#angular/http';
import { Observable } from 'rxjs/Rx';
import { User } from './user';
#Injectable()
export class UserService {
public usersTmp: Array<Object> = new Array<Object>();
public users: Array<User>;
public user: User = new User();
public noteToSend;
constructor(private http: Http) { }
getUsers() {
var headers = new Headers();
headers.append('Accept', 'q=0.8;application/json;q=0.9');
this.http.get('/AngularApp/api/users', { headers: headers })
.map((res: Response) => res.json())
.subscribe(
data => {
console.log(data);
this.usersTmp = data;
},
err => console.error(err),
() => console.log('done')
);
this.users = new Array<User>();
for (var i = 0; i < this.usersTmp.length; i++) {
this.user = new User();
this.user.id = this.usersTmp[i]["userId"];
this.user.name = this.usersTmp[i]["userName"];
this.user.email = this.usersTmp[i]["userEmail"];
this.user.pass = this.usersTmp[i]["userPassword"];
this.users.push(this.user);
}
return this.users;
}
As I noticed my code is going to the for loop until I get answer from server, so I return just empty array. Anyone can help me with that?
In the service, you should return the Observable that your component can subscribe to. It cannot work they way you do it due to the asynchronous mode of the get request.
As a proposal, your service could look similar to this
getUsers() {
let headers = new Headers();
headers.append('Accept', 'q=0.8;application/json;q=0.9');
return this.http.get('/AngularApp/api/users', { headers: headers })
.map((res: Response) => res.json());
}
And the relevant part of your component like this:
constructor(private userService:UserService) {
this.userService.getUsers().subscribe(
data => this.iterateOverUsers(data));
}
iterateOverUsers(data) {
// here comes your for loop
}

Resources