├── .gitattributes ├── .gitignore ├── ASPNETCore-SignalR-Angular-TypeScript.sln ├── ASPNETCore-SignalR-Angular-TypeScript ├── .gitignore ├── ASPNETCore_SignalR_Angular_TypeScript.csproj ├── ClientApp │ ├── .angular-cli.json │ ├── .editorconfig │ ├── .gitignore │ ├── README.md │ ├── e2e │ │ ├── app.e2e-spec.ts │ │ ├── app.po.ts │ │ └── tsconfig.e2e.json │ ├── karma.conf.js │ ├── package-lock.json │ ├── package.json │ ├── protractor.conf.js │ ├── src │ │ ├── app │ │ │ ├── DynamicHub │ │ │ │ ├── dynamichub.component.html │ │ │ │ └── dynamichub.component.ts │ │ │ ├── Models │ │ │ │ ├── chatmessage.model.js │ │ │ │ ├── chatmessage.model.js.map │ │ │ │ ├── chatmessage.model.ts │ │ │ │ ├── tab.model.js │ │ │ │ ├── tab.model.js.map │ │ │ │ └── tab.model.ts │ │ │ ├── app.component.css │ │ │ ├── app.component.html │ │ │ ├── app.component.ts │ │ │ ├── app.module.ts │ │ │ ├── counter │ │ │ │ ├── counter.component.html │ │ │ │ ├── counter.component.spec.ts │ │ │ │ └── counter.component.ts │ │ │ ├── fetch-data │ │ │ │ ├── fetch-data.component.html │ │ │ │ └── fetch-data.component.ts │ │ │ ├── home │ │ │ │ ├── home.component.html │ │ │ │ └── home.component.ts │ │ │ ├── nav-menu │ │ │ │ ├── nav-menu.component.css │ │ │ │ ├── nav-menu.component.html │ │ │ │ └── nav-menu.component.ts │ │ │ ├── services │ │ │ │ ├── dynamicHub.signalR.service.ts │ │ │ │ ├── signalR.service.ts │ │ │ │ └── stock.signalR.service.ts │ │ │ └── stock │ │ │ │ ├── stock.component.html │ │ │ │ └── stock.component.ts │ │ ├── assets │ │ │ └── .gitkeep │ │ ├── environments │ │ │ ├── environment.prod.ts │ │ │ └── environment.ts │ │ ├── index.html │ │ ├── main.ts │ │ ├── polyfills.ts │ │ ├── styles.css │ │ ├── test.ts │ │ ├── tsconfig.app.json │ │ ├── tsconfig.spec.json │ │ └── typings.d.ts │ ├── tsconfig.json │ └── tslint.json ├── Controllers │ └── SampleDataController.cs ├── Hubs │ ├── ChatHub.cs │ ├── DynamicChatHub.cs │ ├── StockTickerHub.cs │ └── TChatHub.cs ├── Models │ ├── ChatMessage.cs │ └── Stock.cs ├── ObservableExtensions.cs ├── Pages │ ├── Error.cshtml │ ├── Error.cshtml.cs │ └── _ViewImports.cshtml ├── Program.cs ├── Startup.cs ├── StockTicker.cs ├── appsettings.Development.json ├── appsettings.json └── wwwroot │ └── favicon.ico ├── README.md ├── package-lock.json └── package.json /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # This .gitignore file was automatically created by Microsoft(R) Visual Studio. 3 | ################################################################################ 4 | 5 | /.vs 6 | /node_modules/@aspnet/signalr 7 | /node_modules/tslib 8 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27703.2000 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3E896075-BEB1-4534-ABC2-DE81E6A6FCD8}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ASPNETCore_SignalR_Angular_TypeScript", "ASPNETCore-SignalR-Angular-TypeScript\ASPNETCore_SignalR_Angular_TypeScript.csproj", "{716372C1-7226-44CD-B98E-90681BAA3CA8}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {716372C1-7226-44CD-B98E-90681BAA3CA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {716372C1-7226-44CD-B98E-90681BAA3CA8}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {716372C1-7226-44CD-B98E-90681BAA3CA8}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {716372C1-7226-44CD-B98E-90681BAA3CA8}.Release|Any CPU.Build.0 = Release|Any CPU 20 | EndGlobalSection 21 | GlobalSection(SolutionProperties) = preSolution 22 | HideSolutionNode = FALSE 23 | EndGlobalSection 24 | GlobalSection(NestedProjects) = preSolution 25 | {716372C1-7226-44CD-B98E-90681BAA3CA8} = {3E896075-BEB1-4534-ABC2-DE81E6A6FCD8} 26 | EndGlobalSection 27 | GlobalSection(ExtensibilityGlobals) = postSolution 28 | SolutionGuid = {CCAE0C42-94CE-4726-A45F-0385A702D47F} 29 | EndGlobalSection 30 | EndGlobal 31 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/.gitignore: -------------------------------------------------------------------------------- 1 | /Properties/launchSettings.json 2 | 3 | ## Ignore Visual Studio temporary files, build results, and 4 | ## files generated by popular Visual Studio add-ons. 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | build/ 23 | bld/ 24 | bin/ 25 | Bin/ 26 | obj/ 27 | Obj/ 28 | 29 | # Visual Studio 2015 cache/options directory 30 | .vs/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | *_i.c 46 | *_p.c 47 | *_i.h 48 | *.ilk 49 | *.meta 50 | *.obj 51 | *.pch 52 | *.pdb 53 | *.pgc 54 | *.pgd 55 | *.rsp 56 | *.sbr 57 | *.tlb 58 | *.tli 59 | *.tlh 60 | *.tmp 61 | *.tmp_proj 62 | *.log 63 | *.vspscc 64 | *.vssscc 65 | .builds 66 | *.pidb 67 | *.svclog 68 | *.scc 69 | 70 | # Chutzpah Test files 71 | _Chutzpah* 72 | 73 | # Visual C++ cache files 74 | ipch/ 75 | *.aps 76 | *.ncb 77 | *.opendb 78 | *.opensdf 79 | *.sdf 80 | *.cachefile 81 | 82 | # Visual Studio profiler 83 | *.psess 84 | *.vsp 85 | *.vspx 86 | *.sap 87 | 88 | # TFS 2012 Local Workspace 89 | $tf/ 90 | 91 | # Guidance Automation Toolkit 92 | *.gpState 93 | 94 | # ReSharper is a .NET coding add-in 95 | _ReSharper*/ 96 | *.[Rr]e[Ss]harper 97 | *.DotSettings.user 98 | 99 | # JustCode is a .NET coding add-in 100 | .JustCode 101 | 102 | # TeamCity is a build add-in 103 | _TeamCity* 104 | 105 | # DotCover is a Code Coverage Tool 106 | *.dotCover 107 | 108 | # NCrunch 109 | _NCrunch_* 110 | .*crunch*.local.xml 111 | nCrunchTemp_* 112 | 113 | # MightyMoose 114 | *.mm.* 115 | AutoTest.Net/ 116 | 117 | # Web workbench (sass) 118 | .sass-cache/ 119 | 120 | # Installshield output folder 121 | [Ee]xpress/ 122 | 123 | # DocProject is a documentation generator add-in 124 | DocProject/buildhelp/ 125 | DocProject/Help/*.HxT 126 | DocProject/Help/*.HxC 127 | DocProject/Help/*.hhc 128 | DocProject/Help/*.hhk 129 | DocProject/Help/*.hhp 130 | DocProject/Help/Html2 131 | DocProject/Help/html 132 | 133 | # Click-Once directory 134 | publish/ 135 | 136 | # Publish Web Output 137 | *.[Pp]ublish.xml 138 | *.azurePubxml 139 | # TODO: Comment the next line if you want to checkin your web deploy settings 140 | # but database connection strings (with potential passwords) will be unencrypted 141 | *.pubxml 142 | *.publishproj 143 | 144 | # NuGet Packages 145 | *.nupkg 146 | # The packages folder can be ignored because of Package Restore 147 | **/packages/* 148 | # except build/, which is used as an MSBuild target. 149 | !**/packages/build/ 150 | # Uncomment if necessary however generally it will be regenerated when needed 151 | #!**/packages/repositories.config 152 | 153 | # Microsoft Azure Build Output 154 | csx/ 155 | *.build.csdef 156 | 157 | # Microsoft Azure Emulator 158 | ecf/ 159 | rcf/ 160 | 161 | # Microsoft Azure ApplicationInsights config file 162 | ApplicationInsights.config 163 | 164 | # Windows Store app package directory 165 | AppPackages/ 166 | BundleArtifacts/ 167 | 168 | # Visual Studio cache files 169 | # files ending in .cache can be ignored 170 | *.[Cc]ache 171 | # but keep track of directories ending in .cache 172 | !*.[Cc]ache/ 173 | 174 | # Others 175 | ClientBin/ 176 | ~$* 177 | *~ 178 | *.dbmdl 179 | *.dbproj.schemaview 180 | *.pfx 181 | *.publishsettings 182 | orleans.codegen.cs 183 | 184 | /node_modules 185 | 186 | # RIA/Silverlight projects 187 | Generated_Code/ 188 | 189 | # Backup & report files from converting an old project file 190 | # to a newer Visual Studio version. Backup files are not needed, 191 | # because we have git ;-) 192 | _UpgradeReport_Files/ 193 | Backup*/ 194 | UpgradeLog*.XML 195 | UpgradeLog*.htm 196 | 197 | # SQL Server files 198 | *.mdf 199 | *.ldf 200 | 201 | # Business Intelligence projects 202 | *.rdl.data 203 | *.bim.layout 204 | *.bim_*.settings 205 | 206 | # Microsoft Fakes 207 | FakesAssemblies/ 208 | 209 | # GhostDoc plugin setting file 210 | *.GhostDoc.xml 211 | 212 | # Node.js Tools for Visual Studio 213 | .ntvs_analysis.dat 214 | 215 | # Visual Studio 6 build log 216 | *.plg 217 | 218 | # Visual Studio 6 workspace options file 219 | *.opt 220 | 221 | # Visual Studio LightSwitch build output 222 | **/*.HTMLClient/GeneratedArtifacts 223 | **/*.DesktopClient/GeneratedArtifacts 224 | **/*.DesktopClient/ModelManifest.xml 225 | **/*.Server/GeneratedArtifacts 226 | **/*.Server/ModelManifest.xml 227 | _Pvt_Extensions 228 | 229 | # Paket dependency manager 230 | .paket/paket.exe 231 | 232 | # FAKE - F# Make 233 | .fake/ 234 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ASPNETCore_SignalR_Angular_TypeScript.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.1 5 | true 6 | Latest 7 | false 8 | ClientApp\ 9 | $(DefaultItemExcludes);$(SpaRoot)node_modules\** 10 | 11 | 12 | false 13 | 345f1cb1-f78c-4215-962b-e51a8c10e694 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | %(DistFiles.Identity) 68 | PreserveNewest 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/.angular-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "project": { 4 | "name": "ASPNETCore_SignalR_Angular_TypeScript" 5 | }, 6 | "apps": [ 7 | { 8 | "root": "src", 9 | "outDir": "dist", 10 | "assets": [ 11 | "assets" 12 | ], 13 | "index": "index.html", 14 | "main": "main.ts", 15 | "polyfills": "polyfills.ts", 16 | "test": "test.ts", 17 | "tsconfig": "tsconfig.app.json", 18 | "testTsconfig": "tsconfig.spec.json", 19 | "prefix": "app", 20 | "styles": [ 21 | "styles.css", 22 | "../node_modules/bootstrap/dist/css/bootstrap.min.css" 23 | ], 24 | "scripts": [], 25 | "environmentSource": "environments/environment.ts", 26 | "environments": { 27 | "dev": "environments/environment.ts", 28 | "prod": "environments/environment.prod.ts" 29 | } 30 | } 31 | ], 32 | "e2e": { 33 | "protractor": { 34 | "config": "./protractor.conf.js" 35 | } 36 | }, 37 | "lint": [ 38 | { 39 | "project": "src/tsconfig.app.json", 40 | "exclude": "**/node_modules/**" 41 | }, 42 | { 43 | "project": "src/tsconfig.spec.json", 44 | "exclude": "**/node_modules/**" 45 | }, 46 | { 47 | "project": "e2e/tsconfig.e2e.json", 48 | "exclude": "**/node_modules/**" 49 | } 50 | ], 51 | "test": { 52 | "karma": { 53 | "config": "./karma.conf.js" 54 | } 55 | }, 56 | "defaults": { 57 | "styleExt": "css", 58 | "component": {}, 59 | "build": { 60 | "progress": true 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /dist-server 6 | /tmp 7 | /out-tsc 8 | 9 | # dependencies 10 | /node_modules 11 | 12 | # IDEs and editors 13 | /.idea 14 | .project 15 | .classpath 16 | .c9/ 17 | *.launch 18 | .settings/ 19 | *.sublime-workspace 20 | 21 | # IDE - VSCode 22 | .vscode/* 23 | !.vscode/settings.json 24 | !.vscode/tasks.json 25 | !.vscode/launch.json 26 | !.vscode/extensions.json 27 | 28 | # misc 29 | /.sass-cache 30 | /connect.lock 31 | /coverage 32 | /libpeerconnection.log 33 | npm-debug.log 34 | testem.log 35 | /typings 36 | 37 | # e2e 38 | /e2e/*.js 39 | /e2e/*.map 40 | 41 | # System Files 42 | .DS_Store 43 | Thumbs.db 44 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/README.md: -------------------------------------------------------------------------------- 1 | # AngularSpa 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.7.0. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 28 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo(); 12 | expect(page.getMainHeading()).toEqual('Hello, world!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/e2e/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getMainHeading() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": [ 9 | "jasmine", 10 | "jasminewd2", 11 | "node" 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular/cli'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular/cli/plugins/karma') 14 | ], 15 | client:{ 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | reports: [ 'html', 'lcovonly' ], 20 | fixWebpackSourcePaths: true 21 | }, 22 | angularCli: { 23 | environment: 'dev' 24 | }, 25 | reporters: ['progress', 'kjhtml'], 26 | port: 9876, 27 | colors: true, 28 | logLevel: config.LOG_INFO, 29 | autoWatch: true, 30 | browsers: ['Chrome'], 31 | singleRun: false 32 | }); 33 | }; 34 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ASPNETCore_SignalR_Angular_TypeScript", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "ng": "ng", 7 | "start": "ng serve --extract-css", 8 | "build": "ng build --extract-css", 9 | "build:ssr": "npm run build -- --app=ssr --output-hashing=media", 10 | "test": "ng test", 11 | "lint": "ng lint", 12 | "e2e": "ng e2e" 13 | }, 14 | "private": true, 15 | "dependencies": { 16 | "@angular/animations": "^5.2.0", 17 | "@angular/common": "^5.2.0", 18 | "@angular/compiler": "^5.2.0", 19 | "@angular/core": "^5.2.0", 20 | "@angular/forms": "^5.2.0", 21 | "@angular/http": "^5.2.0", 22 | "@angular/platform-browser": "^5.2.0", 23 | "@angular/platform-browser-dynamic": "^5.2.0", 24 | "@angular/platform-server": "^5.2.0", 25 | "@angular/router": "^5.2.0", 26 | "@nguniversal/module-map-ngfactory-loader": "^5.0.0-beta.5", 27 | "aspnet-prerendering": "^3.0.1", 28 | "bootstrap": "^3.3.7", 29 | "core-js": "^2.4.1", 30 | "rxjs": "^5.5.6", 31 | "zone.js": "^0.8.19" 32 | }, 33 | "devDependencies": { 34 | "@angular/cli": "~1.7.0", 35 | "@angular/compiler-cli": "^5.2.0", 36 | "@angular/language-service": "^5.2.0", 37 | "@types/jasmine": "~2.8.3", 38 | "@types/jasminewd2": "~2.0.2", 39 | "@types/node": "~6.0.60", 40 | "codelyzer": "^4.0.1", 41 | "jasmine-core": "~2.8.0", 42 | "jasmine-spec-reporter": "~4.2.1", 43 | "karma": "~2.0.0", 44 | "karma-chrome-launcher": "~2.2.0", 45 | "karma-coverage-istanbul-reporter": "^1.2.1", 46 | "karma-jasmine": "~1.1.0", 47 | "karma-jasmine-html-reporter": "^0.2.2", 48 | "protractor": "~5.1.2", 49 | "ts-node": "~4.1.0", 50 | "tslint": "~5.9.1", 51 | "typescript": "~2.5.3" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './e2e/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: 'e2e/tsconfig.e2e.json' 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/app/DynamicHub/dynamichub.component.html: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 |
7 |

