npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ngrx-signalr-core

v18.0.0

Published

A library to handle realtime SignalR Core events using angular and rxjs

Downloads

2,933

Readme

ngrx-signalr-core

A library to handle realtime SignalR (.NET Core) events using angular, rxjs and the @ngrx library.

This library is made for the SignalR client using .NET Core. If you need to target .NET Framework, please check this repository : https://github.com/Odonno/ngrx-signalr

Get started

Install dependencies

npm install rxjs @ngrx/store @ngrx/effects @microsoft/signalr --save
npm install ngrx-signalr-core --save

Once everything is installed, you can use the reducer and the effects inside the AppModule.

@NgModule({
    ...,
    imports: [
        StoreModule.forRoot({ signalr: signalrReducer }),
        EffectsModule.forRoot([SignalREffects, AppEffects])
    ],
    ...
})
export class AppModule { }

First, you will start the application by dispatching the creation of one Hub.

// TODO : your hub definition
const hub = {
  hubName: "hub name",
  url: "https://localhost/path",
};

this.store.dispatch(createSignalRHub(hub));

Creating a SignalR Hub is not enough. You need to start it manually.

initRealtime$ = createEffect(() =>
  this.actions$.pipe(
    ofType(signalrHubUnstarted),
    map((hub) => startSignalRHub(hub))
  )
);

Then you will create an effect to start listening to events once the hub is connected.

listenToEvents$ = createEffect(() =>
  this.actions$.pipe(
    ofType(signalrConnected),
    mergeMapHubToAction(({ hub }) => {
      // TODO : add event listeners
      const whenEvent1$ = hub
        .on("eventName1")
        .pipe(map((x) => createAction(x)));
      const whenEvent2$ = hub
        .on("eventName2")
        .pipe(map((x) => createAction(x)));

      return merge(whenEvent1$, whenEvent2$);
    })
  )
);

You can also send events at anytime.

sendEvent$ = createEffect(() =>
  this.actions$.pipe(
    ofType(SEND_EVENT), // TODO : create a custom action
    mergeMap(({ params }) => {
      const hub = findHub(timeHub);
      if (!hub) {
        return of(hubNotFound(timeHub));
      }

      // TODO : send event to the hub
      return hub.send("eventName", params).pipe(
        map((_) => sendEventFulfilled()),
        catchError((error) => of(sendEventFailed(error)))
      );
    })
  )
);

Now, start with multiple hubs at a time.

// simplified hub creation
const dispatchHubCreation = (hub) => this.store.dispatch(createSignalRHub(hub));

const hub1 = {}; // define hubName and url
const hub2 = {}; // define hubName and url
const hub3 = {}; // define hubName and url

dispatchHubCreation(hub1);
dispatchHubCreation(hub2);
dispatchHubCreation(hub3);

You will then initialize your hubs in the same way but you need to know which one is initialized.

const hub1 = {}; // define hubName and url
const hub2 = {}; // define hubName and url

initHubOne$ = createEffect(() =>
  this.actions$.pipe(
    ofType(signalrHubUnstarted),
    ofHub(hub1),
    mergeMapHubToAction(({ action, hub }) => {
      // TODO : init hub 1
    })
  )
);

initHubTwo$ = createEffect(() =>
  this.actions$.pipe(
    ofType(signalrHubUnstarted),
    ofHub(hub2),
    mergeMapHubToAction(({ action, hub }) => {
      // TODO : init hub 2
    })
  )
);

And then you can start your app when all hubs are connected the first time.

appStarted$ = createEffect(() =>
  this.store.pipe(
    select(selectAreAllHubsConnected),
    filter((areAllHubsConnected) => !!areAllHubsConnected),
    first(),
    map((_) => of(appStarted())) // TODO : create a custom action when hubs are connected
  )
);

~~Since .NET Core, you need to handle the SignalR Hub reconnection by yourself.~~

The simple way to enable hub reconnection is to enable automatic reconnect when creating the hub. You can use one of the 3 options described here:

// Using the Default reconnection policy.
// By default, the client will wait 0, 2, 10 and 30 seconds respectively before trying up to 4 reconnect attempts.
const action = createSignalRHub(hub, url, options, true);

// Using an array containing the delays in milliseconds before trying each reconnect attempt.
// The length of the array represents how many failed reconnect attempts it takes before the client will stop attempting to reconnect.
const action = createSignalRHub(hub, url, options, [10000, 20000, 30000]); // after 10s, after 20s, after 30s

