18 | Swapping to Development environment will display more detailed information about the error that occurred.
19 |
20 |
21 | The Development environment shouldn't be enabled for deployed applications.
22 | It can result in displaying sensitive information from exceptions to end users.
23 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development
24 | and restarting the app.
25 |
19 | Swapping to the Development environment displays detailed information about the error that occurred.
20 |
21 |
22 | The Development environment shouldn't be enabled for deployed applications.
23 | It can result in displaying sensitive information from exceptions to end users.
24 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development
25 | and restarting the app.
26 |
Angular CLI integration. In development mode, there's no need to run ng serve. It runs in the background automatically, so your client-side resources are dynamically built on demand and the page refreshes when you modify any file.
11 |
Efficient production builds. In production mode, development-time features are disabled, and your dotnet publish configuration automatically invokes ng build to produce minified, ahead-of-time compiled JavaScript files.
12 |
13 |
The ClientApp subdirectory is a standard Angular CLI application. If you open a command prompt in that directory, you can run any ng command (e.g., ng test), or use npm to install extra packages into it.
Type {{data.sensorType}} - Value {{data.sensorValue}} received on {{data.timeStamp}}
6 |
7 |
8 |
--------------------------------------------------------------------------------
/signalrNg/ClientApp/src/app/sensor/sensor.component.ts:
--------------------------------------------------------------------------------
1 |
2 | import { filter } from 'rxjs/operators';
3 | import { Component, OnInit } from '@angular/core';
4 | import { Observable, BehaviorSubject } from 'rxjs';
5 | import { HubConnection, HubConnectionBuilder } from '@microsoft/signalr';
6 |
7 | @Component({
8 | selector: 'sensor',
9 | templateUrl: './sensor.component.html'
10 | })
11 | export class SensorComponent implements OnInit {
12 | private _hubConnection = new HubConnectionBuilder().withUrl('https://localhost:7183/hubs/sensor').build();
13 | dataStream: ISensorData[] = [];
14 | subject: BehaviorSubject = new BehaviorSubject({ timeStamp: new Date(), sensorType: '', sensorValue: 0 });
15 |
16 | ngOnInit() {
17 | this.subject.pipe(
18 | filter(val => val.sensorType === '1'))
19 | .subscribe({
20 | next: (value: ISensorData) => this.dataStream.push(value),
21 | error: function (err) { console.log(err); },
22 | complete: () => console.log('done')
23 | });
24 |
25 | this._hubConnection.start()
26 | .then(() => {
27 | this._hubConnection.stream('Values').subscribe({
28 | next: val => this.subject.next(val),
29 | error: err => this.subject.error(err),
30 | complete: () => this.subject.complete()
31 | });
32 | });
33 | }
34 | }
35 |
36 | interface ISensorData {
37 | timeStamp: Date;
38 | sensorType: string;
39 | sensorValue: number;
40 | }
41 |
--------------------------------------------------------------------------------
/signalrNg/ClientApp/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jwooley/SignalrRxSamples/276e13d13cea957af268dee5a0acb73de5b6023f/signalrNg/ClientApp/src/assets/.gitkeep
--------------------------------------------------------------------------------
/signalrNg/ClientApp/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/signalrNg/ClientApp/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const environment = {
6 | production: false
7 | };
8 |
9 | /*
10 | * For easier debugging in development mode, you can import the following file
11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
12 | *
13 | * This import should be commented out in production mode because it will have a negative impact
14 | * on performance if an error is thrown.
15 | */
16 | // import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
17 |
--------------------------------------------------------------------------------
/signalrNg/ClientApp/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SignalrNg
6 |
7 |
8 |
9 |
10 |
11 |
12 | Loading...
13 |
14 |
15 |
--------------------------------------------------------------------------------
/signalrNg/ClientApp/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 | import { environment } from './environments/environment';
6 |
7 | export function getBaseUrl() {
8 | return document.getElementsByTagName('base')[0].href;
9 | }
10 |
11 | const providers = [
12 | { provide: 'BASE_URL', useFactory: getBaseUrl, deps: [] }
13 | ];
14 |
15 | if (environment.production) {
16 | enableProdMode();
17 | }
18 |
19 | platformBrowserDynamic(providers).bootstrapModule(AppModule)
20 | .catch(err => console.log(err));
21 |
--------------------------------------------------------------------------------
/signalrNg/ClientApp/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/guide/browser-support
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /**
22 | * IE11 requires the following for NgClass support on SVG elements
23 | */
24 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
25 |
26 | /**
27 | * Web Animations `@angular/platform-browser/animations`
28 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
29 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
30 | */
31 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
32 |
33 | /**
34 | * By default, zone.js will patch all possible macroTask and DomEvents
35 | * user can disable parts of macroTask/DomEvents patch by setting following flags
36 | * because those flags need to be set before `zone.js` being loaded, and webpack
37 | * will put import in the top of bundle, so user need to create a separate file
38 | * in this directory (for example: zone-flags.ts), and put the following flags
39 | * into that file, and then add the following code before importing zone.js.
40 | * import './zone-flags';
41 | *
42 | * The flags allowed in zone-flags.ts are listed here.
43 | *
44 | * The following flags will work for all browsers.
45 | *
46 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
47 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
48 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
49 | *
50 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
51 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
52 | *
53 | * (window as any).__Zone_enable_cross_context_check = true;
54 | *
55 | */
56 |
57 | /***************************************************************************************************
58 | * Zone JS is required by default for Angular itself.
59 | */
60 | import 'zone.js'; // Included with Angular CLI.
61 |
62 |
63 | /***************************************************************************************************
64 | * APPLICATION IMPORTS
65 | */
66 |
--------------------------------------------------------------------------------
/signalrNg/ClientApp/src/styles.css:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
3 | /* Provide sufficient contrast against white background */
4 | a {
5 | color: #0366d6;
6 | }
7 |
8 | code {
9 | color: #e01a76;
10 | }
11 |
12 | .btn-primary {
13 | color: #fff;
14 | background-color: #1b6ec2;
15 | border-color: #1861ac;
16 | }
17 |
--------------------------------------------------------------------------------
/signalrNg/ClientApp/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | declare const require: {
11 | context(path: string, deep?: boolean, filter?: RegExp): {
12 | keys(): string[];
13 | (id: string): T;
14 | };
15 | };
16 |
17 | // First, initialize the Angular testing environment.
18 | getTestBed().initTestEnvironment(
19 | BrowserDynamicTestingModule,
20 | platformBrowserDynamicTesting()
21 | );
22 | // Then we find all the tests.
23 | const context = require.context('./', true, /\.spec\.ts$/);
24 | // And load the modules.
25 | context.keys().map(context);
26 |
--------------------------------------------------------------------------------
/signalrNg/ClientApp/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "./tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "./out-tsc/app",
6 | "types": []
7 | },
8 | "files": [
9 | "src/main.ts",
10 | "src/polyfills.ts"
11 | ],
12 | "include": [
13 | "src/**/*.d.ts"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/signalrNg/ClientApp/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "outDir": "./dist/out-tsc",
6 | "forceConsistentCasingInFileNames": true,
7 | "strict": true,
8 | "noImplicitReturns": true,
9 | "noFallthroughCasesInSwitch": true,
10 | "sourceMap": true,
11 | "declaration": false,
12 | "downlevelIteration": true,
13 | "experimentalDecorators": true,
14 | "moduleResolution": "node",
15 | "importHelpers": true,
16 | "target": "es2017",
17 | "module": "es2020",
18 | "lib": [
19 | "es2018",
20 | "dom"
21 | ]
22 | },
23 | "angularCompilerOptions": {
24 | "enableI18nLegacyMessageIdFormat": false,
25 | "strictInjectionParameters": true,
26 | "strictInputAccessModifiers": true,
27 | "strictTemplates": true
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/signalrNg/ClientApp/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "./tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "./out-tsc/spec",
6 | "types": [
7 | "jasmine",
8 | "node"
9 | ]
10 | },
11 | "files": [
12 | "src/test.ts",
13 | "src/polyfills.ts"
14 | ],
15 | "include": [
16 | "src/**/*.spec.ts",
17 | "src/**/*.d.ts"
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/signalrNg/Hubs/ChatHub.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.SignalR;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 |
5 | namespace SignalrNg.Hubs
6 | {
7 | public class ChatHub : Hub
8 | {
9 | public Task Send(string data)
10 | {
11 | var currentUser = new List { Context.ConnectionId };
12 | return Clients.AllExcept(currentUser).SendAsync("Send", data);
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/signalrNg/Hubs/DragHub.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.SignalR;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 |
7 | namespace SignalrNg.Hubs
8 | {
9 | public class DragHub : Hub
10 | {
11 | public async Task DragDrop(Coords position)
12 | {
13 | await Clients.Others.SendAsync("Dragged", position);
14 | }
15 | }
16 | public class Coords
17 | {
18 | public int X { get; set; }
19 | public int Y { get; set; }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/signalrNg/Hubs/SensorHub.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.SignalR;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Reactive.Linq;
6 | using System.Threading.Channels;
7 | using System.Threading.Tasks;
8 |
9 | namespace SignalrNg.Hubs
10 | {
11 | public class SensorHub : Hub
12 | {
13 | private static IObservable? _Sensor = null;
14 |
15 | public ChannelReader Values()
16 | {
17 | if (_Sensor == null)
18 | {
19 | var rand = new Random(DateTime.Now.Millisecond);
20 | _Sensor = Observable.Generate(
21 | initialState: 0.0,
22 | condition: x => true,
23 | iterate: inVal => rand.NextDouble(),
24 | resultSelector: val => new SensorData
25 | {
26 | TimeStamp = DateTime.Now,
27 | SensorType = (Math.Floor(val * 4) + 1).ToString(),
28 | SensorValue = val * 20
29 | },
30 | timeSelector: val => TimeSpan.FromMilliseconds(val * 1000))
31 | .Publish()
32 | .AutoConnect(1);
33 | }
34 | return _Sensor.AsChannelReader();
35 | }
36 | }
37 |
38 | public static class ObservableExtensions
39 | {
40 | public static ChannelReader AsChannelReader(this IObservable observable, int? maxBufferSize = null)
41 | {
42 | // This sample shows adapting an observable to a ChannelReader without
43 | // back pressure, if the connection is slower than the producer, memory will
44 | // start to increase.
45 |
46 | // If the channel is bounded, TryWrite will return false and effectively
47 | // drop items.
48 |
49 | // The other alternative is to use a bounded channel, and when the limit is reached
50 | // block on WaitToWriteAsync. This will block a thread pool thread and isn't recommended and isn't shown here.
51 | var channel = maxBufferSize != null ? Channel.CreateBounded(maxBufferSize.Value) : Channel.CreateUnbounded();
52 |
53 | var disposable = observable.Subscribe(
54 | value => channel.Writer.TryWrite(value),
55 | error => channel.Writer.TryComplete(error),
56 | () => channel.Writer.TryComplete());
57 |
58 | // Complete the subscription on the reader completing
59 | channel.Reader.Completion.ContinueWith(task => disposable.Dispose());
60 |
61 | return channel.Reader;
62 | }
63 | }
64 |
65 | public class SensorData
66 | {
67 | public DateTime TimeStamp { get; set; }
68 | public string SensorType { get; set; } = "";
69 | public double SensorValue { get; set; }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/signalrNg/Pages/Error.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model ErrorModel
3 | @{
4 | ViewData["Title"] = "Error";
5 | }
6 |
7 |
Error.
8 |
An error occurred while processing your request.
9 |
10 | @if (Model.ShowRequestId)
11 | {
12 |
13 | Request ID:@Model.RequestId
14 |
15 | }
16 |
17 |
Development Mode
18 |
19 | Swapping to the Development environment displays detailed information about the error that occurred.
20 |
21 |
22 | The Development environment shouldn't be enabled for deployed applications.
23 | It can result in displaying sensitive information from exceptions to end users.
24 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development
25 | and restarting the app.
26 |