Skip to content

Commit

Permalink
File select in grid (#167)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
3 people authored Mar 29, 2022
1 parent 694ce4f commit 6cb70c5
Show file tree
Hide file tree
Showing 28 changed files with 1,356 additions and 0 deletions.
25 changes: 25 additions & 0 deletions fileselect/fileselect-in-grid-row/fileselect-in-grid-row.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31515.178
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "fileselect_in_grid_row", "fileselect-in-grid-row\fileselect_in_grid_row.csproj", "{16535648-C0EA-4911-888A-1A907EB9461C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{16535648-C0EA-4911-888A-1A907EB9461C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{16535648-C0EA-4911-888A-1A907EB9461C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{16535648-C0EA-4911-888A-1A907EB9461C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{16535648-C0EA-4911-888A-1A907EB9461C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {FE66770B-1980-45F9-926B-5E9AC70539F3}
EndGlobalSection
EndGlobal
12 changes: 12 additions & 0 deletions fileselect/fileselect-in-grid-row/fileselect-in-grid-row/App.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
@page "/"

@using System.IO
@using Microsoft.AspNetCore.Hosting
@using System.Threading
@using Telerik.Blazor.Components.FileSelect

@inject IWebHostEnvironment HostingEnvironment


NOTE: This snippet shows one example how to associate file from a FileSelect.
It does not implement full editing (e.g., you would typically implement this in an editor template
and add the necessary CRUD operations and services, and add UI to show the file list in "view" mode).
For brevity, this just shows the approach of using
a lambda expression to provide the necessary metadata to the code that will save the file.

<TelerikGrid Data="@MyData" Height="700px"
Pageable="true" PageSize="5" Sortable="true" Groupable="true"
FilterMode="@GridFilterMode.FilterRow"
Resizable="true" Reorderable="true">
<GridColumns>
<GridColumn Field="@(nameof(Employee.Id))" Width="120px" />
<GridColumn Field="@(nameof(Employee.Name))" Title="Employee Name" Groupable="false" />
<GridColumn Title="Associated Files (max 1MB / file)">
<Template>
@{
Employee empl = context as Employee;
// pass the current grid row in a lambda expression to the select handler to use when saving the files
<TelerikFileSelect OnSelect="@( (FileSelectEventArgs e) => SaveFilesWithMetadata(e, empl) )"
MaxFileSize="@(1024 * 1024)"/>
}
</Template>
</GridColumn>
<GridColumn Field="@(nameof(Employee.Team))" Title="Team" />
</GridColumns>
</TelerikGrid>

@code {
public IEnumerable<Employee> MyData = Enumerable.Range(1, 30).Select(x => new Employee
{
Id = x,
Name = "name " + x,
Team = "team " + x % 5
});

public Dictionary<string, CancellationTokenSource> Tokens { get; set; } = new Dictionary<string, CancellationTokenSource>();

private async Task SaveFilesWithMetadata(FileSelectEventArgs args, Employee empl)
{
int i = 0;
foreach (var file in args.Files)
{
if (!file.InvalidExtension)
{
// we will use the ID of the grid record to alter the way the file is saved
// implement the required businss logic instead
// in this example, we make a folder for each employee by ID, put files by index there and add a GUID for uniqueness
string folder = HostingEnvironment?.WebRootPath + Path.DirectorySeparatorChar + empl.Id;
string fileName = i.ToString() + "_" + Guid.NewGuid() + "_" + file.Name;
await SaveFileToDisk(file, folder, fileName);
i++;
// additionally, you may want to update the model to "know" about those files associated with it
// for example, by adding a file name to its list. This is not done here for brevity
// as how and what needs to be stored depends on the business logic
// so this example won't persist the files information
}
}
}

// this only saves the file to disk. Implement the business logic as needed and tweak this as needed for your case.
private async Task SaveFileToDisk(FileSelectFileInfo file, string folder, string fileName)
{
Tokens.Add(file.Id, new CancellationTokenSource());
if (!Directory.Exists(folder))
{
Directory.CreateDirectory(folder);
}
var path = Path.Combine(folder, fileName);
await using FileStream fs = new FileStream(path, FileMode.Create);
await file.Stream.CopyToAsync(fs, Tokens[file.Id].Token);
}


public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public List<string> Files { get; set; }
public string Team { get; set; }
public DateTime HireDate { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
@page "/"
@namespace fileselect_in_grid_row.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>fileselect_in_grid_row</title>
<base href="~/" />
<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
<link href="css/site.css" rel="stylesheet" />
<link href="_content/Telerik.UI.for.Blazor/css/kendo-theme-default/all.css" rel="stylesheet" />
</head>
<body>
<component type="typeof(App)" render-mode="ServerPrerendered" />

<div id="blazor-error-ui">
<environment include="Staging,Production">
An error has occurred. This application may no longer respond until reloaded.
</environment>
<environment include="Development">
An unhandled exception has occurred. See browser dev tools for details.
</environment>
<a href class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>

<script src="_framework/blazor.server.js"></script>
<script src="_content/Telerik.UI.for.Blazor/js/telerik-blazor.js" defer></script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace fileselect_in_grid_row
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStaticWebAssets();
webBuilder.UseStartup<Startup>();
});
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:55276/",
"sslPort": 44398
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"fileselect_in_grid_row": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:5001;http://localhost:5000"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
@layout TelerikLayout

@inherits LayoutComponentBase

<div class="page">
<div class="sidebar">
<NavMenu />
</div>

<div class="main">
<div class="top-row px-4">
<a href="https://docs.microsoft.com/en-us/aspnet/" target="_blank">About</a>
</div>

<div class="content px-4">
@Body
</div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<div class="top-row pl-4 navbar navbar-dark">
<a class="navbar-brand" href="">fileselect_in_grid_row</a>
<button class="navbar-toggler" @onclick="ToggleNavMenu">
<span class="navbar-toggler-icon"></span>
</button>
</div>

<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<ul class="nav flex-column">
<li class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</li>
</ul>
</div>

@code {
bool collapseNavMenu = true;

string NavMenuCssClass => collapseNavMenu ? "collapse" : null;

void ToggleNavMenu()
{
collapseNavMenu = !collapseNavMenu;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@inherits LayoutComponentBase

<TelerikRootComponent>
@Body
</TelerikRootComponent>
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace fileselect_in_grid_row
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}

public IConfiguration Configuration { get; }

// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddTelerikBlazor();

// SignalR message size for FileSelect
services.Configure<HubOptions>(options =>
{
options.MaximumReceiveMessageSize = 1024 * 1024; // 1MB
});
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapControllers();

endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

@using System.Net.Http
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.JSInterop
@using fileselect_in_grid_row
@using fileselect_in_grid_row.Shared
@using Telerik.Blazor
@using Telerik.Blazor.Components
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
Loading

0 comments on commit 6cb70c5

Please sign in to comment.