├── .github └── screen.gif ├── .gitignore ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── README.md ├── backend └── ASPNETCore │ ├── ASPNETCore.Web │ ├── .vscode │ │ ├── launch.json │ │ └── tasks.json │ ├── ASPNETCore.Web.csproj │ ├── ASPNETCore.Web.csproj.user │ ├── Controllers │ │ └── FoodItemsController.cs │ ├── Dtos │ │ └── FoodItemDto.cs │ ├── Extensions │ │ └── MappingExtensions.cs │ ├── Hubs │ │ └── ChatHub.cs │ ├── MappingProfiles │ │ └── FoodMappings.cs │ ├── Models │ │ ├── ChatMessage.cs │ │ └── FoodItem.cs │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── Repositories │ │ ├── FoodDbContext.cs │ │ ├── FoodRepository.cs │ │ └── IFoodRepository.cs │ ├── Services │ │ ├── HostedServiceBase.cs │ │ ├── SchedulerHostedService.cs │ │ └── TimerServiceConfiguration.cs │ ├── appsettings.Development.json │ ├── appsettings.json │ └── wwwroot │ │ └── index.html │ └── ASPNETCore.sln └── frontend ├── .editorconfig ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json └── tasks.json ├── README.md ├── angular.json ├── package-lock.json ├── package.json ├── src ├── app │ ├── about │ │ ├── container │ │ │ └── about │ │ │ │ ├── about.component.css │ │ │ │ ├── about.component.html │ │ │ │ ├── about.component.spec.ts │ │ │ │ └── about.component.ts │ │ └── index.ts │ ├── app-routes.ts │ ├── app.component.css │ ├── app.component.html │ ├── app.component.spec.ts │ ├── app.component.ts │ ├── app.config.ts │ ├── dashboard │ │ ├── container │ │ │ └── dashboard │ │ │ │ ├── dashboard.component.css │ │ │ │ ├── dashboard.component.html │ │ │ │ ├── dashboard.component.spec.ts │ │ │ │ └── dashboard.component.ts │ │ ├── index.ts │ │ └── presentational │ │ │ ├── chat │ │ │ ├── chat.component.css │ │ │ ├── chat.component.html │ │ │ ├── chat.component.spec.ts │ │ │ └── chat.component.ts │ │ │ ├── cpu │ │ │ ├── cpu.component.css │ │ │ ├── cpu.component.html │ │ │ ├── cpu.component.spec.ts │ │ │ └── cpu.component.ts │ │ │ └── food │ │ │ ├── food.component.css │ │ │ ├── food.component.html │ │ │ ├── food.component.spec.ts │ │ │ └── food.component.ts │ ├── models │ │ ├── chat-message.model.ts │ │ └── foodItem.model.ts │ ├── services │ │ ├── food-data.service.ts │ │ └── signalR.service.ts │ └── shared │ │ └── navigation │ │ ├── navigation.component.css │ │ ├── navigation.component.html │ │ ├── navigation.component.spec.ts │ │ └── navigation.component.ts ├── assets │ └── .gitkeep ├── environments │ ├── environment.development.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── main.ts └── styles.css ├── tsconfig.app.json ├── tsconfig.json └── tsconfig.spec.json /.github/screen.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FabianGosebrink/ASPNETCore-Angular-SignalR-Typescript/9d9bea4e9295246c83ef9bd216890f69dcc1c4e9/.github/screen.gif -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /backend/.vs 2 | /backend/ASPNETCore/obj/ 3 | /backend/ASPNETCore/bin/ 4 | /frontend/angularsignalrclient/.temp 5 | backend/ASPNETCore/Properties/PublishProfiles/ 6 | /backend/ASPNETCore/.vs 7 | /backend/ASPNETCore/ASPNETCore.Web/bin 8 | /backend/ASPNETCore/ASPNETCore.Web/obj 9 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | // Use IntelliSense to find out which attributes exist for C# debugging 6 | // Use hover for the description of the existing attributes 7 | // For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md. 8 | "name": ".NET Core Launch (web)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/backend/ASPNETCore/ASPNETCore.Web/bin/Debug/net7.0/ASPNETCore.Web.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}/backend/ASPNETCore/ASPNETCore.Web", 16 | "stopAtEntry": false, 17 | // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser 18 | "serverReadyAction": { 19 | "action": "openExternally", 20 | "pattern": "\\bNow listening on:\\s+(https?://\\S+)" 21 | }, 22 | "env": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | }, 25 | "sourceFileMap": { 26 | "/Views": "${workspaceFolder}/Views" 27 | } 28 | }, 29 | { 30 | "name": ".NET Core Attach", 31 | "type": "coreclr", 32 | "request": "attach" 33 | } 34 | ] 35 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.codeActionsOnSave": { 3 | "source.organizeImports": true, 4 | "source.fixAll.eslint": true 5 | }, 6 | "editor.formatOnSave": true, 7 | "editor.formatOnPaste": true, 8 | "editor.defaultFormatter": "esbenp.prettier-vscode", 9 | "[typescript]": { 10 | "editor.defaultFormatter": "esbenp.prettier-vscode" 11 | }, 12 | "[json]": { 13 | "editor.defaultFormatter": "esbenp.prettier-vscode" 14 | }, 15 | "[jsonc]": { 16 | "editor.defaultFormatter": "esbenp.prettier-vscode" 17 | }, 18 | "[javascript]": { 19 | "editor.defaultFormatter": "esbenp.prettier-vscode" 20 | }, 21 | "[html]": { 22 | "editor.defaultFormatter": "esbenp.prettier-vscode" 23 | }, 24 | "[less]": { 25 | "editor.defaultFormatter": "esbenp.prettier-vscode" 26 | }, 27 | "typescript.preferences.importModuleSpecifier": "relative" 28 | } 29 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/backend/ASPNETCore/ASPNETCore.sln", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "publish", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "publish", 22 | "${workspaceFolder}/backend/ASPNETCore/ASPNETCore.sln", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "watch", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "watch", 34 | "run", 35 | "--project", 36 | "${workspaceFolder}/backend/ASPNETCore/ASPNETCore.sln" 37 | ], 38 | "problemMatcher": "$msCompile" 39 | } 40 | ] 41 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Welcome to ASPNET Core Angular SignalR Typescript 👋

2 |

3 | 4 | Twitter: FabianGosebrink 5 | 6 |