{{tab.title}}

8 | 9 |

{{msg.message}}

10 |
11 |
12 | 13 |
14 | 28 |
29 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/app/DynamicHub/dynamichub.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, NgZone } from '@angular/core'; 2 | 3 | import { ChatMessage } from '../Models/chatmessage.model'; 4 | import { Tab } from '../Models/tab.model'; 5 | import { DynamicHubSignalRService } from '../services/dynamicHub.signalR.service'; 6 | 7 | @Component({ 8 | selector: 'app-home', 9 | templateUrl: './dynamichub.component.html', 10 | }) 11 | 12 | export class DynamicHubComponent { 13 | chatMessage: ChatMessage; 14 | canSendMessage: boolean; 15 | tabs: Tab[]; 16 | currentRoom: string; 17 | 18 | constructor( 19 | private signalrService: DynamicHubSignalRService, 20 | private _ngZone: NgZone 21 | ) 22 | { 23 | this.subscribeToEvents(); 24 | this.chatMessage = new ChatMessage(); 25 | this.tabs = []; 26 | this.tabs.push(new Tab('Lobby', 'Welcome to lobby')); 27 | this.tabs.push(new Tab('SignalR', 'Welcome to SignalR Room')); 28 | this.currentRoom = 'Lobby'; 29 | } 30 | 31 | sendMessage() { 32 | if (this.canSendMessage) { 33 | this.chatMessage.room = this.currentRoom; 34 | this.signalrService.sendChatMessage(this.chatMessage); 35 | } 36 | } 37 | 38 | OnRoomChange(room) { 39 | this.currentRoom = room; 40 | } 41 | 42 | private subscribeToEvents(): void { 43 | this.signalrService.connectionEstablished.subscribe(() => { 44 | this.canSendMessage = true; 45 | }); 46 | 47 | this.signalrService.messageReceived.subscribe((message: ChatMessage) => { 48 | this._ngZone.run(() => { 49 | this.chatMessage = new ChatMessage(); 50 | let room = this.tabs.find(t => t.heading == message.room); 51 | if (room) { 52 | room.messageHistory.push(new ChatMessage(message.user, message.message, message.room)); 53 | } 54 | }); 55 | }); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/app/Models/chatmessage.model.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | /** represent chat message class */ 4 | var ChatMessage = /** @class */ (function () { 5 | function ChatMessage(user, message, room) { 6 | if (user === void 0) { user = ''; } 7 | if (message === void 0) { message = ''; } 8 | if (room === void 0) { room = ''; } 9 | this.user = user; 10 | this.message = message; 11 | this.room = room; 12 | } 13 | return ChatMessage; 14 | }()); 15 | exports.ChatMessage = ChatMessage; 16 | //# sourceMappingURL=chatmessage.model.js.map -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/app/Models/chatmessage.model.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"chatmessage.model.js","sourceRoot":"","sources":["chatmessage.model.ts"],"names":[],"mappings":";;AACA,mCAAmC;AACnC;IAME,qBAAY,IAAiB,EAAE,OAAkB,EAAC,IAAc;QAApD,qBAAA,EAAA,SAAiB;QAAE,wBAAA,EAAA,YAAkB;QAAC,qBAAA,EAAA,SAAc;QAC9D,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IACH,kBAAC;AAAD,CAAC,AAXD,IAWC;AAXY,kCAAW"} -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/app/Models/chatmessage.model.ts: -------------------------------------------------------------------------------- 1 | 2 | /** represent chat message class */ 3 | export class ChatMessage { 4 | 5 | user: string; 6 | message: string; 7 | room: string; 8 | 9 | constructor(user: string = '', message: string='',room:string='') { 10 | this.user = user; 11 | this.message = message; 12 | this.room = room; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/app/Models/tab.model.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | /** Represent Tab class */ 4 | var Tab = /** @class */ (function () { 5 | function Tab(heading, title) { 6 | if (heading === void 0) { heading = ''; } 7 | if (title === void 0) { title = ''; } 8 | this.heading = heading; 9 | this.title = title; 10 | this.messageHistory = []; 11 | } 12 | return Tab; 13 | }()); 14 | exports.Tab = Tab; 15 | //# sourceMappingURL=tab.model.js.map -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/app/Models/tab.model.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"tab.model.js","sourceRoot":"","sources":["tab.model.ts"],"names":[],"mappings":";;AAEA,0BAA0B;AAC1B;IAKE,aACE,OAAkB,EAClB,KAAe;QADf,wBAAA,EAAA,YAAkB;QAClB,sBAAA,EAAA,UAAe;QAGf,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,cAAc,GAAG,EAAE,CAAC;IAC3B,CAAC;IACH,UAAC;AAAD,CAAC,AAdD,IAcC;AAdY,kBAAG"} -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/app/Models/tab.model.ts: -------------------------------------------------------------------------------- 1 | import { ChatMessage } from '../Models/chatmessage.model'; 2 | 3 | /** Represent Tab class */ 4 | export class Tab { 5 | messageHistory: ChatMessage[]; 6 | heading: string; 7 | title: string; 8 | 9 | constructor( 10 | heading: string='', 11 | title:string='' 12 | ) 13 | { 14 | this.heading = heading; 15 | this.title = title; 16 | this.messageHistory = []; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/app/app.component.css: -------------------------------------------------------------------------------- 1 | @media (max-width: 767px) { 2 | /* On small screens, the nav menu spans the full width of the screen. Leave a space for it. */ 3 | .body-content { 4 | padding-top: 50px; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |
6 |
7 | 8 |
9 |
10 |
11 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.css'] 7 | }) 8 | export class AppComponent { 9 | title = 'app'; 10 | } 11 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import { FormsModule } from '@angular/forms'; 4 | import { HttpClientModule } from '@angular/common/http'; 5 | import { RouterModule } from '@angular/router'; 6 | 7 | import { AppComponent } from './app.component'; 8 | import { NavMenuComponent } from './nav-menu/nav-menu.component'; 9 | import { HomeComponent } from './home/home.component'; 10 | import { CounterComponent } from './counter/counter.component'; 11 | import { FetchDataComponent } from './fetch-data/fetch-data.component'; 12 | import { StockComponent } from './stock/stock.component'; 13 | import { DynamicHubComponent } from './DynamicHub/dynamichub.component' 14 | 15 | import { SignalRService } from './services/signalR.service'; 16 | import { stockSignalRService } from './services/stock.signalR.service'; 17 | import { DynamicHubSignalRService } from './services/dynamicHub.signalR.service'; 18 | 19 | 20 | 21 | @NgModule({ 22 | declarations: [ 23 | AppComponent, 24 | NavMenuComponent, 25 | HomeComponent, 26 | CounterComponent, 27 | FetchDataComponent, 28 | StockComponent, 29 | DynamicHubComponent 30 | ], 31 | imports: [ 32 | BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }), 33 | HttpClientModule, 34 | FormsModule, 35 | RouterModule.forRoot([ 36 | { path: '', component: HomeComponent, pathMatch: 'full' }, 37 | { path: 'counter', component: CounterComponent }, 38 | { path: 'fetch-data', component: FetchDataComponent }, 39 | { path: 'stock-data', component: StockComponent }, 40 | { path: 'dynamic', component: DynamicHubComponent }, 41 | ]) 42 | ], 43 | providers: [SignalRService,stockSignalRService,DynamicHubSignalRService], 44 | bootstrap: [AppComponent] 45 | }) 46 | export class AppModule { } 47 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/app/counter/counter.component.html: -------------------------------------------------------------------------------- 1 |

Counter

2 | 3 |

This is a simple example of an Angular component.

4 | 5 |

Current count: {{ currentCount }}

6 | 7 | 8 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/app/counter/counter.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { CounterComponent } from './counter.component'; 4 | 5 | describe('CounterComponent', () => { 6 | let component: CounterComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ CounterComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(CounterComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should display a title', async(() => { 23 | const titleText = fixture.nativeElement.querySelector('h1').textContent; 24 | expect(titleText).toEqual('Counter'); 25 | })); 26 | 27 | it('should start with count 0, then increments by 1 when clicked', async(() => { 28 | const countElement = fixture.nativeElement.querySelector('strong'); 29 | expect(countElement.textContent).toEqual('0'); 30 | 31 | const incrementButton = fixture.nativeElement.querySelector('button'); 32 | incrementButton.click(); 33 | fixture.detectChanges(); 34 | expect(countElement.textContent).toEqual('1'); 35 | })); 36 | }); 37 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/app/counter/counter.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-counter-component', 5 | templateUrl: './counter.component.html' 6 | }) 7 | export class CounterComponent { 8 | public currentCount = 0; 9 | 10 | public incrementCounter() { 11 | this.currentCount++; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/app/fetch-data/fetch-data.component.html: -------------------------------------------------------------------------------- 1 |

Weather forecast

2 | 3 |

This component demonstrates fetching data from the server.

4 | 5 |

Loading...

6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
DateTemp. (C)Temp. (F)Summary
{{ forecast.dateFormatted }}{{ forecast.temperatureC }}{{ forecast.temperatureF }}{{ forecast.summary }}
25 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/app/fetch-data/fetch-data.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Inject } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | 4 | @Component({ 5 | selector: 'app-fetch-data', 6 | templateUrl: './fetch-data.component.html' 7 | }) 8 | export class FetchDataComponent { 9 | public forecasts: WeatherForecast[]; 10 | 11 | constructor(http: HttpClient, @Inject('BASE_URL') baseUrl: string) { 12 | http.get(baseUrl + 'api/SampleData/WeatherForecasts').subscribe(result => { 13 | this.forecasts = result; 14 | }, error => console.error(error)); 15 | } 16 | } 17 | 18 | interface WeatherForecast { 19 | dateFormatted: string; 20 | temperatureC: number; 21 | temperatureF: number; 22 | summary: string; 23 | } 24 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/app/home/home.component.html: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 |
7 |

{{tab.title}}

8 | 9 |

{{msg.message}}

10 |
11 |
12 | 13 |
14 |
15 |
16 |
17 |
18 | 19 |
20 | 21 | 22 | 23 |
24 |
25 |
26 |
27 |
28 |
29 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/app/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, NgZone } from '@angular/core'; 2 | 3 | import { SignalRService } from '../services/signalR.service'; 4 | import { ChatMessage } from '../Models/chatmessage.model'; 5 | import { Tab } from '../Models/tab.model'; 6 | 7 | @Component({ 8 | selector: 'app-home', 9 | templateUrl: './home.component.html', 10 | }) 11 | 12 | export class HomeComponent { 13 | chatMessage: ChatMessage; 14 | canSendMessage: boolean; 15 | tabs: Tab[]; 16 | currentRoom: string; 17 | 18 | constructor( 19 | private signalrService: SignalRService, 20 | private _ngZone: NgZone 21 | ) 22 | { 23 | this.subscribeToEvents(); 24 | this.chatMessage = new ChatMessage(); 25 | this.tabs = []; 26 | this.tabs.push(new Tab('Lobby', 'Welcome to lobby')); 27 | this.tabs.push(new Tab('SignalR', 'Welcome to SignalR Room')); 28 | this.currentRoom = 'Lobby'; 29 | } 30 | 31 | sendMessage() { 32 | if (this.canSendMessage) { 33 | this.chatMessage.room = this.currentRoom; 34 | this.signalrService.sendChatMessage(this.chatMessage); 35 | } 36 | } 37 | 38 | OnRoomChange(room) { 39 | this.currentRoom = room; 40 | } 41 | 42 | private subscribeToEvents(): void { 43 | this.signalrService.connectionEstablished.subscribe(() => { 44 | this.canSendMessage = true; 45 | }); 46 | 47 | this.signalrService.messageReceived.subscribe((message: ChatMessage) => { 48 | this._ngZone.run(() => { 49 | this.chatMessage = new ChatMessage(); 50 | let room = this.tabs.find(t => t.heading == message.room); 51 | if (room) { 52 | room.messageHistory.push(new ChatMessage(message.user, message.message, message.room)); 53 | } 54 | }); 55 | }); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/app/nav-menu/nav-menu.component.css: -------------------------------------------------------------------------------- 1 | li .glyphicon { 2 | margin-right: 10px; 3 | } 4 | 5 | /* Highlighting rules for nav menu items */ 6 | li.link-active a, 7 | li.link-active a:hover, 8 | li.link-active a:focus { 9 | background-color: #4189C7; 10 | color: white; 11 | } 12 | 13 | /* Keep the nav menu independent of scrolling and on top of other items */ 14 | .main-nav { 15 | position: fixed; 16 | top: 0; 17 | left: 0; 18 | right: 0; 19 | z-index: 1; 20 | } 21 | 22 | @media (min-width: 768px) { 23 | /* On small screens, convert the nav menu to a vertical sidebar */ 24 | .main-nav { 25 | height: 100%; 26 | width: calc(25% - 20px); 27 | } 28 | .navbar { 29 | border-radius: 0px; 30 | border-width: 0px; 31 | height: 100%; 32 | } 33 | .navbar-header { 34 | float: none; 35 | } 36 | .navbar-collapse { 37 | border-top: 1px solid #444; 38 | padding: 0px; 39 | } 40 | .navbar ul { 41 | float: none; 42 | } 43 | .navbar li { 44 | float: none; 45 | font-size: 15px; 46 | margin: 6px; 47 | } 48 | .navbar li a { 49 | padding: 10px 16px; 50 | border-radius: 4px; 51 | } 52 | .navbar a { 53 | /* If a menu item's text is too long, truncate it */ 54 | width: 100%; 55 | white-space: nowrap; 56 | overflow: hidden; 57 | text-overflow: ellipsis; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/app/nav-menu/nav-menu.component.html: -------------------------------------------------------------------------------- 1 | 44 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/app/nav-menu/nav-menu.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-nav-menu', 5 | templateUrl: './nav-menu.component.html', 6 | styleUrls: ['./nav-menu.component.css'] 7 | }) 8 | export class NavMenuComponent { 9 | isExpanded = false; 10 | 11 | collapse() { 12 | this.isExpanded = false; 13 | } 14 | 15 | toggle() { 16 | this.isExpanded = !this.isExpanded; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/app/services/dynamicHub.signalR.service.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter, Injectable } from '@angular/core'; 2 | import { HubConnection, HubConnectionBuilder } from '@aspnet/signalr'; 3 | import { ChatMessage } from '../Models/chatmessage.model'; 4 | 5 | @Injectable() 6 | export class DynamicHubSignalRService { 7 | messageReceived = new EventEmitter(); 8 | connectionEstablished = new EventEmitter(); 9 | 10 | private connectionIsEstablished = false; 11 | private _hubConnection: HubConnection; 12 | 13 | constructor() { 14 | this.createConnection(); 15 | this.registerOnServerEvents(); 16 | this.startConnection(); 17 | } 18 | 19 | sendChatMessage(message: ChatMessage) { 20 | this._hubConnection.invoke('SendMessage', message); 21 | } 22 | 23 | private createConnection() { 24 | this._hubConnection = new HubConnectionBuilder() 25 | .withUrl('/dynamichub') 26 | .build(); 27 | } 28 | 29 | private startConnection(): void { 30 | this._hubConnection 31 | .start() 32 | .then(() => { 33 | this.connectionIsEstablished = true; 34 | console.log('Hub connection started'); 35 | this.connectionEstablished.emit(true); 36 | }) 37 | .catch(err => { 38 | console.log('Error while establishing connection, retrying...'); 39 | setTimeout(this.startConnection(), 5000); 40 | }); 41 | } 42 | 43 | private registerOnServerEvents(): void { 44 | this._hubConnection.on('Send', (data: any) => { 45 | console.log('Data received:' + data); 46 | this.messageReceived.emit(data); 47 | }); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/app/services/signalR.service.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter, Injectable } from '@angular/core'; 2 | import { HubConnection, HubConnectionBuilder } from '@aspnet/signalr'; 3 | import { ChatMessage } from '../Models/chatmessage.model'; 4 | 5 | @Injectable() 6 | export class SignalRService { 7 | messageReceived = new EventEmitter(); 8 | connectionEstablished = new EventEmitter(); 9 | 10 | private connectionIsEstablished = false; 11 | private _hubConnection: HubConnection; 12 | 13 | constructor() { 14 | this.createConnection(); 15 | this.registerOnServerEvents(); 16 | this.startConnection(); 17 | } 18 | 19 | sendChatMessage(message: ChatMessage) { 20 | this._hubConnection.invoke('SendMessage', message); 21 | } 22 | 23 | private createConnection() { 24 | this._hubConnection = new HubConnectionBuilder() 25 | .withUrl('/chathub') 26 | .build(); 27 | } 28 | 29 | private startConnection(): void { 30 | this._hubConnection 31 | .start() 32 | .then(() => { 33 | this.connectionIsEstablished = true; 34 | console.log('Hub connection started'); 35 | this.connectionEstablished.emit(true); 36 | }) 37 | .catch(err => { 38 | console.log('Error while establishing connection, retrying...'); 39 | setTimeout(this.startConnection(), 5000); 40 | }); 41 | } 42 | 43 | private registerOnServerEvents(): void { 44 | this._hubConnection.on('ReceiveMessage', (data: any) => { 45 | this.messageReceived.emit(data); 46 | }); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/app/services/stock.signalR.service.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter, Injectable } from '@angular/core'; 2 | import { HubConnection,HubConnectionBuilder, IStreamResult } from '@aspnet/signalr' 3 | 4 | 5 | @Injectable() 6 | export class stockSignalRService { 7 | connectionEstablished = new EventEmitter(); 8 | marketOpened = new EventEmitter(); 9 | marketClosed = new EventEmitter(); 10 | 11 | private connectionIsEstablished = false; 12 | private _stockHubConnection: HubConnection; 13 | 14 | 15 | constructor() { 16 | this.createConnection(); 17 | this.registerOnServerEvents(); 18 | this.startConnection(); 19 | } 20 | 21 | private createConnection() { 22 | this._stockHubConnection = new HubConnectionBuilder() 23 | .withUrl('/stock') 24 | .build(); 25 | } 26 | 27 | private startConnection(): void { 28 | this._stockHubConnection 29 | .start() 30 | .then(() => { 31 | this.connectionIsEstablished = true; 32 | console.log('stock connection started'); 33 | this.connectionEstablished.emit(true); 34 | }).catch(err => { 35 | setTimeout(this.startConnection(), 5000); 36 | }); 37 | } 38 | 39 | private registerOnServerEvents(): void { 40 | this._stockHubConnection.on("marketOpened", () => { 41 | console.log("marketOpened"); 42 | this.marketOpened.emit(true); 43 | }); 44 | 45 | this._stockHubConnection.on("marketClosed",() => { 46 | console.log("marketClosed"); 47 | this.marketClosed.emit(true); 48 | }); 49 | } 50 | 51 | public startStreaming(): IStreamResult { 52 | return this._stockHubConnection.stream("StreamStocks"); 53 | } 54 | 55 | public getAllStocks(): Promise { 56 | return this._stockHubConnection.invoke("getAllStocks"); 57 | } 58 | 59 | public openMarket() { 60 | this._stockHubConnection.invoke("OpenMarket"); 61 | } 62 | 63 | public CloseMarket() { 64 | this._stockHubConnection.invoke("CloseMarket"); 65 | } 66 | 67 | public ResetMarket() { 68 | this._stockHubConnection.invoke("Reset"); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /ASPNETCore-SignalR-Angular-TypeScript/ClientApp/src/app/stock/stock.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | 5 | 6 | 7 |

Stock Count : {{stocks.length}}

8 |

Market Status : {{marketStatus}}

9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
SymbolPriceChange %
{{stock.symbol}}{{stock.price}}{{stock.percentChange}} {{stock.percentChange >= 0 ? '▲' : '▼'}}
22 |
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 | --------------------------------------------------------------------------------