Skip to content

Commit 6cb70c5

Browse files
marin-bratanovDimo Dimovdimodi
authored
File select in grid (#167)
* feat(fileSelect, grid): file select in grid row sample * chore: fix readme location * chore: hints on wasm * Allow files up to 1MB, fix CSS and use cross-platform path separator * Update fileselect/fileselect-in-grid-row/readme.md Co-authored-by: Dimo Dimov <[email protected]> Co-authored-by: Dimo Dimov <[email protected]>
1 parent 694ce4f commit 6cb70c5

28 files changed

+1356
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 16
4+
VisualStudioVersion = 16.0.31515.178
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "fileselect_in_grid_row", "fileselect-in-grid-row\fileselect_in_grid_row.csproj", "{16535648-C0EA-4911-888A-1A907EB9461C}"
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+
{16535648-C0EA-4911-888A-1A907EB9461C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{16535648-C0EA-4911-888A-1A907EB9461C}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{16535648-C0EA-4911-888A-1A907EB9461C}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{16535648-C0EA-4911-888A-1A907EB9461C}.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 = {FE66770B-1980-45F9-926B-5E9AC70539F3}
24+
EndGlobalSection
25+
EndGlobal
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<Router AppAssembly="@typeof(App).Assembly">
2+
<Found Context="routeData">
3+
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
4+
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
5+
</Found>
6+
<NotFound>
7+
<PageTitle>Not found</PageTitle>
8+
<LayoutView Layout="@typeof(MainLayout)">
9+
<p role="alert">Sorry, there's nothing at this address.</p>
10+
</LayoutView>
11+
</NotFound>
12+
</Router>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
@page "/"
2+
3+
@using System.IO
4+
@using Microsoft.AspNetCore.Hosting
5+
@using System.Threading
6+
@using Telerik.Blazor.Components.FileSelect
7+
8+
@inject IWebHostEnvironment HostingEnvironment
9+
10+
11+
NOTE: This snippet shows one example how to associate file from a FileSelect.
12+
It does not implement full editing (e.g., you would typically implement this in an editor template
13+
and add the necessary CRUD operations and services, and add UI to show the file list in "view" mode).
14+
For brevity, this just shows the approach of using
15+
a lambda expression to provide the necessary metadata to the code that will save the file.
16+
17+
<TelerikGrid Data="@MyData" Height="700px"
18+
Pageable="true" PageSize="5" Sortable="true" Groupable="true"
19+
FilterMode="@GridFilterMode.FilterRow"
20+
Resizable="true" Reorderable="true">
21+
<GridColumns>
22+
<GridColumn Field="@(nameof(Employee.Id))" Width="120px" />
23+
<GridColumn Field="@(nameof(Employee.Name))" Title="Employee Name" Groupable="false" />
24+
<GridColumn Title="Associated Files (max 1MB / file)">
25+
<Template>
26+
@{
27+
Employee empl = context as Employee;
28+
// pass the current grid row in a lambda expression to the select handler to use when saving the files
29+
<TelerikFileSelect OnSelect="@( (FileSelectEventArgs e) => SaveFilesWithMetadata(e, empl) )"
30+
MaxFileSize="@(1024 * 1024)"/>
31+
}
32+
</Template>
33+
</GridColumn>
34+
<GridColumn Field="@(nameof(Employee.Team))" Title="Team" />
35+
</GridColumns>
36+
</TelerikGrid>
37+
38+
@code {
39+
public IEnumerable<Employee> MyData = Enumerable.Range(1, 30).Select(x => new Employee
40+
{
41+
Id = x,
42+
Name = "name " + x,
43+
Team = "team " + x % 5
44+
});
45+
46+
public Dictionary<string, CancellationTokenSource> Tokens { get; set; } = new Dictionary<string, CancellationTokenSource>();
47+
48+
private async Task SaveFilesWithMetadata(FileSelectEventArgs args, Employee empl)
49+
{
50+
int i = 0;
51+
foreach (var file in args.Files)
52+
{
53+
if (!file.InvalidExtension)
54+
{
55+
// we will use the ID of the grid record to alter the way the file is saved
56+
// implement the required businss logic instead
57+
// in this example, we make a folder for each employee by ID, put files by index there and add a GUID for uniqueness
58+
string folder = HostingEnvironment?.WebRootPath + Path.DirectorySeparatorChar + empl.Id;
59+
string fileName = i.ToString() + "_" + Guid.NewGuid() + "_" + file.Name;
60+
await SaveFileToDisk(file, folder, fileName);
61+
i++;
62+
// additionally, you may want to update the model to "know" about those files associated with it
63+
// for example, by adding a file name to its list. This is not done here for brevity
64+
// as how and what needs to be stored depends on the business logic
65+
// so this example won't persist the files information
66+
}
67+
}
68+
}
69+
70+
// this only saves the file to disk. Implement the business logic as needed and tweak this as needed for your case.
71+
private async Task SaveFileToDisk(FileSelectFileInfo file, string folder, string fileName)
72+
{
73+
Tokens.Add(file.Id, new CancellationTokenSource());
74+
if (!Directory.Exists(folder))
75+
{
76+
Directory.CreateDirectory(folder);
77+
}
78+
var path = Path.Combine(folder, fileName);
79+
await using FileStream fs = new FileStream(path, FileMode.Create);
80+
await file.Stream.CopyToAsync(fs, Tokens[file.Id].Token);
81+
}
82+
83+
84+
public class Employee
85+
{
86+
public int Id { get; set; }
87+
public string Name { get; set; }
88+
public List<string> Files { get; set; }
89+
public string Team { get; set; }
90+
public DateTime HireDate { get; set; }
91+
}
92+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
@page "/"
2+
@namespace fileselect_in_grid_row.Pages
3+
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
4+
5+
<!DOCTYPE html>
6+
<html lang="en">
7+
<head>
8+
<meta charset="utf-8" />
9+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
10+
<title>fileselect_in_grid_row</title>
11+
<base href="~/" />
12+
<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
13+
<link href="css/site.css" rel="stylesheet" />
14+
<link href="_content/Telerik.UI.for.Blazor/css/kendo-theme-default/all.css" rel="stylesheet" />
15+
</head>
16+
<body>
17+
<component type="typeof(App)" render-mode="ServerPrerendered" />
18+
19+
<div id="blazor-error-ui">
20+
<environment include="Staging,Production">
21+
An error has occurred. This application may no longer respond until reloaded.
22+
</environment>
23+
<environment include="Development">
24+
An unhandled exception has occurred. See browser dev tools for details.
25+
</environment>
26+
<a href class="reload">Reload</a>
27+
<a class="dismiss">🗙</a>
28+
</div>
29+
30+
<script src="_framework/blazor.server.js"></script>
31+
<script src="_content/Telerik.UI.for.Blazor/js/telerik-blazor.js" defer></script>
32+
</body>
33+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+

2+
using Microsoft.AspNetCore;
3+
using Microsoft.AspNetCore.Hosting;
4+
using Microsoft.Extensions.Configuration;
5+
using Microsoft.Extensions.Hosting;
6+
using Microsoft.Extensions.Logging;
7+
using System;
8+
using System.Collections.Generic;
9+
using System.IO;
10+
using System.Linq;
11+
using System.Threading.Tasks;
12+
13+
namespace fileselect_in_grid_row
14+
{
15+
public class Program
16+
{
17+
public static void Main(string[] args)
18+
{
19+
CreateHostBuilder(args).Build().Run();
20+
}
21+
22+
public static IHostBuilder CreateHostBuilder(string[] args) =>
23+
Host.CreateDefaultBuilder(args)
24+
.ConfigureWebHostDefaults(webBuilder =>
25+
{
26+
webBuilder.UseStaticWebAssets();
27+
webBuilder.UseStartup<Startup>();
28+
});
29+
}
30+
}
31+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"iisSettings": {
3+
"windowsAuthentication": false,
4+
"anonymousAuthentication": true,
5+
"iisExpress": {
6+
"applicationUrl": "http://localhost:55276/",
7+
"sslPort": 44398
8+
}
9+
},
10+
"profiles": {
11+
"IIS Express": {
12+
"commandName": "IISExpress",
13+
"launchBrowser": true,
14+
"environmentVariables": {
15+
"ASPNETCORE_ENVIRONMENT": "Development"
16+
}
17+
},
18+
"fileselect_in_grid_row": {
19+
"commandName": "Project",
20+
"launchBrowser": true,
21+
"environmentVariables": {
22+
"ASPNETCORE_ENVIRONMENT": "Development"
23+
},
24+
"applicationUrl": "https://localhost:5001;http://localhost:5000"
25+
}
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
@layout TelerikLayout
2+
3+
@inherits LayoutComponentBase
4+
5+
<div class="page">
6+
<div class="sidebar">
7+
<NavMenu />
8+
</div>
9+
10+
<div class="main">
11+
<div class="top-row px-4">
12+
<a href="https://docs.microsoft.com/en-us/aspnet/" target="_blank">About</a>
13+
</div>
14+
15+
<div class="content px-4">
16+
@Body
17+
</div>
18+
</div>
19+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<div class="top-row pl-4 navbar navbar-dark">
2+
<a class="navbar-brand" href="">fileselect_in_grid_row</a>
3+
<button class="navbar-toggler" @onclick="ToggleNavMenu">
4+
<span class="navbar-toggler-icon"></span>
5+
</button>
6+
</div>
7+
8+
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
9+
<ul class="nav flex-column">
10+
<li class="nav-item px-3">
11+
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
12+
<span class="oi oi-home" aria-hidden="true"></span> Home
13+
</NavLink>
14+
</li>
15+
</ul>
16+
</div>
17+
18+
@code {
19+
bool collapseNavMenu = true;
20+
21+
string NavMenuCssClass => collapseNavMenu ? "collapse" : null;
22+
23+
void ToggleNavMenu()
24+
{
25+
collapseNavMenu = !collapseNavMenu;
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@inherits LayoutComponentBase
2+
3+
<TelerikRootComponent>
4+
@Body
5+
</TelerikRootComponent>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using Microsoft.AspNetCore.Builder;
2+
using Microsoft.AspNetCore.Components;
3+
using Microsoft.AspNetCore.Hosting;
4+
using Microsoft.AspNetCore.HttpsPolicy;
5+
using Microsoft.AspNetCore.SignalR;
6+
using Microsoft.Extensions.Configuration;
7+
using Microsoft.Extensions.DependencyInjection;
8+
using Microsoft.Extensions.Hosting;
9+
using System;
10+
using System.Collections.Generic;
11+
using System.Linq;
12+
using System.Threading.Tasks;
13+
14+
namespace fileselect_in_grid_row
15+
{
16+
public class Startup
17+
{
18+
public Startup(IConfiguration configuration)
19+
{
20+
Configuration = configuration;
21+
}
22+
23+
public IConfiguration Configuration { get; }
24+
25+
// This method gets called by the runtime. Use this method to add services to the container.
26+
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
27+
public void ConfigureServices(IServiceCollection services)
28+
{
29+
services.AddRazorPages();
30+
services.AddServerSideBlazor();
31+
services.AddTelerikBlazor();
32+
33+
// SignalR message size for FileSelect
34+
services.Configure<HubOptions>(options =>
35+
{
36+
options.MaximumReceiveMessageSize = 1024 * 1024; // 1MB
37+
});
38+
}
39+
40+
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
41+
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
42+
{
43+
if (env.IsDevelopment())
44+
{
45+
app.UseDeveloperExceptionPage();
46+
}
47+
else
48+
{
49+
app.UseExceptionHandler("/Error");
50+
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
51+
app.UseHsts();
52+
}
53+
54+
app.UseHttpsRedirection();
55+
app.UseStaticFiles();
56+
57+
app.UseRouting();
58+
59+
app.UseEndpoints(endpoints =>
60+
{
61+
endpoints.MapDefaultControllerRoute();
62+
endpoints.MapControllers();
63+
64+
endpoints.MapBlazorHub();
65+
endpoints.MapFallbackToPage("/_Host");
66+
});
67+
}
68+
}
69+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
@using System.Net.Http
3+
@using Microsoft.AspNetCore.Authorization
4+
@using Microsoft.AspNetCore.Components.Forms
5+
@using Microsoft.AspNetCore.Components.Routing
6+
@using Microsoft.AspNetCore.Components.Authorization
7+
@using Microsoft.AspNetCore.Components.Web
8+
@using Microsoft.JSInterop
9+
@using fileselect_in_grid_row
10+
@using fileselect_in_grid_row.Shared
11+
@using Telerik.Blazor
12+
@using Telerik.Blazor.Components
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Debug",
5+
"System": "Information",
6+
"Microsoft": "Information"
7+
}
8+
}
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft": "Warning",
6+
"Microsoft.Hosting.Lifetime": "Information"
7+
}
8+
},
9+
"AllowedHosts": "*"
10+
}

0 commit comments

Comments
 (0)