Skip to content

Commit b1107f9

Browse files
authored
Merge pull request #35146 from dotnet/main
2 parents 750f389 + 595f5c3 commit b1107f9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+397
-229
lines changed

aspnetcore/blazor/components/lifecycle.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -828,6 +828,80 @@ In the following example:
828828

829829
:::moniker-end
830830

831+
To display a loading indicator while the background work is taking place, use the following approach.
832+
833+
Create a loading indicator component with a `Loading` parameter that can display child content in a <xref:Microsoft.AspNetCore.Components.RenderFragment>. For the `Loading` parameter:
834+
835+
* When `true`, display a loading indicator.
836+
* When `false`, render the component's content (`ChildContent`). For more information, see [Child content render fragments](xref:blazor/components/index#child-content-render-fragments).
837+
838+
`ContentLoading.razor`:
839+
840+
```razor
841+
@if (Loading)
842+
{
843+
<progress id="loadingIndicator" aria-label="Content loading…"></progress>
844+
}
845+
else
846+
{
847+
@ChildContent
848+
}
849+
850+
@code {
851+
[Parameter]
852+
public RenderFragment? ChildContent { get; set; }
853+
854+
[Parameter]
855+
public bool Loading { get; set; }
856+
}
857+
```
858+
859+
:::moniker range=">= aspnetcore-6.0"
860+
861+
To load CSS styles for the indicator, add the styles to `<head>` content with the <xref:Microsoft.AspNetCore.Components.Web.HeadContent> component. For more information, see <xref:blazor/components/control-head-content>.
862+
863+
```razor
864+
@if (Loading)
865+
{
866+
<!-- OPTIONAL ...
867+
<HeadContent>
868+
<style>
869+
...
870+
</style>
871+
</HeadContent>
872+
-->
873+
<progress id="loadingIndicator" aria-label="Content loading…"></progress>
874+
}
875+
else
876+
{
877+
@ChildContent
878+
}
879+
880+
...
881+
```
882+
883+
:::moniker-end
884+
885+
Wrap the component's Razor markup with the `ContentLoading` component and pass a value in a C# field to the `Loading` parameter when initialization work is performed by the component:
886+
887+
```razor
888+
<ContentLoading Loading="@loading">
889+
...
890+
</ContentLoading>
891+
892+
@code {
893+
private bool loading = true;
894+
...
895+
896+
protected override async Task OnInitializedAsync()
897+
{
898+
await LongRunningWork().ContinueWith(_ => loading = false);
899+
}
900+
901+
...
902+
}
903+
```
904+
831905
## Blazor Server reconnection events
832906

