23 |
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/app/stock/stock.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from "@angular/core";
2 |
3 | import { stockSignalRService } from "../services/stock.signalR.service";
4 | import { forEach } from "@angular/router/src/utils/collection";
5 |
6 |
7 | @Component({
8 | templateUrl: './stock.component.html',
9 | selector:"app-stock"
10 | })
11 |
12 | export class StockComponent {
13 |
14 | stocks = [];
15 | marketStatus: string;
16 |
17 | constructor(private stockService: stockSignalRService) {
18 | this.stocks = [];
19 | this.marketStatus = 'closed';
20 | //subscribe for connection eastablish
21 | //fetch the stocks details
22 | stockService.connectionEstablished.subscribe(() => {
23 | stockService.getAllStocks().then((data) => {
24 | this.stocks = data;
25 | });
26 | });
27 |
28 | //subscribe for market open
29 | stockService.marketOpened.subscribe(() => {
30 | this.marketStatus = 'open';
31 | this.startStrearming();
32 | });
33 |
34 | //subscribe for market close
35 | stockService.marketClosed.subscribe(() => {
36 | this.marketStatus = 'closed';
37 | });
38 |
39 | }
40 |
41 | openMarketClicked() {
42 | this.stockService.openMarket();
43 | }
44 |
45 | startStrearming() {
46 | this.stockService.startStreaming().subscribe({
47 | next: (data) => {
48 | this.displayStock(data);
49 | },
50 | error: function (err) {
51 | console.log('Error:' + err);
52 | },
53 | complete: function () {
54 | console.log('completed');
55 | }
56 | });
57 | }
58 |
59 | closeMarketClicked() {
60 | this.stockService.CloseMarket();
61 | }
62 |
63 | resetClicked() {
64 | this.stockService.ResetMarket();
65 | }
66 |
67 | displayStock(stock) {
68 | console.log("stock updated:" + stock.symbol);
69 | for (let i in this.stocks) {
70 | //console.log(i);
71 | if (this.stocks[i].symbol == stock.symbol) {
72 | this.stocks[i] = stock;
73 | }
74 | }
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nemi-chand/ASPNETCore-SignalR-Angular-TypeScript/51619eea62d9423ac1a082b67ec56de8a4821c38/ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/assets/.gitkeep
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // The file contents for the current environment will overwrite these during build.
2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do
3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead.
4 | // The list of which env maps to which file can be found in `.angular-cli.json`.
5 |
6 | export const environment = {
7 | production: false
8 | };
9 |
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ASPNETCore_SignalR_Angular_TypeScript
6 |
7 |
8 |
9 |
10 |
11 |
12 | Loading...
13 |
14 |
15 |
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/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 |
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/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/docs/ts/latest/guide/browser-support.html
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/
22 | // import 'core-js/es6/symbol';
23 | // import 'core-js/es6/object';
24 | // import 'core-js/es6/function';
25 | // import 'core-js/es6/parse-int';
26 | // import 'core-js/es6/parse-float';
27 | // import 'core-js/es6/number';
28 | // import 'core-js/es6/math';
29 | // import 'core-js/es6/string';
30 | // import 'core-js/es6/date';
31 | // import 'core-js/es6/array';
32 | // import 'core-js/es6/regexp';
33 | // import 'core-js/es6/map';
34 | // import 'core-js/es6/weak-map';
35 | // import 'core-js/es6/set';
36 |
37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */
38 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
39 |
40 | /** IE10 and IE11 requires the following for the Reflect API. */
41 | // import 'core-js/es6/reflect';
42 |
43 |
44 | /** Evergreen browsers require these. **/
45 | // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
46 | import 'core-js/es7/reflect';
47 |
48 |
49 | /**
50 | * Required to support Web Animations `@angular/platform-browser/animations`.
51 | * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
52 | **/
53 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
54 |
55 | /**
56 | * By default, zone.js will patch all possible macroTask and DomEvents
57 | * user can disable parts of macroTask/DomEvents patch by setting following flags
58 | */
59 |
60 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
61 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
62 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
63 |
64 | /*
65 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
66 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
67 | */
68 | // (window as any).__Zone_enable_cross_context_check = true;
69 |
70 | /***************************************************************************************************
71 | * Zone JS is required by default for Angular itself.
72 | */
73 | import 'zone.js/dist/zone'; // Included with Angular CLI.
74 |
75 |
76 |
77 | /***************************************************************************************************
78 | * APPLICATION IMPORTS
79 | */
80 |
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/styles.css:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/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/dist/zone-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: any;
11 |
12 | // First, initialize the Angular testing environment.
13 | getTestBed().initTestEnvironment(
14 | BrowserDynamicTestingModule,
15 | platformBrowserDynamicTesting()
16 | );
17 | // Then we find all the tests.
18 | const context = require.context('./', true, /\.spec\.ts$/);
19 | // And load the modules.
20 | context.keys().map(context);
21 |
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "baseUrl": "./",
6 | "module": "es2015",
7 | "types": []
8 | },
9 | "exclude": [
10 | "test.ts",
11 | "**/*.spec.ts"
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/spec",
5 | "baseUrl": "./",
6 | "module": "commonjs",
7 | "types": [
8 | "jasmine",
9 | "node"
10 | ]
11 | },
12 | "files": [
13 | "test.ts"
14 | ],
15 | "include": [
16 | "**/*.spec.ts",
17 | "**/*.d.ts"
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/typings.d.ts:
--------------------------------------------------------------------------------
1 | /* SystemJS module definition */
2 | declare var module: NodeModule;
3 | interface NodeModule {
4 | id: string;
5 | }
6 |
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/ClientApp/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "outDir": "./dist/out-tsc",
5 | "sourceMap": true,
6 | "declaration": false,
7 | "moduleResolution": "node",
8 | "emitDecoratorMetadata": true,
9 | "experimentalDecorators": true,
10 | "target": "es5",
11 | "typeRoots": [
12 | "node_modules/@types"
13 | ],
14 | "lib": [
15 | "es2017",
16 | "dom"
17 | ]
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/ClientApp/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rulesDirectory": [
3 | "node_modules/codelyzer"
4 | ],
5 | "rules": {
6 | "arrow-return-shorthand": true,
7 | "callable-types": true,
8 | "class-name": true,
9 | "comment-format": [
10 | true,
11 | "check-space"
12 | ],
13 | "curly": true,
14 | "deprecation": {
15 | "severity": "warn"
16 | },
17 | "eofline": true,
18 | "forin": true,
19 | "import-blacklist": [
20 | true,
21 | "rxjs",
22 | "rxjs/Rx"
23 | ],
24 | "import-spacing": true,
25 | "indent": [
26 | true,
27 | "spaces"
28 | ],
29 | "interface-over-type-literal": true,
30 | "label-position": true,
31 | "max-line-length": [
32 | true,
33 | 140
34 | ],
35 | "member-access": false,
36 | "member-ordering": [
37 | true,
38 | {
39 | "order": [
40 | "static-field",
41 | "instance-field",
42 | "static-method",
43 | "instance-method"
44 | ]
45 | }
46 | ],
47 | "no-arg": true,
48 | "no-bitwise": true,
49 | "no-console": [
50 | true,
51 | "debug",
52 | "info",
53 | "time",
54 | "timeEnd",
55 | "trace"
56 | ],
57 | "no-construct": true,
58 | "no-debugger": true,
59 | "no-duplicate-super": true,
60 | "no-empty": false,
61 | "no-empty-interface": true,
62 | "no-eval": true,
63 | "no-inferrable-types": [
64 | true,
65 | "ignore-params"
66 | ],
67 | "no-misused-new": true,
68 | "no-non-null-assertion": true,
69 | "no-shadowed-variable": true,
70 | "no-string-literal": false,
71 | "no-string-throw": true,
72 | "no-switch-case-fall-through": true,
73 | "no-trailing-whitespace": true,
74 | "no-unnecessary-initializer": true,
75 | "no-unused-expression": true,
76 | "no-use-before-declare": true,
77 | "no-var-keyword": true,
78 | "object-literal-sort-keys": false,
79 | "one-line": [
80 | true,
81 | "check-open-brace",
82 | "check-catch",
83 | "check-else",
84 | "check-whitespace"
85 | ],
86 | "prefer-const": true,
87 | "quotemark": [
88 | true,
89 | "single"
90 | ],
91 | "radix": true,
92 | "semicolon": [
93 | true,
94 | "always"
95 | ],
96 | "triple-equals": [
97 | true,
98 | "allow-null-check"
99 | ],
100 | "typedef-whitespace": [
101 | true,
102 | {
103 | "call-signature": "nospace",
104 | "index-signature": "nospace",
105 | "parameter": "nospace",
106 | "property-declaration": "nospace",
107 | "variable-declaration": "nospace"
108 | }
109 | ],
110 | "unified-signatures": true,
111 | "variable-name": false,
112 | "whitespace": [
113 | true,
114 | "check-branch",
115 | "check-decl",
116 | "check-operator",
117 | "check-separator",
118 | "check-type"
119 | ],
120 | "directive-selector": [
121 | true,
122 | "attribute",
123 | "app",
124 | "camelCase"
125 | ],
126 | "component-selector": [
127 | true,
128 | "element",
129 | "app",
130 | "kebab-case"
131 | ],
132 | "no-output-on-prefix": true,
133 | "use-input-property-decorator": true,
134 | "use-output-property-decorator": true,
135 | "use-host-property-decorator": true,
136 | "no-input-rename": true,
137 | "no-output-rename": true,
138 | "use-life-cycle-interface": true,
139 | "use-pipe-transform-interface": true,
140 | "component-class-suffix": true,
141 | "directive-class-suffix": true
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/Controllers/SampleDataController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using Microsoft.AspNetCore.Mvc;
6 |
7 | namespace ASPNETCore_SignalR_Angular_TypeScript.Controllers
8 | {
9 | [Route("api/[controller]")]
10 | public class SampleDataController : Controller
11 | {
12 | private static string[] Summaries = new[]
13 | {
14 | "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
15 | };
16 |
17 | [HttpGet("[action]")]
18 | public IEnumerable WeatherForecasts()
19 | {
20 | var rng = new Random();
21 | return Enumerable.Range(1, 5).Select(index => new WeatherForecast
22 | {
23 | DateFormatted = DateTime.Now.AddDays(index).ToString("d"),
24 | TemperatureC = rng.Next(-20, 55),
25 | Summary = Summaries[rng.Next(Summaries.Length)]
26 | });
27 | }
28 |
29 | public class WeatherForecast
30 | {
31 | public string DateFormatted { get; set; }
32 | public int TemperatureC { get; set; }
33 | public string Summary { get; set; }
34 |
35 | public int TemperatureF
36 | {
37 | get
38 | {
39 | return 32 + (int)(TemperatureC / 0.5556);
40 | }
41 | }
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/Hubs/ChatHub.cs:
--------------------------------------------------------------------------------
1 | using ASPNETCore_SignalR_Angular_TypeScript.Models;
2 | using Microsoft.AspNetCore.SignalR;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 |
8 | namespace ASPNETCore_SignalR_Angular_TypeScript.Hubs
9 | {
10 | public class ChatHub : Hub
11 | {
12 | public async Task SendMessage(ChatMessage message)
13 | {
14 | await Clients.All.SendAsync("ReceiveMessage", message);
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/Hubs/DynamicChatHub.cs:
--------------------------------------------------------------------------------
1 | using ASPNETCore_SignalR_Angular_TypeScript.Models;
2 | using Microsoft.AspNetCore.SignalR;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 |
8 | namespace ASPNETCore_SignalR_Angular_TypeScript.Hubs
9 | {
10 | public class DynamicChatHub : DynamicHub
11 | {
12 | public async Task SendMessage(ChatMessage message)
13 | {
14 | await Clients.All.Send(message);
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/Hubs/StockTickerHub.cs:
--------------------------------------------------------------------------------
1 | using ASPNETCore_SignalR_Angular_TypeScript.Models;
2 | using Microsoft.AspNetCore.SignalR;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Threading.Channels;
7 | using System.Threading.Tasks;
8 |
9 | namespace ASPNETCore_SignalR_Angular_TypeScript.Hubs
10 | {
11 |
12 | ///
13 | /// stock ticker hub
14 | ///
15 | public class StockTickerHub : Hub
16 | {
17 | private StockTicker _stockTicker;
18 |
19 | public StockTickerHub(StockTicker stockTicker)
20 | {
21 | this._stockTicker = stockTicker;
22 | }
23 |
24 | public IEnumerable GetAllStocks()
25 | {
26 | return _stockTicker.GetAllStocks();
27 | }
28 |
29 | public ChannelReader StreamStocks()
30 | {
31 | return _stockTicker.StreamStocks().AsChannelReader(10);
32 | }
33 |
34 | public string GetMarketState()
35 | {
36 | return _stockTicker.MarketState.ToString();
37 | }
38 |
39 | public async Task OpenMarket()
40 | {
41 | await _stockTicker.OpenMarket();
42 | }
43 |
44 | public async Task CloseMarket()
45 | {
46 | await _stockTicker.CloseMarket();
47 | }
48 |
49 | public async Task Reset()
50 | {
51 | await _stockTicker.Reset();
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/Hubs/TChatHub.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 ASPNETCore_SignalR_Angular_TypeScript.Hubs
8 | {
9 | public class TChatHub : Hub
10 | {
11 |
12 | public Task Send(string message)
13 | {
14 | return Clients.All.Send(message);
15 | }
16 |
17 | }
18 |
19 | public interface IChatClient
20 | {
21 | Task Send(string message);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/Models/ChatMessage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace ASPNETCore_SignalR_Angular_TypeScript.Models
7 | {
8 | public class ChatMessage
9 | {
10 | public string user { get; set; }
11 |
12 | public string message { get; set; }
13 |
14 | public string room { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/Models/Stock.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace ASPNETCore_SignalR_Angular_TypeScript.Models
7 | {
8 | public class Stock
9 | {
10 | private decimal _price;
11 |
12 | public string Symbol { get; set; }
13 |
14 | public decimal DayOpen { get; private set; }
15 |
16 | public decimal DayLow { get; private set; }
17 |
18 | public decimal DayHigh { get; private set; }
19 |
20 | public decimal LastChange { get; private set; }
21 |
22 | public decimal Change
23 | {
24 | get
25 | {
26 | return Price - DayOpen;
27 | }
28 | }
29 |
30 | public double PercentChange
31 | {
32 | get
33 | {
34 | return (double)Math.Round(Change / Price, 4);
35 | }
36 | }
37 |
38 | public decimal Price
39 | {
40 | get
41 | {
42 | return _price;
43 | }
44 | set
45 | {
46 | if (_price == value)
47 | {
48 | return;
49 | }
50 |
51 | LastChange = value - _price;
52 | _price = value;
53 |
54 | if (DayOpen == 0)
55 | {
56 | DayOpen = _price;
57 | }
58 | if (_price < DayLow || DayLow == 0)
59 | {
60 | DayLow = _price;
61 | }
62 | if (_price > DayHigh)
63 | {
64 | DayHigh = _price;
65 | }
66 | }
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/ObservableExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Channels;
3 |
4 | namespace ASPNETCore_SignalR_Angular_TypeScript
5 | {
6 | public static class ObservableExtensions
7 | {
8 | public static ChannelReader AsChannelReader(this IObservable observable, int? maxBufferSize = null)
9 | {
10 | // This sample shows adapting an observable to a ChannelReader without
11 | // back pressure, if the connection is slower than the producer, memory will
12 | // start to increase.
13 |
14 | // If the channel is bounded, TryWrite will return false and effectively
15 | // drop items.
16 |
17 | // The other alternative is to use a bounded channel, and when the limit is reached
18 | // block on WaitToWriteAsync. This will block a thread pool thread and isn't recommended and isn't shown here.
19 | var channel = maxBufferSize != null ? Channel.CreateBounded(maxBufferSize.Value) : Channel.CreateUnbounded();
20 |
21 | var disposable = observable.Subscribe(
22 | value => channel.Writer.TryWrite(value),
23 | error => channel.Writer.TryComplete(error),
24 | () => channel.Writer.TryComplete());
25 |
26 | // Complete the subscription on the reader completing
27 | channel.Reader.Completion.ContinueWith(task => disposable.Dispose());
28 |
29 | return channel.Reader;
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/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 Development environment will display more detailed information about the error that occurred.
20 |
21 |
22 | Development environment should not be enabled in deployed applications, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the ASPNETCORE_ENVIRONMENT environment variable to Development, and restarting the application.
23 |
24 |
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/Pages/Error.cshtml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 | using Microsoft.AspNetCore.Mvc;
7 | using Microsoft.AspNetCore.Mvc.RazorPages;
8 |
9 | namespace ASPNETCore_SignalR_Angular_TypeScript.Pages
10 | {
11 | public class ErrorModel : PageModel
12 | {
13 | public string RequestId { get; set; }
14 |
15 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
16 |
17 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
18 | public void OnGet()
19 | {
20 | RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/Pages/_ViewImports.cshtml:
--------------------------------------------------------------------------------
1 | @using ASPNETCore_SignalR_Angular_TypeScript
2 | @namespace ASPNETCore_SignalR_Angular_TypeScript.Pages
3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
4 |
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 | using Microsoft.AspNetCore;
7 | using Microsoft.AspNetCore.Hosting;
8 | using Microsoft.Extensions.Configuration;
9 | using Microsoft.Extensions.Logging;
10 |
11 | namespace ASPNETCore_SignalR_Angular_TypeScript
12 | {
13 | public class Program
14 | {
15 | public static void Main(string[] args)
16 | {
17 | CreateWebHostBuilder(args).Build().Run();
18 | }
19 |
20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
21 | WebHost.CreateDefaultBuilder(args)
22 | .UseStartup();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/Startup.cs:
--------------------------------------------------------------------------------
1 | using ASPNETCore_SignalR_Angular_TypeScript.Hubs;
2 | using Microsoft.AspNetCore.Builder;
3 | using Microsoft.AspNetCore.Hosting;
4 | using Microsoft.AspNetCore.HttpsPolicy;
5 | using Microsoft.AspNetCore.Mvc;
6 | using Microsoft.AspNetCore.SpaServices.AngularCli;
7 | using Microsoft.Extensions.Configuration;
8 | using Microsoft.Extensions.DependencyInjection;
9 |
10 | namespace ASPNETCore_SignalR_Angular_TypeScript
11 | {
12 | public class Startup
13 | {
14 | public Startup(IConfiguration configuration)
15 | {
16 | Configuration = configuration;
17 | }
18 |
19 | public IConfiguration Configuration { get; }
20 |
21 | // This method gets called by the runtime. Use this method to add services to the container.
22 | public void ConfigureServices(IServiceCollection services)
23 | {
24 | services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
25 |
26 | // In production, the Angular files will be served from this directory
27 | services.AddSpaStaticFiles(configuration =>
28 | {
29 | configuration.RootPath = "ClientApp/dist";
30 | });
31 |
32 | services.AddSignalR();
33 |
34 | services.AddSingleton();
35 | }
36 |
37 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
38 | public void Configure(IApplicationBuilder app, IHostingEnvironment env)
39 | {
40 | if (env.IsDevelopment())
41 | {
42 | app.UseDeveloperExceptionPage();
43 | }
44 | else
45 | {
46 | app.UseExceptionHandler("/Error");
47 | app.UseHsts();
48 | }
49 |
50 | //app.UseHttpsRedirection();
51 | app.UseStaticFiles();
52 | app.UseSpaStaticFiles();
53 |
54 | app.UseSignalR(route =>
55 | {
56 | route.MapHub("/chathub");
57 | route.MapHub("/stock");
58 | route.MapHub("/dynamichub");
59 | });
60 |
61 | app.UseMvc(routes =>
62 | {
63 | routes.MapRoute(
64 | name: "default",
65 | template: "{controller}/{action=Index}/{id?}");
66 | });
67 |
68 | app.UseSpa(spa =>
69 | {
70 | // To learn more about options for serving an Angular SPA from ASP.NET Core,
71 | // see https://go.microsoft.com/fwlink/?linkid=864501
72 |
73 | spa.Options.SourcePath = "ClientApp";
74 |
75 | if (env.IsDevelopment())
76 | {
77 | spa.UseAngularCliServer(npmScript: "start");
78 | }
79 | });
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/StockTicker.cs:
--------------------------------------------------------------------------------
1 | using ASPNETCore_SignalR_Angular_TypeScript.Hubs;
2 | using ASPNETCore_SignalR_Angular_TypeScript.Models;
3 | using Microsoft.AspNetCore.SignalR;
4 | using System;
5 | using System.Collections.Concurrent;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Reactive.Subjects;
9 | using System.Threading;
10 | using System.Threading.Tasks;
11 |
12 | namespace ASPNETCore_SignalR_Angular_TypeScript
13 | {
14 | public class StockTicker
15 | {
16 | private readonly SemaphoreSlim _marketStateLock = new SemaphoreSlim(1, 1);
17 | private readonly SemaphoreSlim _updateStockPricesLock = new SemaphoreSlim(1, 1);
18 |
19 | private readonly ConcurrentDictionary _stocks = new ConcurrentDictionary();
20 |
21 | private readonly Subject _subject = new Subject();
22 |
23 | // Stock can go up or down by a percentage of this factor on each change
24 | private readonly double _rangePercent = 0.002;
25 |
26 | private readonly TimeSpan _updateInterval = TimeSpan.FromMilliseconds(250);
27 | private readonly Random _updateOrNotRandom = new Random();
28 |
29 | private Timer _timer;
30 | private volatile bool _updatingStockPrices;
31 | private volatile MarketState _marketState;
32 |
33 | public StockTicker(IHubContext hub)
34 | {
35 | Hub = hub;
36 | LoadDefaultStocks();
37 | }
38 |
39 | private IHubContext Hub
40 | {
41 | get;
42 | set;
43 | }
44 |
45 | public MarketState MarketState
46 | {
47 | get { return _marketState; }
48 | private set { _marketState = value; }
49 | }
50 |
51 | public IEnumerable GetAllStocks()
52 | {
53 | return _stocks.Values;
54 | }
55 |
56 | public IObservable StreamStocks()
57 | {
58 | return _subject;
59 | }
60 |
61 | public async Task OpenMarket()
62 | {
63 | await _marketStateLock.WaitAsync();
64 | try
65 | {
66 | if (MarketState != MarketState.Open)
67 | {
68 | _timer = new Timer(UpdateStockPrices, null, _updateInterval, _updateInterval);
69 |
70 | MarketState = MarketState.Open;
71 |
72 | await BroadcastMarketStateChange(MarketState.Open);
73 | }
74 | }
75 | finally
76 | {
77 | _marketStateLock.Release();
78 | }
79 | }
80 |
81 | public async Task CloseMarket()
82 | {
83 | await _marketStateLock.WaitAsync();
84 | try
85 | {
86 | if (MarketState == MarketState.Open)
87 | {
88 | if (_timer != null)
89 | {
90 | _timer.Dispose();
91 | }
92 |
93 | MarketState = MarketState.Closed;
94 |
95 | await BroadcastMarketStateChange(MarketState.Closed);
96 | }
97 | }
98 | finally
99 | {
100 | _marketStateLock.Release();
101 | }
102 | }
103 |
104 | public async Task Reset()
105 | {
106 | await _marketStateLock.WaitAsync();
107 | try
108 | {
109 | if (MarketState != MarketState.Closed)
110 | {
111 | throw new InvalidOperationException("Market must be closed before it can be reset.");
112 | }
113 |
114 | LoadDefaultStocks();
115 | await BroadcastMarketReset();
116 | }
117 | finally
118 | {
119 | _marketStateLock.Release();
120 | }
121 | }
122 |
123 | private void LoadDefaultStocks()
124 | {
125 | _stocks.Clear();
126 |
127 | var stocks = new List
128 | {
129 | new Stock { Symbol = "HDFC Bank", Price = 2049.35m },
130 | new Stock { Symbol = "Bharti Airtel", Price = 377.55m },
131 | new Stock { Symbol = "SBI", Price = 273.00m },
132 | new Stock { Symbol = "Reliance", Price = 984.35m }
133 | };
134 |
135 | stocks.ForEach(stock => _stocks.TryAdd(stock.Symbol, stock));
136 | }
137 |
138 | private async void UpdateStockPrices(object state)
139 | {
140 | // This function must be re-entrant as it's running as a timer interval handler
141 | await _updateStockPricesLock.WaitAsync();
142 | try
143 | {
144 | if (!_updatingStockPrices)
145 | {
146 | _updatingStockPrices = true;
147 |
148 | foreach (var stock in _stocks.Values)
149 | {
150 | TryUpdateStockPrice(stock);
151 |
152 | _subject.OnNext(stock);
153 | }
154 |
155 | _updatingStockPrices = false;
156 | }
157 | }
158 | finally
159 | {
160 | _updateStockPricesLock.Release();
161 | }
162 | }
163 |
164 | private bool TryUpdateStockPrice(Stock stock)
165 | {
166 | // Randomly choose whether to udpate this stock or not
167 | var r = _updateOrNotRandom.NextDouble();
168 | if (r > 0.1)
169 | {
170 | return false;
171 | }
172 |
173 | // Update the stock price by a random factor of the range percent
174 | var random = new Random((int)Math.Floor(stock.Price));
175 | var percentChange = random.NextDouble() * _rangePercent;
176 | var pos = random.NextDouble() > 0.51;
177 | var change = Math.Round(stock.Price * (decimal)percentChange, 2);
178 | change = pos ? change : -change;
179 |
180 | stock.Price += change;
181 | return true;
182 | }
183 |
184 | private async Task BroadcastMarketStateChange(MarketState marketState)
185 | {
186 | switch (marketState)
187 | {
188 | case MarketState.Open:
189 | await Hub.Clients.All.SendAsync("marketOpened");
190 | break;
191 | case MarketState.Closed:
192 | await Hub.Clients.All.SendAsync("marketClosed");
193 | break;
194 | default:
195 | break;
196 | }
197 | }
198 |
199 | private async Task BroadcastMarketReset()
200 | {
201 | await Hub.Clients.All.SendAsync("marketReset");
202 | }
203 | }
204 |
205 | public enum MarketState
206 | {
207 | Closed,
208 | Open
209 | }
210 | }
211 |
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "System": "Information",
6 | "Microsoft": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Warning"
5 | }
6 | },
7 | "AllowedHosts": "*"
8 | }
9 |
--------------------------------------------------------------------------------
/ASPNETCore-SignalR-Angular-TypeScript/wwwroot/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nemi-chand/ASPNETCore-SignalR-Angular-TypeScript/51619eea62d9423ac1a082b67ec56de8a4821c38/ASPNETCore-SignalR-Angular-TypeScript/wwwroot/favicon.ico
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ASPNET Core, SignalR, Angular and TypeScript
2 | Getting started with SignalR using Angular 5 and ASPNET Core
3 | #### Blog: https://www.c-sharpcorner.com/article/getting-started-with-signalr-using-aspnet-co-using-angular-5/
4 |
5 |
6 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ASPNETCore-SignalR-Angular-TypeScript",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@aspnet/signalr": {
8 | "version": "1.0.0",
9 | "resolved": "https://registry.npmjs.org/@aspnet/signalr/-/signalr-1.0.0.tgz",
10 | "integrity": "sha512-7fXNdSTnp2y7a3i7BnvBpQpDEoG71DNq1J/Caowr+3v/nzGivnJApRg40VgBp6FlyeJqoBEQO/QuDPE0kTpczg=="
11 | },
12 | "tslib": {
13 | "version": "1.9.2",
14 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.2.tgz",
15 | "integrity": "sha512-AVP5Xol3WivEr7hnssHDsaM+lVrVXWUvd1cfXTRkTj80b//6g2wIFEH6hZG0muGZRnHGrfttpdzRk3YlBkWjKw=="
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ASPNETCore-SignalR-Angular-TypeScript",
3 | "version": "1.0.0",
4 | "description": "Getting started with SignalR using Angular and ASPNET Core",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "@aspnet/signalr": "^1.0.0",
14 | "tslib": "^1.9.2"
15 | },
16 | "devDependencies": {},
17 | "repository": {
18 | "type": "git",
19 | "url": "git+https://github.com/nemi-chand/ASPNETCore-SignalR-Angular-TypeScript.git"
20 | },
21 | "bugs": {
22 | "url": "https://github.com/nemi-chand/ASPNETCore-SignalR-Angular-TypeScript/issues"
23 | },
24 | "homepage": "https://github.com/nemi-chand/ASPNETCore-SignalR-Angular-TypeScript#readme"
25 | }
26 |
--------------------------------------------------------------------------------