From 26664449fc282017fec31f187d9e3f47d25fece1 Mon Sep 17 00:00:00 2001 From: Alexis DRAI Date: Fri, 20 Jan 2023 12:24:36 +0100 Subject: [PATCH] :robot: Implement DI and IoC --- blazor_lab/Pages/Add.razor.cs | 47 +---------- blazor_lab/Pages/List.razor.cs | 31 ++------ blazor_lab/Program.cs | 3 + blazor_lab/Services/DataLocalService.cs | 99 ++++++++++++++++++++++++ blazor_lab/Services/IDataService.cs | 11 +++ blazor_lab/blazor_lab.csproj | 2 +- blazor_lab/wwwroot/images/zdada.png | Bin 0 -> 15791 bytes 7 files changed, 125 insertions(+), 68 deletions(-) create mode 100644 blazor_lab/Services/DataLocalService.cs create mode 100644 blazor_lab/Services/IDataService.cs create mode 100644 blazor_lab/wwwroot/images/zdada.png diff --git a/blazor_lab/Pages/Add.razor.cs b/blazor_lab/Pages/Add.razor.cs index 935ec0c..6badefb 100644 --- a/blazor_lab/Pages/Add.razor.cs +++ b/blazor_lab/Pages/Add.razor.cs @@ -1,5 +1,5 @@ using blazor_lab.Models; -using Blazored.LocalStorage; +using blazor_lab.Services; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Forms; @@ -9,13 +9,10 @@ namespace blazor_lab.Pages { [Inject] - public ILocalStorageService LocalStorageService { get; set; } + public IDataService DataService { get; set; } [Inject] - public IWebHostEnvironment WebHostEnvironment { get; set; } - - [Inject] - public NavigationManager NavigationManager { get; set; } + public NavigationManager NavigationManager { get; set; } /// /// The default enchant categories. @@ -39,43 +36,7 @@ namespace blazor_lab.Pages private async void HandleValidSubmit() { - // Get the current data - var currentData = await LocalStorageService.GetItemAsync>("data"); - - // Simulate the Id - itemModel.Id = currentData.Max(item => item.Id) + 1; - - // Add the item to the current data - currentData.Add(new Item - { - Id = itemModel.Id, - DisplayName = itemModel.DisplayName, - Name = itemModel.Name, - RepairWith = itemModel.RepairWith, - EnchantCategories = itemModel.EnchantCategories, - MaxDurability = itemModel.MaxDurability, - StackSize = itemModel.StackSize, - CreatedDate = DateTime.Now - }); - - // Save the image - var imagePathInfo = new DirectoryInfo($"{WebHostEnvironment.WebRootPath}/images"); - - // Check if the folder "images" exist - if (!imagePathInfo.Exists) - { - imagePathInfo.Create(); - } - - // Determine the image name - var fileName = new FileInfo($"{imagePathInfo}/{itemModel.Name}.png"); - - // Write the file content - await File.WriteAllBytesAsync(fileName.FullName, itemModel.ImageContent); - - // Save the data - await LocalStorageService.SetItemAsync("data", currentData); - + await DataService.Add(itemModel); NavigationManager.NavigateTo("list"); } private async Task LoadImage(InputFileChangeEventArgs e) diff --git a/blazor_lab/Pages/List.razor.cs b/blazor_lab/Pages/List.razor.cs index 818b8af..f5f5870 100644 --- a/blazor_lab/Pages/List.razor.cs +++ b/blazor_lab/Pages/List.razor.cs @@ -1,4 +1,5 @@ using blazor_lab.Models; +using blazor_lab.Services; using Blazored.LocalStorage; using Blazorise.DataGrid; using Microsoft.AspNetCore.Components; @@ -12,16 +13,10 @@ namespace blazor_lab.Pages private int totalItems; [Inject] - public HttpClient HttpClient { get; set; } + public IDataService DataService { get; set; } [Inject] - public ILocalStorageService LocalStorageService { get; set; } - - [Inject] - public NavigationManager NavigationManager { get; set; } - - [Inject] - public IWebHostEnvironment WebHostEnvironment { get; set; } + public IWebHostEnvironment WebHostEnvironment { get; set; } protected override async Task OnAfterRenderAsync(bool firstRender) { @@ -31,15 +26,7 @@ namespace blazor_lab.Pages return; } - var currentData = await LocalStorageService.GetItemAsync("data"); - - // Check if data exist in the local storage - if (currentData == null) - { - // this code add in the local storage the fake data (we load the data sync for initialize the data before load the OnReadData method) - var originalData = HttpClient.GetFromJsonAsync($"{NavigationManager.BaseUri}fake-data.json").Result; - await LocalStorageService.SetItemAsync("data", originalData); - } + var currentData = await DataService.List(1, 10); } private async Task OnReadData(DataGridReadDataEventArgs e) @@ -49,20 +36,16 @@ namespace blazor_lab.Pages return; } - //Real API => - //var response = await HttpClient.GetJsonAsync( $"http://my-api/api/data?page={e.Page}&pageSize={e.PageSize}" ); - var response = (await LocalStorageService.GetItemAsync("data")).Skip((e.Page - 1) * e.PageSize).Take(e.PageSize).ToList(); - if (!e.CancellationToken.IsCancellationRequested) { - totalItems = (await LocalStorageService.GetItemAsync>("data")).Count; - items = new List(response); // an actual data for the current page + items = await DataService.List(e.Page, e.PageSize); + totalItems = await DataService.Count(); } } protected override async Task OnInitializedAsync() { - items = await HttpClient.GetFromJsonAsync>($"{NavigationManager.BaseUri}fake-data.json"); + items = await DataService.List(1, 10); } } } diff --git a/blazor_lab/Program.cs b/blazor_lab/Program.cs index 6be187b..e04d440 100644 --- a/blazor_lab/Program.cs +++ b/blazor_lab/Program.cs @@ -3,6 +3,7 @@ using Blazorise; using Blazorise.Bootstrap; using Blazorise.Icons.FontAwesome; using Blazored.LocalStorage; +using blazor_lab.Services; var builder = WebApplication.CreateBuilder(args); @@ -20,6 +21,8 @@ builder.Services builder.Services.AddBlazoredLocalStorage(); +builder.Services.AddScoped(); + var app = builder.Build(); // Configure the HTTP request pipeline. diff --git a/blazor_lab/Services/DataLocalService.cs b/blazor_lab/Services/DataLocalService.cs new file mode 100644 index 0000000..d4c05d4 --- /dev/null +++ b/blazor_lab/Services/DataLocalService.cs @@ -0,0 +1,99 @@ +using blazor_lab.Models; +using Blazored.LocalStorage; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Hosting; + +namespace blazor_lab.Services +{ + public class DataLocalService : IDataService + { + + + private readonly HttpClient _httpClient; + private readonly ILocalStorageService _localStorageService; + private readonly NavigationManager _navigationManager; + private readonly IWebHostEnvironment _webHostEnvironment; + private readonly string pathToFakeData; + + public DataLocalService( + HttpClient httpClient, + ILocalStorageService localStorageService, + NavigationManager navigationManager, + IWebHostEnvironment webHostEnvironment + ) + { + _httpClient = httpClient; + _localStorageService = localStorageService; + _navigationManager = navigationManager; + _webHostEnvironment = webHostEnvironment; + pathToFakeData = $"{_navigationManager.BaseUri}fake-data.json"; + } + + public async Task Add(ItemModel itemModel) + { + // Get the current data + var currentData = await _localStorageService.GetItemAsync>("data"); + + // Simulate the Id + itemModel.Id = currentData.Max(item => item.Id) + 1; + + // Add the item to the current data + currentData.Add(new Item + { + Id = itemModel.Id, + DisplayName = itemModel.DisplayName, + Name = itemModel.Name, + RepairWith = itemModel.RepairWith, + EnchantCategories = itemModel.EnchantCategories, + MaxDurability = itemModel.MaxDurability, + StackSize = itemModel.StackSize, + CreatedDate = DateTime.Now + }); + + // Save the image + var imagePathInfo = new DirectoryInfo($"{_webHostEnvironment.WebRootPath}/images"); + + // Check if the folder "images" exist + if (!imagePathInfo.Exists) + { + imagePathInfo.Create(); + } + + // Determine the image name + var fileName = new FileInfo($"{imagePathInfo}/{itemModel.Name}.png"); + + // Write the file content + await File.WriteAllBytesAsync(fileName.FullName, itemModel.ImageContent); + + // Save the data + await _localStorageService.SetItemAsync("data", currentData); + } + + public async Task Count() + { + if (await _localStorageService.GetItemAsync("data") == null) + { + var originalData = + await _httpClient + .GetFromJsonAsync(pathToFakeData); + + await _localStorageService.SetItemAsync("data", originalData); + } + + return (await _localStorageService.GetItemAsync("data")).Length; + } + + public async Task> List(int currentPage, int pageSize) + { + if (await _localStorageService.GetItemAsync("data") == null) + { + var originalData = + await _httpClient + .GetFromJsonAsync(pathToFakeData); + await _localStorageService.SetItemAsync("data", originalData); + } + + return (await _localStorageService.GetItemAsync("data")).Skip((currentPage - 1) * pageSize).Take(pageSize).ToList(); + } + } +} diff --git a/blazor_lab/Services/IDataService.cs b/blazor_lab/Services/IDataService.cs new file mode 100644 index 0000000..5e2c307 --- /dev/null +++ b/blazor_lab/Services/IDataService.cs @@ -0,0 +1,11 @@ +using blazor_lab.Models; + +namespace blazor_lab.Services +{ + public interface IDataService + { + Task Add(ItemModel itemModel); + Task Count(); + Task> List(int currentPage, int pageSize); + } +} diff --git a/blazor_lab/blazor_lab.csproj b/blazor_lab/blazor_lab.csproj index bf3e670..c45c8d4 100644 --- a/blazor_lab/blazor_lab.csproj +++ b/blazor_lab/blazor_lab.csproj @@ -1,4 +1,4 @@ - + net6.0 diff --git a/blazor_lab/wwwroot/images/zdada.png b/blazor_lab/wwwroot/images/zdada.png new file mode 100644 index 0000000000000000000000000000000000000000..989ea6a559d70d4557ffa8c8f70fd64dac5dc94d GIT binary patch literal 15791 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 R3mCG7Q;<=Su8}kg{%^&Ie?R~L literal 0 HcmV?d00001