833907
The component lifecycle events covered in this article operate separately from [server-side reconnection event handlers](xref:blazor/fundamentals/signalr#reflect-the-server-side-connection-state-in-the-ui). When the SignalR connection to the client is lost, only UI updates are interrupted. UI updates are resumed when the connection is re-established. For more information on circuit handler events and configuration, see <xref:blazor/fundamentals/signalr>.

aspnetcore/blazor/components/rendering.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,4 +326,4 @@ The state manager approach is similar to the earlier case with <xref:System.Time
326326
<!-- UPDATE 10.0 Will be removed for a new feature in this area.
327327
Tracked by: https://github.com/dotnet/aspnetcore/issues/49056 -->
328328

329-
A loading progress indicator isn't present in an app created from the Blazor Web App project template. A new loading progress indicator feature is planned for a future release of .NET. In the meantime, an app can adopt custom code to create a loading progress indicator. For more information, see <xref:blazor/fundamentals/startup#client-side-loading-progress-indicators>.
329+
A loading progress indicator isn't present in an app created from the Blazor Web App project template. A new loading progress indicator feature is planned for a future release of .NET. In the meantime, an app can adopt custom code to create a loading progress indicator. For more information, see <xref:blazor/fundamentals/startup#client-side-loading-indicators>.

aspnetcore/blazor/fundamentals/startup.md

Lines changed: 149 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,33 @@ For Blazor Web Apps:
127127
> </script>
128128
> ```
129129
130+
:::moniker-end
131+
132+
:::moniker range=">= aspnetcore-8.0 < aspnetcore-10.0"
133+
134+
Due to a [framework bug in .NET 8 and 9 (`dotnet/aspnetcore` #54049)](https://github.com/dotnet/aspnetcore/issues/54049), the Blazor script must be manually started when `beforeWebAssemblyStart(options, extensions)` or `afterWebAssemblyStarted(blazor)` are called. If the server app doesn't already start Blazor manually with a WebAssembly (`webAssembly: {...}`) configuration, update the `App` component in the server project with the following.
135+
136+
In `Components/App.razor`, remove the existing Blazor `<script>` tag:
137+
138+
```diff
139+
- <script src="_framework/blazor.web.js"></script>
140+
```
141+
142+
Replace the `<script>` tag with the following markup that starts Blazor manually with a WebAssembly (`webAssembly: {...}`) configuration:
143+
144+
```html
145+
<script src="_framework/blazor.web.js" autostart="false"></script>
146+
<script>
147+
Blazor.start({
148+
webAssembly: {}
149+
});
150+
</script>
151+
```
152+
153+
:::moniker-end
154+
155+
:::moniker range=">= aspnetcore-8.0"
156+
130157
For Blazor Server, Blazor WebAssembly, and Blazor Hybrid apps:
131158

132159
:::moniker-end
@@ -237,6 +264,7 @@ For examples of JS initializers, see the following resources:
237264

238265
:::moniker range=">= aspnetcore-8.0"
239266

267+
* [Blazor Web App loading indicator](#global-interactive-webassembly-rendering-without-prerendering) (*Global Interactive WebAssembly rendering without prerendering example*)
240268
* <xref:blazor/js-interop/ssr>
241269
* <xref:blazor/components/js-spa-frameworks#render-razor-components-from-javascript> (*`quoteContainer2` example*)
242270
* <xref:blazor/components/event-handling#custom-event-arguments> (*Custom clipboard paste event example*)
@@ -564,85 +592,50 @@ app.MapFallbackToFile("index.html", staticFileOptions);
564592

565593
:::moniker range=">= aspnetcore-7.0"
566594

567-
## Client-side loading progress indicators
595+
## Client-side loading indicators
568596

569-
A loading progress indicator shows the loading progress of the app to users, indicating that the app is loading normally and that the user should wait until loading is finished.
597+
A loading indicator shows that the app is loading normally and that the user should wait until loading is finished.
570598

571599
:::moniker-end
572600

573601
:::moniker range=">= aspnetcore-8.0"
574602

575-
### Blazor Web App loading progress
603+
### Blazor Web App loading indicator
576604

577-
The loading progress indicator used in Blazor WebAssembly apps isn't present in an app created from the Blazor Web App project template. Usually, a loading progress indicator isn't desirable for interactive WebAssembly components because Blazor Web Apps prerender client-side components on the server for fast initial load times. For mixed-render-mode situations, the framework or developer code must also be careful to avoid the following problems:
605+
The loading indicator used in Blazor WebAssembly apps isn't present in an app created from the Blazor Web App project template. Usually, a loading indicator isn't desirable for interactive WebAssembly components because Blazor Web Apps prerender client-side components on the server for fast initial load times. For mixed-render-mode situations, the framework or developer code must also be careful to avoid the following problems:
578606

579607
* Showing multiple loading indicators on the same rendered page.
580608
* Inadvertently discarding prerendered content while the .NET WebAssembly runtime is loading.
581609

582610
<!-- UPDATE 10.0 Will be removed for a new feature in this area.
583611
Tracked by: https://github.com/dotnet/aspnetcore/issues/49056 -->
584612

585-
A future release of .NET might provide a framework-based loading progress indicator. In the meantime, you can add a custom loading progress indicator to a Blazor Web App.
613+
A future release of .NET might provide a framework-based loading indicator. In the meantime, you can add a custom loading indicator to a Blazor Web App.
614+
615+
#### Per-component Interactive WebAssembly rendering with prerendering
616+
617+
*This scenario applies to per-component Interactive WebAssembly rendering (`@rendermode InteractiveWebAssembly` applied to individual components).*
586618

587-
Create a `LoadingProgress` component in the `.Client` app that calls <xref:System.OperatingSystem.IsBrowser%2A?displayProperty=nameWithType>:
619+
Create a `ContentLoading` component in the `Layout` folder of the `.Client` app that calls <xref:System.OperatingSystem.IsBrowser%2A?displayProperty=nameWithType>:
588620

589-
* When `false`, display a loading progress indicator while the Blazor bundle is downloaded and before the Blazor runtime activates on the client.
621+
* When `false`, display a loading indicator.
590622
* When `true`, render the requested component's content.
591623

592-
The following demonstration uses the loading progress indicator found in apps created from the Blazor WebAssembly template, including a modification of the styles that the template provides. The styles are loaded into the app's `<head>` content by the <xref:Microsoft.AspNetCore.Components.Web.HeadContent> component. For more information, see <xref:blazor/components/control-head-content>.
624+
To load CSS styles for the indicator, add the styles to `<head>` content with the <xref:Microsoft.AspNetCore.Components.Web.HeadContent> component. For more information, see <xref:blazor/components/control-head-content>.
593625

594-
`LoadingProgress.razor`:
626+
`Layout/ContentLoading.razor`:
595627

596628
```razor
597629
@if (!OperatingSystem.IsBrowser())
598630
{
631+
<!-- OPTIONAL ...
599632
<HeadContent>
600633
<style>
601-
.loading-progress {
602-
position: relative;
603-
display: block;
604-
width: 8rem;
605-
height: 8rem;
606-
margin: 20vh auto 1rem auto;
607-
}
608-
609-
.loading-progress circle {
610-
fill: none;
611-
stroke: #e0e0e0;
612-
stroke-width: 0.6rem;
613-
transform-origin: 50% 50%;
614-
transform: rotate(-90deg);
615-
}
616-
617-
.loading-progress circle:last-child {
618-
stroke: #1b6ec2;
619-
stroke-dasharray:
620-
calc(3.142 * var(--blazor-load-percentage, 0%) * 0.8),
621-
500%;
622-
transition: stroke-dasharray 0.05s ease-in-out;
623-
}
624-
625-
.loading-progress-text {
626-
position: relative;
627-
text-align: center;
628-
font-weight: bold;
629-
top: -90px;
630-
}
631-
632-
.loading-progress-text:after {
633-
content: var(--blazor-load-percentage-text, "Loading");
634-
}
635-
636-
code {
637-
color: #c02d76;
638-
}
634+
...
639635
</style>
640636
</HeadContent>
641-
<svg class="loading-progress">
642-
<circle r="40%" cx="50%" cy="50%" />
643-
<circle r="40%" cx="50%" cy="50%" />
644-
</svg>
645-
<div class="loading-progress-text"></div>
637+
-->
638+
<progress id="loadingIndicator" aria-label="Content loading…"></progress>
646639
}
647640
else
648641
{
@@ -655,7 +648,7 @@ else
655648
}
656649
```
657650

658-
In a component that adopts Interactive WebAssembly rendering, wrap the component's Razor markup with the `LoadingProgress` component. The following example demonstrates the approach with the `Counter` component of an app created from the Blazor Web App project template.
651+
In a component that adopts Interactive WebAssembly rendering, wrap the component's Razor markup with the `ContentLoading` component. The following example demonstrates the approach with the `Counter` component of an app created from the Blazor Web App project template.
659652

660653
`Pages/Counter.razor`:
661654

@@ -665,13 +658,13 @@ In a component that adopts Interactive WebAssembly rendering, wrap the component
665658
666659
<PageTitle>Counter</PageTitle>
667660
668-
<LoadingProgress>
661+
<ContentLoading>
669662
<h1>Counter</h1>
670663
671664
<p role="status">Current count: @currentCount</p>
672665
673666
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
674-
</LoadingProgress>
667+
</ContentLoading>
675668
676669
@code {
677670
private int currentCount = 0;
@@ -683,6 +676,107 @@ In a component that adopts Interactive WebAssembly rendering, wrap the component
683676
}
684677
```
685678

679+
#### Global Interactive WebAssembly rendering with prerendering
680+
681+
*This scenario applies to global Interactive WebAssembly rendering without prerendering (`@rendermode="InteractiveWebAssembly"` on the `HeadOutlet` and `Routes` components in the `App` component).*
682+
683+
Create a `ContentLoading` component in the `Layout` folder of the `.Client` app that calls <xref:Microsoft.AspNetCore.Components.RendererInfo.IsInteractive?displayProperty=nameWithType>:
684+
685+
* When `false`, display a loading indicator.
686+
* When `true`, render the requested component's content.
687+
688+
To load CSS styles for the indicator, add the styles to `<head>` content with the <xref:Microsoft.AspNetCore.Components.Web.HeadContent> component. For more information, see <xref:blazor/components/control-head-content>.
689+
690+
`Layout/ContentLoading.razor`:
691+
692+
```razor
693+
@if (!RendererInfo.IsInteractive)
694+
{
695+
<!-- OPTIONAL ...
696+
<HeadContent>
697+
<style>
698+
...
699+
</style>
700+
</HeadContent>
701+
-->
702+
<progress id="loadingIndicator" aria-label="Content loading…"></progress>
703+
}
704+
else
705+
{
706+
@ChildContent
707+
}
708+
709+
@code {
710+
[Parameter]
711+
public RenderFragment? ChildContent { get; set; }
712+
}
713+
```
714+
715+
In the `MainLayout` component (`Layout/MainLayout.razor`) of the `.Client` project, wrap the <xref:Microsoft.AspNetCore.Components.LayoutComponentBase.Body%2A> property (`@Body`) with the `ContentLoading` component:
716+
717+
In `Layout/MainLayout.razor`:
718+
719+
```diff
720+
+ <ContentLoading>
721+
@Body
722+
+ </ContentLoading>
723+
```
724+
725+
#### Global Interactive WebAssembly rendering without prerendering
726+
727+
*This scenario applies to global Interactive WebAssembly rendering without prerendering (`@rendermode="new InteractiveWebAssemblyRenderMode(prerender: false)"` on the `HeadOutlet` and `Routes` components in the `App` component).*
728+
729+
Add a [JavaScript initializer](#javascript-initializers) to the app. In the following JavaScript module file name example, the `{ASSEMBLY NAME}` placeholder is the assembly name of the server project (for example, `BlazorSample`). The `wwwroot` folder where the module is placed is the `wwwroot` folder in the server-side project, not the `.Client` project.
730+
731+
The following example uses a [`progress`](https://developer.mozilla.org/docs/Web/HTML/Element/progress) indicator that doesn't indicate the actual progress of [delivering client-side boot resources to the client](#load-client-side-boot-resources), but it serves as a general approach for further development if you want the progress indicator to show the actual progress of loading the app's boot resources.
732+
733+
`wwwroot/{ASSEMBLY NAME}.lib.module.js`:
734+
735+
```javascript
736+
export function beforeWebStart(options) {
737+
var progress = document.createElement("progress");
738+
progress.id = 'loadingIndicator';
739+
progress.ariaLabel = 'Blazor loading…';
740+
progress.style = 'position:absolute;top:50%;left:50%;margin-right:-50%;' +
741+
'transform:translate(-50%,-50%);';
742+
document.body.appendChild(progress);
743+
}
744+
745+
export function afterWebAssemblyStarted(blazor) {
746+
var progress = document.getElementById('loadingIndicator');
747+
progress.remove();
748+
}
749+
```
750+
751+
:::moniker-end
752+
753+
:::moniker range=">= aspnetcore-8.0 < aspnetcore-10.0"
754+
755+
Due to a [framework bug in .NET 8 and 9 (`dotnet/aspnetcore` #54049)](https://github.com/dotnet/aspnetcore/issues/54049), the Blazor script must be manually started. If the server app doesn't already start Blazor manually with a WebAssembly (`webAssembly: {...}`) configuration, update the `App` component in the server project with the following.
756+
757+
In `Components/App.razor`, remove the existing Blazor `<script>` tag:
758+
759+
```diff
760+
- <script src="_framework/blazor.web.js"></script>
761+
```
762+
763+
Replace the `<script>` tag with the following markup that starts Blazor manually with a WebAssembly (`webAssembly: {...}`) configuration:
764+
765+
```html
766+
<script src="_framework/blazor.web.js" autostart="false"></script>
767+
<script>
768+
Blazor.start({
769+
webAssembly: {}
770+
});
771+
</script>
772+
```
773+
774+
If you notice a short delay between the loading indicator removal and the first page render, you can guarantee removal of the indicator after rendering by calling for indicator removal in the [`OnAfterRenderAsync` lifecycle method](xref:blazor/components/lifecycle#after-component-render-onafterrenderasync) of either the `MainLayout` or `Routes` components. For more information and a code example, see [Document an approach for a loading indicator that works with global Interactive WebAssembly without prerendering (`dotnet/AspNetCore.Docs` #35111)](https://github.com/dotnet/AspNetCore.Docs/issues/35111#issuecomment-2778796998).
775+
776+
:::moniker-end
777+
778+
:::moniker range=">= aspnetcore-8.0"
779+
686780
### Blazor WebAssembly app loading progress
687781

688782
:::moniker-end

0 commit comments

Comments
 (0)