7 | 8 | With this project we are running an Angular project which is getting updated by an ASP.NET Core backend with SignalR. It receives data over a REST interface and gets datapushed with SignalR. Enjoy! 9 | 10 | ![signalr](.github/screen.gif "signalr") 11 | 12 | ## Install 13 | 14 | Just clone this repo and run 15 | 16 | ```javascript 17 | npm install 18 | ``` 19 | 20 | in the `\frontend\angularsignalrclient` folder. 21 | 22 | ## Usage 23 | 24 | ```javascript 25 | npm start 26 | ``` 27 | 28 | in the `\frontend\angularsignalrclient` folder. 29 | 30 | To run the backend make sure you have installed the [dotnet cli](https://dotnet.microsoft.com/) and run 31 | 32 | ```bash 33 | dotnet run 34 | ``` 35 | 36 | in the `backend\ASPNETCore` folder. 37 | 38 | ```javascript 39 | http://localhost:4200 40 | ``` 41 | 42 | to see the page then. 43 | 44 | Have fun! 45 | 46 | ## Author 47 | 48 | 👤 **Fabian Gosebrink** 49 | 50 | - Twitter: [@FabianGosebrink](https://twitter.com/FabianGosebrink) 51 | - Github: [@FabianGosebrink](https://github.com/FabianGosebrink) 52 | 53 | ## Show your support 54 | 55 | Give a ⭐️ if this project helped you! 56 | 57 | --- 58 | 59 | _This README was generated with ❤️ by [readme-md-generator](https://github.com/kefranabg/readme-md-generator)_ 60 | -------------------------------------------------------------------------------- /backend/ASPNETCore/ASPNETCore.Web/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (web)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/bin/Debug/netcoreapp3.0/ASPNETCore.Web.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | "stopAtEntry": false, 17 | // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser 18 | "serverReadyAction": { 19 | "action": "openExternally", 20 | "pattern": "^\\s*Now listening on:\\s+(https?://\\S+)" 21 | }, 22 | "env": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | }, 25 | "sourceFileMap": { 26 | "/Views": "${workspaceFolder}/Views" 27 | } 28 | }, 29 | { 30 | "name": ".NET Core Attach", 31 | "type": "coreclr", 32 | "request": "attach", 33 | "processId": "${command:pickProcess}" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /backend/ASPNETCore/ASPNETCore.Web/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/ASPNETCore.Web.csproj", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "publish", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "publish", 22 | "${workspaceFolder}/ASPNETCore.Web.csproj", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "watch", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "watch", 34 | "run", 35 | "${workspaceFolder}/ASPNETCore.Web.csproj", 36 | "/property:GenerateFullPaths=true", 37 | "/consoleloggerparameters:NoSummary" 38 | ], 39 | "problemMatcher": "$msCompile" 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /backend/ASPNETCore/ASPNETCore.Web/ASPNETCore.Web.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | all 17 | runtime; build; native; contentfiles; analyzers; buildtransitive 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /backend/ASPNETCore/ASPNETCore.Web/ASPNETCore.Web.csproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | ProjectDebugger 5 | 6 | 7 | ASPNETCore.Web 8 | 9 | -------------------------------------------------------------------------------- /backend/ASPNETCore/ASPNETCore.Web/Controllers/FoodItemsController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using AspNetCoreAngularSignalR.Dtos; 5 | using AspNetCoreAngularSignalR.Hubs; 6 | using AspNetCoreAngularSignalR.Models; 7 | using AspNetCoreAngularSignalR.Repositories; 8 | using AutoMapper; 9 | using Microsoft.AspNetCore.Mvc; 10 | using Microsoft.AspNetCore.SignalR; 11 | 12 | namespace AspNetCoreAngularSignalR.Controllers 13 | { 14 | [Route("api/[controller]")] 15 | public class FoodItemsController : Controller 16 | { 17 | private readonly IFoodRepository _foodRepository; 18 | private readonly IHubContext _chatHubContext; 19 | private readonly IMapper _mapper; 20 | 21 | public FoodItemsController( 22 | IFoodRepository foodRepository, 23 | IHubContext hubContext, 24 | IMapper mapper) 25 | { 26 | _foodRepository = foodRepository; 27 | _chatHubContext = hubContext; 28 | _mapper = mapper; 29 | } 30 | 31 | [HttpGet] 32 | public ActionResult> GetAllFoods() 33 | { 34 | var foods = _foodRepository.GetAll(); 35 | return Ok(foods.Select(x => _mapper.Map(x))); 36 | } 37 | 38 | [HttpGet] 39 | [Route("{id:int}", Name = nameof(GetSingleFood))] 40 | public ActionResult GetSingleFood(int id) 41 | { 42 | FoodItem foodItem = _foodRepository.GetSingle(id); 43 | 44 | if (foodItem == null) 45 | { 46 | return NotFound(); 47 | } 48 | 49 | return Ok(_mapper.Map(foodItem)); 50 | } 51 | 52 | [HttpPost] 53 | public ActionResult AddFoodToList([FromBody] FoodItemDto viewModel) 54 | { 55 | if (viewModel == null) 56 | { 57 | return BadRequest(); 58 | } 59 | 60 | if (!ModelState.IsValid) 61 | { 62 | return BadRequest(ModelState); 63 | } 64 | 65 | FoodItem item = _mapper.Map(viewModel); 66 | item.Created = DateTime.Now; 67 | FoodItem newFoodItem = _foodRepository.Add(item); 68 | 69 | _chatHubContext.Clients.All.SendAsync("FoodAdded", newFoodItem); 70 | 71 | return CreatedAtRoute( 72 | nameof(GetSingleFood), 73 | new { id = newFoodItem.Id }, 74 | _mapper.Map(newFoodItem)); 75 | } 76 | 77 | [HttpPut] 78 | [Route("{foodItemId:int}")] 79 | public ActionResult UpdateFoodInList(int foodItemId, [FromBody] FoodItemDto viewModel) 80 | { 81 | if (viewModel == null) 82 | { 83 | return BadRequest(); 84 | } 85 | 86 | if (!ModelState.IsValid) 87 | { 88 | return BadRequest(ModelState); 89 | } 90 | 91 | 92 | FoodItem singleById = _foodRepository.GetSingle(foodItemId); 93 | 94 | if (singleById == null) 95 | { 96 | return NotFound(); 97 | } 98 | 99 | singleById.ItemName = viewModel.ItemName; 100 | 101 | FoodItem newFoodItem = _foodRepository.Update(singleById); 102 | _chatHubContext.Clients.All.SendAsync("FoodUpdated", newFoodItem); 103 | return Ok(_mapper.Map(newFoodItem)); 104 | } 105 | 106 | [HttpDelete] 107 | [Route("{foodItemId:int}")] 108 | public ActionResult DeleteFoodFromList(int foodItemId) 109 | { 110 | 111 | FoodItem singleById = _foodRepository.GetSingle(foodItemId); 112 | 113 | if (singleById == null) 114 | { 115 | return NotFound(); 116 | } 117 | 118 | _foodRepository.Delete(foodItemId); 119 | 120 | _chatHubContext.Clients.All.SendAsync("FoodDeleted"); 121 | return NoContent(); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /backend/ASPNETCore/ASPNETCore.Web/Dtos/FoodItemDto.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AspNetCoreAngularSignalR.Dtos 4 | { 5 | public class FoodItemDto 6 | { 7 | public int Id { get; set; } 8 | public string ItemName { get; set; } 9 | public DateTime Created { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /backend/ASPNETCore/ASPNETCore.Web/Extensions/MappingExtensions.cs: -------------------------------------------------------------------------------- 1 | using ASPNETCore.Web.MappingProfiles; 2 | using AutoMapper; 3 | using Microsoft.Extensions.DependencyInjection; 4 | 5 | namespace ASPNETCore.Extensions 6 | { 7 | public static class MappingExtensions 8 | { 9 | public static void AddMappingProfiles(this IServiceCollection services) 10 | { 11 | services.AddAutoMapper(typeof(FoodMappings)); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /backend/ASPNETCore/ASPNETCore.Web/Hubs/ChatHub.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using AspNetCoreAngularSignalR.Models; 3 | using Microsoft.AspNetCore.SignalR; 4 | 5 | namespace AspNetCoreAngularSignalR.Hubs 6 | { 7 | public class ChatHub : Hub 8 | { 9 | public async Task SendMessage(ChatMessage chatMessage) 10 | { 11 | await Clients.All.SendAsync("Send", chatMessage); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /backend/ASPNETCore/ASPNETCore.Web/MappingProfiles/FoodMappings.cs: -------------------------------------------------------------------------------- 1 | using AspNetCoreAngularSignalR.Dtos; 2 | using AspNetCoreAngularSignalR.Models; 3 | using AutoMapper; 4 | 5 | namespace ASPNETCore.Web.MappingProfiles 6 | { 7 | public class FoodMappings : Profile 8 | { 9 | public FoodMappings() 10 | { 11 | CreateMap().ReverseMap(); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /backend/ASPNETCore/ASPNETCore.Web/Models/ChatMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AspNetCoreAngularSignalR.Models 4 | { 5 | public class ChatMessage 6 | { 7 | public string Message { get; set; } 8 | public DateTime Sent { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /backend/ASPNETCore/ASPNETCore.Web/Models/FoodItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace AspNetCoreAngularSignalR.Models 5 | { 6 | public class FoodItem 7 | { 8 | [Key] 9 | public int Id { get; set; } 10 | public string ItemName { get; set; } 11 | public DateTime Created { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/ASPNETCore/ASPNETCore.Web/Program.cs: -------------------------------------------------------------------------------- 1 | using ASPNETCore.Extensions; 2 | using ASPNETCore.Repositories; 3 | using ASPNETCore.Web; 4 | using ASPNETCore.Web.MappingProfiles; 5 | using AspNetCoreAngularSignalR.Hubs; 6 | using AspNetCoreAngularSignalR.Repositories; 7 | using AspNetCoreAngularSignalR.Services; 8 | using Microsoft.AspNetCore.Mvc.ApiExplorer; 9 | using Microsoft.EntityFrameworkCore; 10 | using Microsoft.Extensions.Options; 11 | using Newtonsoft.Json.Serialization; 12 | using Swashbuckle.AspNetCore.SwaggerGen; 13 | 14 | var builder = WebApplication.CreateBuilder(args); 15 | 16 | builder.Services.AddControllers() 17 | .AddNewtonsoftJson(options => 18 | options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver()); 19 | 20 | builder.Services.AddEndpointsApiExplorer(); 21 | builder.Services.AddSwaggerGen(); 22 | 23 | builder.Services.AddCors(options => 24 | { 25 | options.AddPolicy("CorsPolicy", 26 | builder => 27 | { 28 | builder 29 | .AllowAnyHeader() 30 | .AllowAnyMethod() 31 | .WithOrigins("http://localhost:4200") 32 | .AllowCredentials(); 33 | }); 34 | }); 35 | 36 | builder.Services.AddSingleton(); 37 | builder.Services.Configure(builder.Configuration.GetSection("TimeService")); 38 | builder.Services.AddSingleton(); 39 | 40 | builder.Services.AddRouting(options => options.LowercaseUrls = true); 41 | 42 | builder.Services.AddSignalR(); 43 | 44 | builder.Services.AddMappingProfiles(); 45 | 46 | builder.Services.AddDbContext(opt => opt.UseInMemoryDatabase("FoodDb")); 47 | 48 | var app = builder.Build(); 49 | 50 | if (app.Environment.IsDevelopment()) 51 | { 52 | app.UseDeveloperExceptionPage(); 53 | } 54 | var loggerFactory = app.Services.GetRequiredService(); 55 | 56 | app.UseSwagger(); 57 | app.UseSwaggerUI(); 58 | 59 | app.UseHttpsRedirection(); 60 | app.UseCors("CorsPolicy"); 61 | app.UseDefaultFiles(); 62 | app.UseStaticFiles(); 63 | app.UseRouting(); 64 | 65 | app.MapControllers(); 66 | app.MapHub("/coolmessages"); 67 | 68 | app.Run(); -------------------------------------------------------------------------------- /backend/ASPNETCore/ASPNETCore.Web/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:58816", 8 | "sslPort": 44389 9 | } 10 | }, 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "swagger", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "ASPNETCore.Web": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "launchUrl": "swagger", 24 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /backend/ASPNETCore/ASPNETCore.Web/Repositories/FoodDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | 3 | namespace ASPNETCore.Repositories 4 | { 5 | public class FoodDbContext : DbContext 6 | { 7 | public FoodDbContext(DbContextOptions options) 8 | : base(options) 9 | { 10 | } 11 | 12 | public DbSet TodoItems { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /backend/ASPNETCore/ASPNETCore.Web/Repositories/FoodRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using AspNetCoreAngularSignalR.Models; 5 | 6 | namespace AspNetCoreAngularSignalR.Repositories 7 | { 8 | public class FoodRepository : IFoodRepository 9 | { 10 | readonly Dictionary _foods = new Dictionary(); 11 | 12 | public FoodRepository() 13 | { 14 | _foods.Add(1, new FoodItem() { ItemName = "Spaghetti Bolognese", Id = 1, Created = DateTime.Now}); 15 | } 16 | 17 | public List GetAll() 18 | { 19 | return _foods.Select(x => x.Value).ToList(); 20 | } 21 | 22 | public FoodItem GetSingle(int id) 23 | { 24 | return _foods.FirstOrDefault(x => x.Key == id).Value; 25 | } 26 | 27 | public FoodItem Add(FoodItem toAdd) 28 | { 29 | int newId = !GetAll().Any() ? 1 : GetAll().Max(x => x.Id) + 1; 30 | toAdd.Id = newId; 31 | _foods.Add(newId, toAdd); 32 | return toAdd; 33 | } 34 | 35 | public FoodItem Update(FoodItem toUpdate) 36 | { 37 | FoodItem single = GetSingle(toUpdate.Id); 38 | 39 | if (single == null) 40 | { 41 | return null; 42 | } 43 | 44 | _foods[single.Id] = toUpdate; 45 | return toUpdate; 46 | } 47 | 48 | public void Delete(int id) 49 | { 50 | _foods.Remove(id); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /backend/ASPNETCore/ASPNETCore.Web/Repositories/IFoodRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using AspNetCoreAngularSignalR.Models; 3 | 4 | namespace AspNetCoreAngularSignalR.Repositories 5 | { 6 | public interface IFoodRepository 7 | { 8 | List GetAll(); 9 | FoodItem GetSingle(int id); 10 | FoodItem Add(FoodItem toAdd); 11 | FoodItem Update(FoodItem toUpdate); 12 | void Delete(int id); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /backend/ASPNETCore/ASPNETCore.Web/Services/HostedServiceBase.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using Microsoft.Extensions.Hosting; 4 | 5 | namespace AspNetCoreAngularSignalR.Services 6 | { 7 | public abstract class HostedServiceBase : IHostedService 8 | { 9 | // see for correct usage of IHostedService implementation: 10 | // https://gist.github.com/davidfowl/a7dd5064d9dcf35b6eae1a7953d615e3 11 | 12 | private Task _executingTask; 13 | private CancellationTokenSource _cts; 14 | 15 | public Task StartAsync(CancellationToken cancellationToken) 16 | { 17 | // Create a linked token so we can trigger cancellation outside of this token's cancellation 18 | _cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); 19 | 20 | // Store the task we're executing 21 | _executingTask = ExecuteAsync(_cts.Token); 22 | 23 | // If the task is completed then return it, otherwise it's running 24 | return _executingTask.IsCompleted ? _executingTask : Task.CompletedTask; 25 | } 26 | 27 | public async Task StopAsync(CancellationToken cancellationToken) 28 | { 29 | // Stop called without start 30 | if (_executingTask == null) 31 | { 32 | return; 33 | } 34 | 35 | // Signal cancellation to the executing method 36 | _cts.Cancel(); 37 | 38 | // Wait until the task completes or the stop token triggers 39 | await Task.WhenAny(_executingTask, Task.Delay(-1, cancellationToken)); 40 | 41 | // Throw if cancellation triggered 42 | cancellationToken.ThrowIfCancellationRequested(); 43 | } 44 | 45 | // Derived classes should override this and execute a long running method until 46 | // cancellation is requested 47 | protected abstract Task ExecuteAsync(CancellationToken cancellationToken); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /backend/ASPNETCore/ASPNETCore.Web/Services/SchedulerHostedService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using AspNetCoreAngularSignalR.Hubs; 5 | using Microsoft.AspNetCore.SignalR; 6 | using Microsoft.Extensions.Logging; 7 | using Microsoft.Extensions.Options; 8 | 9 | namespace AspNetCoreAngularSignalR.Services 10 | { 11 | public class SchedulerHostedService : HostedServiceBase 12 | { 13 | private readonly ILogger _logger; 14 | private readonly IOptions _options; 15 | private readonly IHubContext _hubContext; 16 | 17 | private readonly Random _random = new Random(); 18 | 19 | public SchedulerHostedService( 20 | ILoggerFactory loggerFactory, 21 | IOptions options, 22 | IHubContext hubContext) 23 | { 24 | _logger = loggerFactory.CreateLogger(); 25 | _options = options; 26 | _hubContext = hubContext; 27 | } 28 | 29 | protected override async Task ExecuteAsync(CancellationToken cancellationToken) 30 | { 31 | while (!cancellationToken.IsCancellationRequested) 32 | { 33 | var randomValue = _random.Next(0, 100); 34 | 35 | _logger.LogInformation($"Sending newCpuValue {randomValue}..."); 36 | 37 | await _hubContext.Clients.All.SendAsync("newCpuValue", randomValue); 38 | 39 | await Task.Delay(TimeSpan.FromMilliseconds(_options.Value.Period), cancellationToken); 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /backend/ASPNETCore/ASPNETCore.Web/Services/TimerServiceConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace AspNetCoreAngularSignalR.Services 2 | { 3 | public class TimerServiceConfiguration 4 | { 5 | public int DueTime { get; set; } 6 | 7 | public int Period { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /backend/ASPNETCore/ASPNETCore.Web/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /backend/ASPNETCore/ASPNETCore.Web/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*", 9 | "TimeService": { 10 | "DueTime": 3000, 11 | "Period": 1500 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /backend/ASPNETCore/ASPNETCore.Web/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | Server API runs -------------------------------------------------------------------------------- /backend/ASPNETCore/ASPNETCore.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29318.209 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ASPNETCore.Web", "ASPNETCore.Web\ASPNETCore.Web.csproj", "{33CB9E94-6C3F-4A17-AC76-3FD33AF8A979}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {33CB9E94-6C3F-4A17-AC76-3FD33AF8A979}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {33CB9E94-6C3F-4A17-AC76-3FD33AF8A979}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {33CB9E94-6C3F-4A17-AC76-3FD33AF8A979}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {33CB9E94-6C3F-4A17-AC76-3FD33AF8A979}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {5E848F73-2A9E-4B47-885A-720FA6B9F189} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /frontend/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://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 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # Compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | /bazel-out 8 | 9 | # Node 10 | /node_modules 11 | npm-debug.log 12 | yarn-error.log 13 | 14 | # IDEs and editors 15 | .idea/ 16 | .project 17 | .classpath 18 | .c9/ 19 | *.launch 20 | .settings/ 21 | *.sublime-workspace 22 | 23 | # Visual Studio Code 24 | .vscode/* 25 | !.vscode/settings.json 26 | !.vscode/tasks.json 27 | !.vscode/launch.json 28 | !.vscode/extensions.json 29 | .history/* 30 | 31 | # Miscellaneous 32 | /.angular/cache 33 | .sass-cache/ 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | testem.log 38 | /typings 39 | 40 | # System files 41 | .DS_Store 42 | Thumbs.db 43 | -------------------------------------------------------------------------------- /frontend/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 3 | "recommendations": ["angular.ng-template"] 4 | } 5 | -------------------------------------------------------------------------------- /frontend/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 3 | "version": "0.2.0", 4 | "configurations": [ 5 | { 6 | "name": "ng serve", 7 | "type": "chrome", 8 | "request": "launch", 9 | "preLaunchTask": "npm: start", 10 | "url": "http://localhost:4200/" 11 | }, 12 | { 13 | "name": "ng test", 14 | "type": "chrome", 15 | "request": "launch", 16 | "preLaunchTask": "npm: test", 17 | "url": "http://localhost:9876/debug.html" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /frontend/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558 3 | "version": "2.0.0", 4 | "tasks": [ 5 | { 6 | "type": "npm", 7 | "script": "start", 8 | "isBackground": true, 9 | "problemMatcher": { 10 | "owner": "typescript", 11 | "pattern": "$tsc", 12 | "background": { 13 | "activeOnStart": true, 14 | "beginsPattern": { 15 | "regexp": "(.*?)" 16 | }, 17 | "endsPattern": { 18 | "regexp": "bundle generation complete" 19 | } 20 | } 21 | } 22 | }, 23 | { 24 | "type": "npm", 25 | "script": "test", 26 | "isBackground": true, 27 | "problemMatcher": { 28 | "owner": "typescript", 29 | "pattern": "$tsc", 30 | "background": { 31 | "activeOnStart": true, 32 | "beginsPattern": { 33 | "regexp": "(.*?)" 34 | }, 35 | "endsPattern": { 36 | "regexp": "bundle generation complete" 37 | } 38 | } 39 | } 40 | } 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # NewFrontend 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 16.0.4. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application 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. 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 a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. 28 | -------------------------------------------------------------------------------- /frontend/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "newFrontend": { 7 | "projectType": "application", 8 | "schematics": {}, 9 | "root": "", 10 | "sourceRoot": "src", 11 | "prefix": "app", 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "outputPath": "dist/new-frontend", 17 | "index": "src/index.html", 18 | "main": "src/main.ts", 19 | "polyfills": ["zone.js"], 20 | "tsConfig": "tsconfig.app.json", 21 | "assets": ["src/favicon.ico", "src/assets"], 22 | "styles": [ 23 | "node_modules/@fortawesome/fontawesome-free/css/all.css", 24 | "src/styles.css" 25 | ], 26 | "scripts": [] 27 | }, 28 | "configurations": { 29 | "production": { 30 | "budgets": [ 31 | { 32 | "type": "initial", 33 | "maximumWarning": "500kb", 34 | "maximumError": "1mb" 35 | }, 36 | { 37 | "type": "anyComponentStyle", 38 | "maximumWarning": "2kb", 39 | "maximumError": "4kb" 40 | } 41 | ], 42 | "outputHashing": "all" 43 | }, 44 | "development": { 45 | "buildOptimizer": false, 46 | "optimization": false, 47 | "vendorChunk": true, 48 | "extractLicenses": false, 49 | "sourceMap": true, 50 | "namedChunks": true, 51 | "fileReplacements": [ 52 | { 53 | "replace": "src/environments/environment.ts", 54 | "with": "src/environments/environment.development.ts" 55 | } 56 | ] 57 | } 58 | }, 59 | "defaultConfiguration": "production" 60 | }, 61 | "serve": { 62 | "builder": "@angular-devkit/build-angular:dev-server", 63 | "configurations": { 64 | "production": { 65 | "browserTarget": "newFrontend:build:production" 66 | }, 67 | "development": { 68 | "browserTarget": "newFrontend:build:development" 69 | } 70 | }, 71 | "defaultConfiguration": "development" 72 | }, 73 | "extract-i18n": { 74 | "builder": "@angular-devkit/build-angular:extract-i18n", 75 | "options": { 76 | "browserTarget": "newFrontend:build" 77 | } 78 | }, 79 | "test": { 80 | "builder": "@angular-devkit/build-angular:karma", 81 | "options": { 82 | "polyfills": ["zone.js", "zone.js/testing"], 83 | "tsConfig": "tsconfig.spec.json", 84 | "assets": ["src/favicon.ico", "src/assets"], 85 | "styles": ["src/styles.css"], 86 | "scripts": [] 87 | } 88 | } 89 | } 90 | } 91 | }, 92 | "cli": { 93 | "analytics": "16050acf-7df8-46ba-b862-6cf8b42b9257" 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "new-frontend", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "watch": "ng build --watch --configuration development", 9 | "test": "ng test" 10 | }, 11 | "private": true, 12 | "dependencies": { 13 | "@angular/animations": "^16.0.0", 14 | "@angular/common": "^16.0.0", 15 | "@angular/compiler": "^16.0.0", 16 | "@angular/core": "^16.0.0", 17 | "@angular/forms": "^16.0.0", 18 | "@angular/platform-browser": "^16.0.0", 19 | "@angular/platform-browser-dynamic": "^16.0.0", 20 | "@angular/router": "^16.0.0", 21 | "@fortawesome/fontawesome-free": "^6.4.2", 22 | "@microsoft/signalr": "^7.0.11", 23 | "@swimlane/ngx-charts": "^20.4.1", 24 | "rxjs": "~7.8.0", 25 | "tslib": "^2.3.0", 26 | "zone.js": "~0.13.0" 27 | }, 28 | "devDependencies": { 29 | "@angular-devkit/build-angular": "^16.0.4", 30 | "@angular/cli": "~16.0.4", 31 | "@angular/compiler-cli": "^16.0.0", 32 | "@types/jasmine": "~4.3.0", 33 | "jasmine-core": "~4.6.0", 34 | "karma": "~6.4.0", 35 | "karma-chrome-launcher": "~3.2.0", 36 | "karma-coverage": "~2.2.0", 37 | "karma-jasmine": "~5.1.0", 38 | "karma-jasmine-html-reporter": "~2.0.0", 39 | "typescript": "~5.0.2" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /frontend/src/app/about/container/about/about.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FabianGosebrink/ASPNETCore-Angular-SignalR-Typescript/9d9bea4e9295246c83ef9bd216890f69dcc1c4e9/frontend/src/app/about/container/about/about.component.css -------------------------------------------------------------------------------- /frontend/src/app/about/container/about/about.component.html: -------------------------------------------------------------------------------- 1 |

