Too Long; Didn't Read: How does one implement a strongly-typed SignalR hub (custom methods) in Angular 6?
Ultimately, I'm trying to use a Strongly-Typed SignalR Hub in Angular 6 using services and interfaces. I'll start with the Hub first. Since SignalR has the option to now strongly-typed commands, I'd like to use these for better securing data. I wouldn't mind also having a way to do authentication, but that may be a story for a different time.
ApHub.cs
using System.Threading.Tasks;
using AckermanPuglisi.Thesaurus;
using Microsoft.AspNetCore.SignalR;
namespace Tinhalo.Hubs {
public class ApHub : Hub<INlgClient> {
private readonly IApContext _apContext;
public ApHub(IApContext apContext) {
_apContext = apContext;
}
public async Task GetTraits() {
await Clients.Caller.GetAllTraits(_apContext.Traits);
}
public async Task GetEmotions() {
await Clients.Caller.GetAllEmotions(_apContext.Emotions);
}
public async Task ConnectionMessage() {
await Clients.Caller.CheckConnection("You are connected");
}
}
}
From there, I create the interface for SignalR to have the typed methods that I'll need to call in Angular. I've made the models already in a models folder, but I'll leave out the details on that because I'm not sure if they are relevant.
INlgClient.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AckermanPuglisi.Thesaurus.Data.Graph.Models;
namespace Tinhalo.Hubs {
public interface INlgClient {
Task GetAllTraits(ICharacterTrait[] apContextTraits);
Task GetTrait(ICharacterTrait trait);
Task GetAllEmotions(Emotion[] apContextEmotions);
Task GetEmotion(Emotion emotion);
Task GetTraitEmotions(string traitName);
Task CheckConnection(string connectMessage);
}
}
These previous two things work for sure when they are un-typed. After that, I do some stuff in Angular 6, namely the service (which I've found out you can't start() the hub connection until you have window
defined... else you get a XMLHttpRequest undefined error)
signal.service.ts
import { Injectable, Inject, OnInit, AfterViewInit } from '@angular/core';
import { HubConnection, HubConnectionBuilder, JsonHubProtocol } from "@aspnet/signalr";
import { Emotion } from '../models/emotion.model';
import { Trait } from '../models/trait.model';
import * as signalr from '@aspnet/signalr';
import { Subject } from 'rxjs';
@Injectable({
providedIn: "root"
})
export class SignalService {
public aphub: HubConnection;
public hubConnected: any;
constructor(@Inject('BASE_URL') baseUrl: string) {
let options = {
transport: signalr.HttpTransportType.WebSockets
| signalr.HttpTransportType.LongPolling,
AccessTokenFactory: async () => {
// Get and return the access token.
// This function can return a JavaScript Promise if asynchronous
// logic is required to retrieve the access token.
}
};
let protocol = new signalr.JsonHubProtocol();
this.aphub = new signalr.HubConnectionBuilder()
.configureLogging(signalr.LogLevel.Trace)
.withUrl(`${baseUrl}aphub`, options)
.withHubProtocol(protocol).build();
}
}
Now, if I do this.aphub.getTraits((traits) => { ... });
which is the equivalent to hub.invoke('getTraits' ... )
, it'll say that the method isn't defined at design-time but will show up on inspection during run time. This tells me that I totally need an interface for this - which I sorta have. I think the big issue is that I'll likely be able to implement my custom methods on the new class, but I don't know what I must custom implement because of the mix of private/internal stuff to SignalR HubConnections.
I may not even be implementing the right object, even.
aphub.interface.ts
import { HubConnection } from "@aspnet/signalr";
interface IAphub extends HubConnection {
getTraits(): void;
getTrait(traitName: string): void;
getTraitEmotions(traitName: string): void;
getEmotions(): void;
getEmotion(emotionName: string): void;
connectionMessage(): void;
}
class ApHub implements IAphub {
/* What goes here? */
}
Please be kind, I'm fairly new to Angular itself but I get definitely get the C# part of it. I've been in MVC for quite a while - Typescript is a bit new to me, but I get most of it. SignalR seems the best way to go about asynchronous data because it has more than .Caller
, but also .Broadcast
which will come in handy later on down the line.
Google has a tonne of stuff on Angular, but with Angular 1.x, Angular 2.x, and all of the versioning confusion (ng-thing vs. ng2-thing vs. ngx-thing plus stuff with $
and $scope
and again with dumptrucks full of Angular 4.x code), I just want to find an answer for my AspNetCore 2.1 (to be 3.0), SignalR@latest, and Angular 6 website.
I have tried to generate an interface using SignalR.exe, but it doesn't work saying it is missing a manifest - which is likely because it is a dotnet core application.
What does here?
. – Lazar Ljubenović