├── .gitignore
├── BlazorOffsiteDemos.App
├── App.cshtml
├── BlazorOffsiteDemos.App.csproj
├── Pages
│ ├── CascadingValidation.cshtml
│ ├── Counter.cshtml
│ ├── CounterDisplay.cshtml
│ ├── EventsAndBinding.cshtml
│ ├── Index.cshtml
│ ├── Interop.cshtml
│ ├── OverrideLayout.cshtml
│ ├── Products.cshtml
│ ├── Routing.cshtml
│ ├── TemplatedComponents.cshtml
│ └── _ViewImports.cshtml
├── Program.cs
├── Properties
│ └── launchSettings.json
├── Services
│ ├── AuthState.cs
│ ├── DefaultProductsRepository.cs
│ └── IProductsRepository.cs
├── Shared
│ ├── CardLayout.cshtml
│ ├── MainLayout.cshtml
│ ├── NavMenu.cshtml
│ ├── UserDisplay.cshtml
│ └── Validation
│ │ ├── IValidator.cs
│ │ ├── RequiredValidator.cshtml
│ │ ├── ValidatingForm.cshtml
│ │ └── ValidatorBase.cshtml
├── Startup.cs
├── _ViewImports.cshtml
└── wwwroot
│ ├── card-images
│ ├── bitzer_maloney.jpg
│ ├── hairy_maclary.jpg
│ ├── scarface_claw.jpg
│ ├── schnitzel_von_krumm.jpg
│ └── slinky_malinki.jpg
│ ├── css
│ └── site.css
│ ├── index.html
│ ├── js
│ └── demos.js
│ └── material-components
│ ├── BlazorMaterialComponents.js
│ └── lib
│ ├── flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2
│ ├── icon.css
│ ├── material-components-web.js
│ └── material-components-web.min.css
├── BlazorOffsiteDemos.Server
├── BlazorOffsiteDemos.Server.csproj
├── Index.cshtml
├── Program.cs
├── Properties
│ └── launchSettings.json
└── Startup.cs
├── BlazorOffsiteDemos.sln
└── MaterialComponents
├── MaterialComponents.csproj
├── Mdc.cs
├── MdcButton.cshtml
├── MdcCheckbox.cshtml
├── MdcComponentBase.cs
├── MdcDialog.cshtml
├── MdcDrawer.cshtml
├── MdcLinearProgress.cshtml
├── MdcList.cshtml
├── MdcNavLink.cshtml
├── MdcSelect.cshtml
├── MdcSwitch.cshtml
├── MdcTextField.cshtml
├── MdcTopAppBar.cshtml
├── THIRD_PARTY_NOTICES.txt
└── _ViewImports.cshtml
/.gitignore:
--------------------------------------------------------------------------------
1 | .vs/
2 | bin/
3 | obj/
4 | node_modules/
5 | *.user
6 |
--------------------------------------------------------------------------------
/BlazorOffsiteDemos.App/App.cshtml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
--------------------------------------------------------------------------------
/BlazorOffsiteDemos.App/BlazorOffsiteDemos.App.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | dotnet
6 | blazor serve
7 | 7.3
8 |
9 |
10 | false
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/BlazorOffsiteDemos.App/Pages/CascadingValidation.cshtml:
--------------------------------------------------------------------------------
1 | @page "/cascading-validation"
2 |
3 |
Validating form
4 |
5 |
6 |
7 |
8 |
9 |
10 | kittenName)
12 | Message="What is the kitten called?" />
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | kittenBreed)
27 | Message="Look, we're asking you a question here, buddy." />
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | kittenAcceptsTerms)
38 | Message="All kittens must accept terms. No exceptions." />
39 |
40 |
41 |
42 | Submit
43 |
44 |
45 | @message
46 |
47 | @functions {
48 | string kittenName;
49 | string kittenBreed;
50 | bool kittenAcceptsTerms;
51 | string message;
52 |
53 | void Submit()
54 | {
55 | message = $"Would submit name: {kittenName}; breed: {kittenBreed}; accepts terms: {kittenAcceptsTerms}";
56 | StateHasChanged();
57 | }
58 |
59 | void InvalidSubmit()
60 | {
61 | message = null;
62 | StateHasChanged();
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/BlazorOffsiteDemos.App/Pages/Counter.cshtml:
--------------------------------------------------------------------------------
1 | @page "/counter"
2 | @implements IDisposable
3 | @using System.Threading
4 |
5 | Counter
6 |
7 |
8 |
9 |
10 | Click me
11 |
12 | Parent renders: @(++numRenders)
13 |
14 | @functions {
15 | int currentCount = 0;
16 | int numRenders = 0;
17 |
18 | void IncrementCount()
19 | {
20 | currentCount++;
21 | }
22 |
23 | IDisposable timer;
24 |
25 | protected override void OnInit()
26 | {
27 | timer = new Timer(_ => Invoke(StateHasChanged), null, 0, 1000);
28 | }
29 |
30 | public void Dispose()
31 | {
32 | timer.Dispose();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/BlazorOffsiteDemos.App/Pages/CounterDisplay.cshtml:
--------------------------------------------------------------------------------
1 |
2 | @Label: @Value
3 |
4 |
5 | (child renders: @(++numRenders))
6 |
7 |
8 |
9 | @functions {
10 | int numRenders;
11 |
12 | [Parameter] string Label { get; set; }
13 | [Parameter] int Value { get; set; }
14 | }
15 |
--------------------------------------------------------------------------------
/BlazorOffsiteDemos.App/Pages/EventsAndBinding.cshtml:
--------------------------------------------------------------------------------
1 | @page "/events-and-binding"
2 |
3 | Events and binding
4 |
5 |
6 |
7 | Hover here
8 |
9 | @string.Join(", ", log)
10 |
11 |
12 |
13 |
14 |
17 |
23 |
24 |
25 | You chose: @kittenBreed
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | Submit
34 |
35 |
36 | @message
37 |
38 |
39 | @functions {
40 | List log = new List();
41 | string kittenBreed;
42 | string kittenName;
43 | string message;
44 |
45 | void UpdateMessage()
46 | {
47 | message = $"Breed: {kittenBreed ?? "None"}; Name: {kittenName ?? "None"}";
48 | }
49 |
50 | async Task MouseOver()
51 | {
52 | log.Add("In");
53 |
54 | await Task.Delay(1000);
55 | log.Add("Delayed");
56 | }
57 |
58 | void MouseOut()
59 | {
60 | log.Add("Out");
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/BlazorOffsiteDemos.App/Pages/Index.cshtml:
--------------------------------------------------------------------------------
1 | @page "/"
2 |
3 | Hello, world!
4 |
5 |
6 | Welcome to your new web app.
7 |
8 |
--------------------------------------------------------------------------------
/BlazorOffsiteDemos.App/Pages/Interop.cshtml:
--------------------------------------------------------------------------------
1 | @page "/js-interop"
2 | @inject IJSRuntime jsRuntime
3 | JS interop
4 |
5 |
6 | Call prompt
7 | Result: @promptResult
8 |
9 |
10 | @functions {
11 | string promptResult;
12 |
13 | async Task DoPrompt()
14 | {
15 | promptResult = await jsRuntime.InvokeAsync(
16 | "demoJsFuncs.showPrompt",
17 | "Tell us about your childhood",
18 | new DotNetObjectRef(this));
19 | }
20 |
21 | [JSInvokable(nameof(GetCurrentTime))]
22 | public async Task GetCurrentTime()
23 | {
24 | await Task.Delay(100); // Async is optional
25 | return DateTime.Now.ToLongTimeString();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/BlazorOffsiteDemos.App/Pages/OverrideLayout.cshtml:
--------------------------------------------------------------------------------
1 | @page "/override-layout"
2 | @layout MainLayout
3 |
4 |
5 | @foreach (var card in cards)
6 | {
7 |
8 |
9 |
10 |
11 |
@card.Title
12 |
13 |
@card.Subtitle
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
25 |
26 |
27 |
28 | }
29 |
30 |
31 | @functions {
32 | CardContents[] cards = new CardContents[]
33 | {
34 | new CardContents
35 | {
36 | Title = "Hairy MacLary",
37 | Subtitle = "... from Donaldson's Dairy",
38 | ImageUrl = "card-images/hairy_maclary.jpg"
39 | },
40 |
41 | new CardContents
42 | {
43 | Title = "Scarface Claw",
44 | Subtitle = "The toughest tom in town",
45 | ImageUrl = "card-images/scarface_claw.jpg"
46 | },
47 |
48 | new CardContents
49 | {
50 | Title = "Bitzer Maloney",
51 | Subtitle = "... all skinny and bony",
52 | ImageUrl = "card-images/bitzer_maloney.jpg"
53 | },
54 |
55 | new CardContents
56 | {
57 | Title = "Schnitzel Von Krumm",
58 | Subtitle = "... with a very low tum",
59 | ImageUrl = "card-images/schnitzel_von_krumm.jpg"
60 | },
61 |
62 | new CardContents
63 | {
64 | Title = "Slinky Malinki",
65 | Subtitle = "Rapscallion cat",
66 | ImageUrl = "card-images/slinky_malinki.jpg"
67 | }
68 | };
69 |
70 | class CardContents
71 | {
72 | public string Title { get; set; }
73 | public string Subtitle { get; set; }
74 | public string Description { get; set; }
75 | public string ImageUrl { get; set; }
76 | }
77 | }
--------------------------------------------------------------------------------
/BlazorOffsiteDemos.App/Pages/Products.cshtml:
--------------------------------------------------------------------------------
1 | @page "/products"
2 | @implements IDisposable
3 | @layout MainLayout
4 | @inject IProductsRepository ProductsRepository
5 |
6 | Products
8 |
14 |
15 |
16 | @if (displayedProducts == null)
17 | {
18 |
19 | Loading...
20 | }
21 | else
22 | {
23 |
24 | shop
25 | @product.DisplayName
26 | @product.Description
27 |
28 | { enableSave = true; }) />
32 |
33 |
34 | }
35 |
36 | @functions {
37 | IEnumerable displayedProducts;
38 | bool enableSave;
39 |
40 | protected override async Task OnInitAsync()
41 | {
42 | displayedProducts = await ProductsRepository.GetProductsAsync();
43 | ProductsRepository.StateChanged += OnRepositoryStateChanged;
44 | }
45 |
46 | async Task SaveChanges()
47 | {
48 | enableSave = false;
49 | await ProductsRepository.SaveChangesAsync();
50 | }
51 |
52 | void OnRepositoryStateChanged(object sender, EventArgs args)
53 | {
54 | Invoke(() =>
55 | {
56 | enableSave = false;
57 | StateHasChanged();
58 | });
59 | }
60 |
61 | public void Dispose()
62 | {
63 | ProductsRepository.StateChanged -= OnRepositoryStateChanged;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/BlazorOffsiteDemos.App/Pages/Routing.cshtml:
--------------------------------------------------------------------------------
1 | @page "/routing"
2 | @page "/routing/{id:int}"
3 | @page "/routing/{id:int}/{title}"
4 | @inject IUriHelper uriHelper
5 |
6 | Routing
7 |
8 | ID: @Id
9 | Title: @Title
10 | Absolute URL: @uriHelper.GetAbsoluteUri()
11 |
12 |
13 | - No params
14 | - With ID
15 | - With ID and Title
16 | - With ID and Title and query
17 |
18 |
19 |
22 |
23 | { await Task.Delay(1000); uriHelper.NavigateTo("counter"); })>
24 | Programmatic navigation
25 |
26 |
27 | @functions {
28 | [Parameter] int Id { get; set; }
29 | [Parameter] string Title { get; set; }
30 |
31 | // Workaround known issue (PR pending merge)
32 | public override Task SetParametersAsync(ParameterCollection parameters)
33 | {
34 | Id = default;
35 | Title = default;
36 | return base.SetParametersAsync(parameters);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/BlazorOffsiteDemos.App/Pages/TemplatedComponents.cshtml:
--------------------------------------------------------------------------------
1 | @page "/templated-components"
2 |
3 | Templated components
4 |
5 |
6 |
7 | Item @fakeProduct.DisplayName is in stock: @fakeProduct.IsInStock
8 |
9 |
10 | Cancel
11 | OK
12 |
13 |
14 |
15 | Show dialog
16 |
17 | @if (dialogResult != null)
18 | {
19 | Result: @dialogResult
20 | }
21 |
22 | @functions {
23 | MdcDialog myDialog;
24 | string dialogResult;
25 |
26 | static Product fakeProduct = new Product
27 | {
28 | ProductId = 1,
29 | DisplayName = "Cheesy Peas",
30 | Description = "If you like cheese, and you like peas, then you'll love cheesy peas.",
31 | IsInStock = true
32 | };
33 |
34 | async Task ShowDialog()
35 | {
36 | dialogResult = await myDialog.ShowAsync();
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/BlazorOffsiteDemos.App/Pages/_ViewImports.cshtml:
--------------------------------------------------------------------------------
1 | @layout CardLayout
2 |
--------------------------------------------------------------------------------
/BlazorOffsiteDemos.App/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Blazor.Hosting;
2 |
3 | namespace BlazorOffsiteDemos.App
4 | {
5 | public class Program
6 | {
7 | public static void Main(string[] args)
8 | {
9 | CreateHostBuilder(args).Build().Run();
10 | }
11 |
12 | public static IWebAssemblyHostBuilder CreateHostBuilder(string[] args) =>
13 | BlazorWebAssemblyHost.CreateDefaultBuilder()
14 | .UseBlazorStartup();
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/BlazorOffsiteDemos.App/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:58805/",
7 | "sslPort": 0
8 | }
9 | },
10 | "profiles": {
11 | "IIS Express": {
12 | "commandName": "IISExpress",
13 | "launchBrowser": true,
14 | "environmentVariables": {
15 | "ASPNETCORE_ENVIRONMENT": "Development"
16 | }
17 | },
18 | "BlazorOffsiteDemos.App": {
19 | "commandName": "Project",
20 | "launchBrowser": true,
21 | "environmentVariables": {
22 | "ASPNETCORE_ENVIRONMENT": "Development"
23 | },
24 | "applicationUrl": "http://localhost:58807/"
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/BlazorOffsiteDemos.App/Services/AuthState.cs:
--------------------------------------------------------------------------------
1 | namespace BlazorOffsiteDemos.App.Services
2 | {
3 | public class AuthState
4 | {
5 | public string UserName { get; set; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/BlazorOffsiteDemos.App/Services/DefaultProductsRepository.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 |
5 | namespace BlazorOffsiteDemos.App.Services
6 | {
7 | public class DefaultProductsRepository : IProductsRepository
8 | {
9 | private Product[] _products = new Product[]
10 | {
11 | new Product { ProductId = 1, DisplayName = "Curly-wurly straw", Description = "Amaze your friends with this cheap plastic tube", IsInStock = true },
12 | new Product { ProductId = 2, DisplayName = "Pepsi Connoisseur (12mo subscription)", Description = "Covers all developments in the world of Pepsi™", IsInStock = false },
13 | new Product { ProductId = 3, DisplayName = "Recarbonator Pro", Description = "Revitalize your old beverages in just hours", IsInStock = true },
14 | };
15 |
16 | public event EventHandler StateChanged;
17 |
18 | public async Task> GetProductsAsync()
19 | {
20 | await Task.Delay(2000);
21 | return _products;
22 | }
23 |
24 | public Task SaveChangesAsync()
25 | {
26 | StateChanged?.Invoke(this, null);
27 | return Task.CompletedTask;
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/BlazorOffsiteDemos.App/Services/IProductsRepository.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 |
5 | namespace BlazorOffsiteDemos.App.Services
6 | {
7 | public interface IProductsRepository
8 | {
9 | Task> GetProductsAsync();
10 |
11 | Task SaveChangesAsync();
12 |
13 | event EventHandler StateChanged;
14 | }
15 |
16 | public class Product
17 | {
18 | public int ProductId { get; set; }
19 | public string DisplayName { get; set; }
20 | public string Description { get; set; }
21 | public bool IsInStock { get; set; }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/BlazorOffsiteDemos.App/Shared/CardLayout.cshtml:
--------------------------------------------------------------------------------
1 | @inherits LayoutComponentBase
2 | @layout MainLayout
3 |
4 |
5 | @Body
6 |
--------------------------------------------------------------------------------
/BlazorOffsiteDemos.App/Shared/MainLayout.cshtml:
--------------------------------------------------------------------------------
1 | @inherits LayoutComponentBase
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | @if (authState == null)
12 | {
13 | Log in
14 | }
15 | else
16 | {
17 |
18 | Log out
19 | }
20 |
21 |
22 |
23 |
24 |
25 | @Body
26 |
27 |
28 |
29 |
30 |
31 | @functions {
32 | bool sidebarIsOpen;
33 |
34 | AuthState authState = new AuthState { UserName = "steve" };
35 |
36 | void ToggleSidebar()
37 | {
38 | sidebarIsOpen = !sidebarIsOpen;
39 | StateHasChanged();
40 | }
41 |
42 | void LogIn()
43 | {
44 | authState = new AuthState { UserName = "Someone else" };
45 | }
46 |
47 | void LogOut()
48 | {
49 | authState = null;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/BlazorOffsiteDemos.App/Shared/NavMenu.cshtml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | @if (CurrentAuthState != null)
6 | {
7 |
8 | }
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | @functions {
17 | [Parameter] Action OnItemClicked { get; set; }
18 | [CascadingParameter] AuthState CurrentAuthState { get; set; }
19 | }
20 |
--------------------------------------------------------------------------------
/BlazorOffsiteDemos.App/Shared/UserDisplay.cshtml:
--------------------------------------------------------------------------------
1 |
2 | account_circle
3 | @UserName
4 |
5 |
6 | @functions {
7 | [Parameter] string UserName { get; set; }
8 | }
--------------------------------------------------------------------------------
/BlazorOffsiteDemos.App/Shared/Validation/IValidator.cs:
--------------------------------------------------------------------------------
1 | namespace BlazorOffsiteDemos.App.Shared.Validation
2 | {
3 | public interface IValidator
4 | {
5 | bool Validate();
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/BlazorOffsiteDemos.App/Shared/Validation/RequiredValidator.cshtml:
--------------------------------------------------------------------------------
1 | @inherits ValidatorBase
2 | @functions {
3 | [Parameter] Func