2 | about works!! 3 |

4 | -------------------------------------------------------------------------------- /frontend/src/app/about/container/about/about.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { AboutComponent } from './about.component'; 4 | 5 | describe('AboutComponent', () => { 6 | let component: AboutComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | imports: [AboutComponent] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(AboutComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/src/app/about/container/about/about.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-about', 5 | templateUrl: './about.component.html', 6 | styleUrls: ['./about.component.css'], 7 | standalone: true, 8 | }) 9 | export class AboutComponent {} 10 | -------------------------------------------------------------------------------- /frontend/src/app/about/index.ts: -------------------------------------------------------------------------------- 1 | import { AboutComponent } from './container/about/about.component'; 2 | 3 | export const ABOUT_ROUTES = [{ path: '', component: AboutComponent }]; 4 | -------------------------------------------------------------------------------- /frontend/src/app/app-routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | 3 | export const APP_ROUTES: Routes = [ 4 | { path: '', redirectTo: 'dashboard', pathMatch: 'full' }, 5 | { 6 | path: 'dashboard', 7 | loadChildren: () => import('./dashboard').then((m) => m.DASHBOARD_ROUTES), 8 | }, 9 | { 10 | path: 'about', 11 | loadChildren: () => import('./about').then((m) => m.ABOUT_ROUTES), 12 | }, 13 | ]; 14 | -------------------------------------------------------------------------------- /frontend/src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FabianGosebrink/ASPNETCore-Angular-SignalR-Typescript/9d9bea4e9295246c83ef9bd216890f69dcc1c4e9/frontend/src/app/app.component.css -------------------------------------------------------------------------------- /frontend/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /frontend/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(() => TestBed.configureTestingModule({ 7 | imports: [RouterTestingModule, AppComponent] 8 | })); 9 | 10 | it('should create the app', () => { 11 | const fixture = TestBed.createComponent(AppComponent); 12 | const app = fixture.componentInstance; 13 | expect(app).toBeTruthy(); 14 | }); 15 | 16 | it(`should have as title 'newFrontend'`, () => { 17 | const fixture = TestBed.createComponent(AppComponent); 18 | const app = fixture.componentInstance; 19 | expect(app.title).toEqual('newFrontend'); 20 | }); 21 | 22 | it('should render title', () => { 23 | const fixture = TestBed.createComponent(AppComponent); 24 | fixture.detectChanges(); 25 | const compiled = fixture.nativeElement as HTMLElement; 26 | expect(compiled.querySelector('.content span')?.textContent).toContain('newFrontend app is running!'); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /frontend/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { NgSwitch, NgSwitchCase, NgSwitchDefault } from '@angular/common'; 2 | import { Component } from '@angular/core'; 3 | import { RouterOutlet } from '@angular/router'; 4 | import { NavigationComponent } from './shared/navigation/navigation.component'; 5 | 6 | @Component({ 7 | selector: 'app-root', 8 | templateUrl: './app.component.html', 9 | styleUrls: ['./app.component.css'], 10 | standalone: true, 11 | imports: [ 12 | NgSwitch, 13 | NgSwitchDefault, 14 | NgSwitchCase, 15 | RouterOutlet, 16 | NavigationComponent, 17 | ], 18 | }) 19 | export class AppComponent { 20 | title = 'frontend'; 21 | } 22 | -------------------------------------------------------------------------------- /frontend/src/app/app.config.ts: -------------------------------------------------------------------------------- 1 | import { provideHttpClient } from '@angular/common/http'; 2 | import { ApplicationConfig, importProvidersFrom } from '@angular/core'; 3 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 4 | import { provideRouter } from '@angular/router'; 5 | import { APP_ROUTES } from './app-routes'; 6 | 7 | export const appConfig: ApplicationConfig = { 8 | providers: [ 9 | provideRouter(APP_ROUTES), 10 | provideHttpClient(), 11 | importProvidersFrom(BrowserAnimationsModule), 12 | ], 13 | }; 14 | -------------------------------------------------------------------------------- /frontend/src/app/dashboard/container/dashboard/dashboard.component.css: -------------------------------------------------------------------------------- 1 | .wrapper { 2 | width: 90%; 3 | min-height: 300px; 4 | border-radius: 21px 21px 0 0; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/src/app/dashboard/container/dashboard/dashboard.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 8 |
9 |
10 | 11 |
12 |
13 | 19 |
20 |
21 | 22 |
23 |
24 | 29 |
30 |
31 |
32 | 33 | Loading... 34 | -------------------------------------------------------------------------------- /frontend/src/app/dashboard/container/dashboard/dashboard.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { DashboardComponent } from './dashboard.component'; 4 | 5 | describe('DashboardComponent', () => { 6 | let component: DashboardComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ DashboardComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(DashboardComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/src/app/dashboard/container/dashboard/dashboard.component.ts: -------------------------------------------------------------------------------- 1 | import { AsyncPipe, NgIf } from '@angular/common'; 2 | import { Component, OnInit, inject } from '@angular/core'; 3 | import { Observable } from 'rxjs'; 4 | import { ChatMessage } from '../../../models/chat-message.model'; 5 | import { FoodItem } from '../../../models/foodItem.model'; 6 | import { FoodDataService } from '../../../services/food-data.service'; 7 | import { SignalRService } from '../../../services/signalR.service'; 8 | import { ChatComponent } from '../../presentational/chat/chat.component'; 9 | import { CpuComponent } from '../../presentational/cpu/cpu.component'; 10 | import { FoodComponent } from '../../presentational/food/food.component'; 11 | 12 | @Component({ 13 | selector: 'app-dashboard', 14 | templateUrl: './dashboard.component.html', 15 | styleUrls: ['./dashboard.component.css'], 16 | standalone: true, 17 | imports: [CpuComponent, FoodComponent, ChatComponent, AsyncPipe, NgIf], 18 | }) 19 | export class DashboardComponent implements OnInit { 20 | private readonly signalRService = inject(SignalRService); 21 | private readonly foodDataService = inject(FoodDataService); 22 | 23 | cpuValue$ = this.signalRService.newCpuValue$; 24 | signalrConnectionEstablished$ = this.signalRService.connectionEstablished$; 25 | foodItems$: Observable; 26 | chatmessages = []; 27 | 28 | ngOnInit() { 29 | this.signalRService.foodChanged$.subscribe(() => this.getFoodData()); 30 | 31 | this.signalRService.messageReceived$.subscribe((message) => { 32 | this.chatmessages = [...this.chatmessages, message]; 33 | }); 34 | 35 | this.getFoodData(); 36 | } 37 | 38 | saveFood(item: FoodItem) { 39 | if (item.id) { 40 | this.foodDataService 41 | .updateFood(item) 42 | .subscribe(() => console.log('food updated')); 43 | } else { 44 | this.foodDataService 45 | .addFood(item.itemName) 46 | .subscribe(() => console.log('food added')); 47 | } 48 | } 49 | 50 | deleteFood(item: FoodItem) { 51 | if (!confirm('Really delete?')) { 52 | return; 53 | } 54 | 55 | this.foodDataService 56 | .deleteFood(item.id) 57 | .subscribe(() => console.log('food deleted')); 58 | } 59 | 60 | sendChat(message: ChatMessage) { 61 | this.signalRService.sendChatMessage(message); 62 | } 63 | 64 | private getFoodData() { 65 | this.foodItems$ = this.foodDataService.getAllFood(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /frontend/src/app/dashboard/index.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | import { DashboardComponent } from './container/dashboard/dashboard.component'; 3 | 4 | export const DASHBOARD_ROUTES: Routes = [ 5 | { path: '', component: DashboardComponent }, 6 | ]; 7 | -------------------------------------------------------------------------------- /frontend/src/app/dashboard/presentational/chat/chat.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FabianGosebrink/ASPNETCore-Angular-SignalR-Typescript/9d9bea4e9295246c83ef9bd216890f69dcc1c4e9/frontend/src/app/dashboard/presentational/chat/chat.component.css -------------------------------------------------------------------------------- /frontend/src/app/dashboard/presentational/chat/chat.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 9 |
10 | 17 |
18 | 19 |
    20 |
  • 21 | {{ message.sent | date: 'medium' }}: {{ message.message }} 22 |
  • 23 |
24 | -------------------------------------------------------------------------------- /frontend/src/app/dashboard/presentational/chat/chat.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { ChatComponent } from './chat.component'; 4 | 5 | describe('ChatComponent', () => { 6 | let component: ChatComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ ChatComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(ChatComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/src/app/dashboard/presentational/chat/chat.component.ts: -------------------------------------------------------------------------------- 1 | import { DatePipe, NgFor } from '@angular/common'; 2 | import { 3 | ChangeDetectionStrategy, 4 | Component, 5 | EventEmitter, 6 | Input, 7 | Output, 8 | inject, 9 | } from '@angular/core'; 10 | import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; 11 | 12 | @Component({ 13 | selector: 'app-chat', 14 | templateUrl: './chat.component.html', 15 | styleUrls: ['./chat.component.css'], 16 | changeDetection: ChangeDetectionStrategy.OnPush, 17 | standalone: true, 18 | imports: [DatePipe, ReactiveFormsModule, NgFor], 19 | }) 20 | export class ChatComponent { 21 | private readonly formbuilder = inject(FormBuilder); 22 | 23 | @Input() chatmessages = []; 24 | 25 | @Input() connectionEstablished = false; 26 | 27 | @Output() sendChat = new EventEmitter(); 28 | 29 | form = this.formbuilder.group({ 30 | chatmessage: ['', Validators.required], 31 | }); 32 | 33 | send() { 34 | const chatMessage = { 35 | sent: new Date().toISOString(), 36 | message: this.form.value.chatmessage, 37 | }; 38 | this.sendChat.emit(chatMessage); 39 | this.form.reset(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /frontend/src/app/dashboard/presentational/cpu/cpu.component.css: -------------------------------------------------------------------------------- 1 | .container-center { 2 | display: flex; 3 | justify-content: center; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/src/app/dashboard/presentational/cpu/cpu.component.html: -------------------------------------------------------------------------------- 1 |

CPU Value

2 | 3 |
4 | 15 | 16 | 17 |
18 | -------------------------------------------------------------------------------- /frontend/src/app/dashboard/presentational/cpu/cpu.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { CpuComponent } from './cpu.component'; 4 | 5 | describe('CpuComponent', () => { 6 | let component: CpuComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ CpuComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(CpuComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/src/app/dashboard/presentational/cpu/cpu.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; 2 | import { NgxChartsModule } from '@swimlane/ngx-charts'; 3 | 4 | @Component({ 5 | selector: 'app-cpu', 6 | templateUrl: './cpu.component.html', 7 | styleUrls: ['./cpu.component.css'], 8 | standalone: true, 9 | imports: [NgxChartsModule], 10 | }) 11 | export class CpuComponent implements OnChanges { 12 | @Input() cpuValue: number; 13 | 14 | view: number[] = [400, 400]; 15 | data: { name: string; value: number }[] = []; 16 | 17 | colorScheme = { 18 | domain: ['#5AA454'], 19 | }; 20 | 21 | ngOnChanges(changes: SimpleChanges) { 22 | if (changes.cpuValue) { 23 | this.renderChart(changes.cpuValue.currentValue); 24 | } 25 | } 26 | 27 | renderChart(cpuValue: number) { 28 | this.data = [ 29 | { 30 | name: 'CPU', 31 | value: cpuValue || 0, 32 | }, 33 | ]; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /frontend/src/app/dashboard/presentational/food/food.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FabianGosebrink/ASPNETCore-Angular-SignalR-Typescript/9d9bea4e9295246c83ef9bd216890f69dcc1c4e9/frontend/src/app/dashboard/presentational/food/food.component.css -------------------------------------------------------------------------------- /frontend/src/app/dashboard/presentational/food/food.component.html: -------------------------------------------------------------------------------- 1 |

FoodList

2 | 3 |
4 |
5 | 6 | 12 |
13 | 20 |
21 | 22 |
23 |
    24 |
  • 25 | {{ item.itemName }} 26 | 27 | 35 | 43 | 44 |
  • 45 |
46 |
47 | -------------------------------------------------------------------------------- /frontend/src/app/dashboard/presentational/food/food.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { FoodComponent } from './food.component'; 4 | 5 | describe('FoodComponent', () => { 6 | let component: FoodComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ FoodComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(FoodComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/src/app/dashboard/presentational/food/food.component.ts: -------------------------------------------------------------------------------- 1 | import { NgFor } from '@angular/common'; 2 | import { Component, EventEmitter, Input, Output, inject } from '@angular/core'; 3 | import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; 4 | import { FoodItem } from '../../../models/foodItem.model'; 5 | 6 | @Component({ 7 | selector: 'app-food', 8 | templateUrl: './food.component.html', 9 | styleUrls: ['./food.component.css'], 10 | standalone: true, 11 | imports: [ReactiveFormsModule, NgFor], 12 | }) 13 | export class FoodComponent { 14 | private readonly formbuilder = inject(FormBuilder); 15 | @Input() foodItems = []; 16 | @Input() connectionEstablished = false; 17 | 18 | @Output() foodSaved = new EventEmitter(); 19 | @Output() foodDeleted = new EventEmitter(); 20 | 21 | form = this.formbuilder.group({ 22 | id: 0, 23 | itemName: ['', Validators.required], 24 | }); 25 | 26 | saveFood() { 27 | this.foodSaved.emit(this.form.value as FoodItem); 28 | this.form.reset(); 29 | } 30 | 31 | setFoodItemToEdit(foodItem: FoodItem) { 32 | const { itemName, id } = foodItem; 33 | 34 | this.form.patchValue({ itemName, id }); 35 | } 36 | 37 | deleteFoodItem(foodItem: FoodItem) { 38 | this.foodDeleted.emit(foodItem); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /frontend/src/app/models/chat-message.model.ts: -------------------------------------------------------------------------------- 1 | export interface ChatMessage { 2 | message: string; 3 | sent: string; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/src/app/models/foodItem.model.ts: -------------------------------------------------------------------------------- 1 | export interface FoodItem { 2 | id: number; 3 | itemName: string; 4 | created: Date; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/src/app/services/food-data.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient } from '@angular/common/http'; 2 | import { Injectable, inject } from '@angular/core'; 3 | import { environment } from '../../environments/environment'; 4 | import { FoodItem } from '../models/foodItem.model'; 5 | 6 | @Injectable({ providedIn: 'root' }) 7 | export class FoodDataService { 8 | private readonly http = inject(HttpClient); 9 | 10 | private actionUrl = 11 | environment.baseUrls.server + environment.baseUrls.apiUrl + 'foodItems/'; 12 | 13 | getAllFood() { 14 | return this.http.get(this.actionUrl); 15 | } 16 | 17 | getSingleFood(id: number) { 18 | return this.http.get(this.actionUrl + id); 19 | } 20 | 21 | addFood(itemName: string) { 22 | return this.http.post(this.actionUrl, { itemName }); 23 | } 24 | 25 | updateFood(foodToUpdate: FoodItem) { 26 | return this.http.put( 27 | this.actionUrl + foodToUpdate.id, 28 | foodToUpdate 29 | ); 30 | } 31 | 32 | deleteFood(id: number) { 33 | return this.http.delete(this.actionUrl + id); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /frontend/src/app/services/signalR.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { 3 | HubConnection, 4 | HubConnectionBuilder, 5 | HubConnectionState, 6 | LogLevel, 7 | } from '@microsoft/signalr'; 8 | import { BehaviorSubject, Subject } from 'rxjs'; 9 | import { environment } from '../../environments/environment'; 10 | import { ChatMessage } from '../models/chat-message.model'; 11 | 12 | @Injectable({ providedIn: 'root' }) 13 | export class SignalRService { 14 | private hubConnection: HubConnection; 15 | 16 | private foodchanged = new Subject(); 17 | 18 | private messageReceived = new Subject(); 19 | 20 | private newCpuValue = new Subject(); 21 | 22 | private connectionEstablished = new BehaviorSubject(false); 23 | 24 | get foodChanged$() { 25 | return this.foodchanged.asObservable(); 26 | } 27 | 28 | get messageReceived$() { 29 | return this.messageReceived.asObservable(); 30 | } 31 | 32 | get newCpuValue$() { 33 | return this.newCpuValue.asObservable(); 34 | } 35 | 36 | get connectionEstablished$() { 37 | return this.connectionEstablished.asObservable(); 38 | } 39 | 40 | constructor() { 41 | this.createConnection(); 42 | this.registerOnServerEvents(); 43 | this.startConnection(); 44 | } 45 | 46 | sendChatMessage(message: ChatMessage) { 47 | this.hubConnection.invoke('SendMessage', message); 48 | } 49 | 50 | private createConnection() { 51 | this.hubConnection = new HubConnectionBuilder() 52 | .withUrl(environment.baseUrls.server + 'coolmessages') 53 | .withAutomaticReconnect() 54 | .configureLogging(LogLevel.Information) 55 | .build(); 56 | } 57 | 58 | private startConnection() { 59 | if (this.hubConnection.state === HubConnectionState.Connected) { 60 | return; 61 | } 62 | 63 | this.hubConnection.start().then( 64 | () => { 65 | this.connectionEstablished.next(true); 66 | }, 67 | (error) => console.error(error) 68 | ); 69 | } 70 | 71 | private registerOnServerEvents(): void { 72 | this.hubConnection.on('FoodAdded', (data) => { 73 | this.foodchanged.next(data); 74 | }); 75 | 76 | this.hubConnection.on('FoodDeleted', () => { 77 | this.foodchanged.next('this could be data'); 78 | }); 79 | 80 | this.hubConnection.on('FoodUpdated', () => { 81 | this.foodchanged.next('this could be data'); 82 | }); 83 | 84 | this.hubConnection.on('Send', (data) => { 85 | this.messageReceived.next(data); 86 | }); 87 | 88 | this.hubConnection.on('newCpuValue', (data: number) => { 89 | this.newCpuValue.next(data); 90 | }); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /frontend/src/app/shared/navigation/navigation.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FabianGosebrink/ASPNETCore-Angular-SignalR-Typescript/9d9bea4e9295246c83ef9bd216890f69dcc1c4e9/frontend/src/app/shared/navigation/navigation.component.css -------------------------------------------------------------------------------- /frontend/src/app/shared/navigation/navigation.component.html: -------------------------------------------------------------------------------- 1 | 28 | -------------------------------------------------------------------------------- /frontend/src/app/shared/navigation/navigation.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { NavigationComponent } from './navigation.component'; 4 | 5 | describe('NavigationComponent', () => { 6 | let component: NavigationComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ NavigationComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(NavigationComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/src/app/shared/navigation/navigation.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { RouterModule } from '@angular/router'; 3 | 4 | @Component({ 5 | selector: 'app-navigation', 6 | templateUrl: './navigation.component.html', 7 | styleUrls: ['./navigation.component.css'], 8 | standalone: true, 9 | imports: [RouterModule], 10 | }) 11 | export class NavigationComponent {} 12 | -------------------------------------------------------------------------------- /frontend/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FabianGosebrink/ASPNETCore-Angular-SignalR-Typescript/9d9bea4e9295246c83ef9bd216890f69dcc1c4e9/frontend/src/assets/.gitkeep -------------------------------------------------------------------------------- /frontend/src/environments/environment.development.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: false, 3 | baseUrls: { 4 | server: 'https://localhost:5001/', 5 | apiUrl: 'api/', 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /frontend/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | baseUrls: { 4 | server: 'https://aspnetcore20190520055459.azurewebsites.net/', 5 | apiUrl: 'api/', 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /frontend/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FabianGosebrink/ASPNETCore-Angular-SignalR-Typescript/9d9bea4e9295246c83ef9bd216890f69dcc1c4e9/frontend/src/favicon.ico -------------------------------------------------------------------------------- /frontend/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NewFrontend 6 | 7 | 8 | 9 | 15 | 16 | 17 | 18 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /frontend/src/main.ts: -------------------------------------------------------------------------------- 1 | import { bootstrapApplication } from '@angular/platform-browser'; 2 | import { AppComponent } from './app/app.component'; 3 | import { appConfig } from './app/app.config'; 4 | 5 | bootstrapApplication(AppComponent, appConfig).catch((err) => 6 | console.error(err) 7 | ); 8 | -------------------------------------------------------------------------------- /frontend/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /frontend/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "src/main.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "outDir": "./dist/out-tsc", 7 | "forceConsistentCasingInFileNames": true, 8 | "strict": false, 9 | "noImplicitOverride": true, 10 | "noPropertyAccessFromIndexSignature": false, 11 | "noImplicitReturns": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "sourceMap": true, 14 | "declaration": false, 15 | "downlevelIteration": true, 16 | "experimentalDecorators": true, 17 | "moduleResolution": "node", 18 | "importHelpers": true, 19 | "target": "ES2022", 20 | "module": "ES2022", 21 | "useDefineForClassFields": false, 22 | "lib": ["ES2022", "dom"] 23 | }, 24 | "angularCompilerOptions": { 25 | "enableI18nLegacyMessageIdFormat": false, 26 | "strictInjectionParameters": true, 27 | "strictInputAccessModifiers": true, 28 | "strictTemplates": true 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /frontend/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": [ 7 | "jasmine" 8 | ] 9 | }, 10 | "include": [ 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | --------------------------------------------------------------------------------