// Using a custom reconnect policy.
// The retry policy that controls the timing and number of reconnect attempts.
const action = createSignalRHub(hub, url, options, {
  nextRetryDelayInMilliseconds: (context) => {
    // ...
    return 10000;
  },
});

this.store.dispatch(action);

It is currently deprecated but you can perform your own reconnection strategy using the power of @ngrx. Here is an example on how to apply periodic reconnection:

// try to reconnect all hubs every 10s (when the navigator is online)
whenDisconnected$ = createReconnectEffect(this.actions$);

In this example, we did not use a custom reconnection policy. So the default behavior will automatically be to apply a periodic reconnection attempt every 10 seconds when the hub is disconnected and when there is a network connection.

Of course, you can write your own reconnectionPolicy inside the options of the function, so you have the benefit to write your own reconnection pattern (periodic retry, exponential retry, etc..).

You can also filter by hubName so that it will affect only one hub.

API features

The SignalR Hub is an abstraction of the hub connection. It contains function you can use to:

  • start the connection
  • listen to events emitted
  • send a new event
interface ISignalRHub {
  hubName: string;
  url: string;
  options: IHttpConnectionOptions | undefined;

  start$: Observable<void>;
  stop$: Observable<void>;
  state$: Observable<string>;
  error$: Observable<Error | undefined>;

  constructor(
    hubName: string,
    url: string,
    options: IHttpConnectionOptions | undefined
  );

  start(): Observable<void>;
  stop(): Observable<void>;
  on<T>(eventName: string): Observable<T>;
  stream<T>(methodName: string, ...args: any[]): Observable<T>;
  send<T>(methodName: string, ...args: any[]): Observable<T>;
  sendStream<T>(methodName: string, subject: Subject<T>): Observable<void>;
  hasSubscriptions(): boolean;
}

You can find an existing hub by its name and url.

function findHub(hubName: string, url: string): ISignalRHub | undefined;
function findHub({
  hubName,
  url,
}: {
  hubName: string;
  url: string;
}): ISignalRHub | undefined;

And create a new hub.

function createHub(
  hubName: string,
  url: string,
  options: IHttpConnectionOptions | undefined
): ISignalRHub | undefined;

The state contains all existing hubs that was created with their according status (unstarted, connected, disconnected).

const unstarted = "unstarted";
const connected = "connected";
const disconnected = "disconnected";

type SignalRHubState =
  | typeof unstarted
  | typeof connected
  | typeof disconnected;

type SignalRHubStatus = {
  hubName: string;
  url: string;
  state: SignalRHubState;
};
class BaseSignalRStoreState {
  hubStatuses: SignalRHubStatus[];
}

Actions to dispatch

createSignalRHub will initialize a new hub connection but it won't start the connection so you can create event listeners.

const createSignalRHub = createAction(
  "@ngrx/signalr/createHub",
  props<{
    hubName: string;
    url: string;
    options?: IHttpConnectionOptions | undefined;
  }>()
);

startSignalRHub will start the hub connection so you can send and receive events.

const startSignalRHub = createAction(
  "@ngrx/signalr/startHub",
  props<{ hubName: string; url: string }>()
);

stopSignalRHub will stop the current hub connection.

const stopSignalRHub = createAction(
  "@ngrx/signalr/stopHub",
  props<{ hubName: string; url: string }>()
);

reconnectSignalRHub will give you a way to reconnect to the hub.

const reconnectSignalRHub = createAction(
  "@ngrx/signalr/reconnectHub",
  props<{ hubName: string; url: string }>()
);

hubNotFound can be used when you do retrieve your SignalR hub based on its name and url.

const hubNotFound = createAction(
  "@ngrx/signalr/hubNotFound",
  props<{ hubName: string; url: string }>()
);
// create hub automatically
createHub$;
// listen to start result (success/fail)
// listen to change connection state (connecting, connected, disconnected, reconnecting)
// listen to hub error
beforeStartHub$;
// start hub automatically
startHub$;
// stop hub
stopHub$;
// used to select all hub statuses in state
const hubStatuses$ = store.pipe(select(selectHubsStatuses));

// used to select a single hub status based on its name and url
const hubStatus$ = store.pipe(select(selectHubStatus, { hubName, url }));

// used to know if all hubs are connected
const areAllHubsConnected$ = store.pipe(select(selectAreAllHubsConnected));

// used to know when a hub is in a particular state
const hasHubState$ = store.pipe(
  select(selectHasHubState, { hubName, url, state })
);