I am facing an issue with blazor server in .NET 8.
I want to get into a global variable if the user is using a mobile browser in C#.
public class AppState
{
public bool isMobile {get;set;}
}
//In program.cs
builder.Services.AddScoped<AppState>();
And then somewhere in my JS:
window.myApp= {
isMobile: function () {
return ($(window).width() < 768);
}
}
//Code to run
appState.isMobile = await JSRuntime.InvokeAsync<bool>("myApp.isMobile");
My question:
Where can I load the JS to make the property isMobile available everywhere ?
So far I've tried, or suggest :
If I understand your problem correctly, you need to render to get the screen size before you can make some UI/UX decisions. Along the way you also need to handle pre-rendering.
The way to achieve this is to defer rendering of the normal content until the first render event has occurred, and a JS context exists.
Anyone please comment if I've missed something critical here.
First a modified JS script to return the screen width. I've moved the decision logic into the C# code:
wwwroot/app.js
window.myApp = {
getScreenWidth: function () {
return window.innerWidth;
}
}
Registered in App.razor
<body>
<Routes @rendermode="InteractiveServer" />
<script src="_framework/blazor.web.js"></script>
<script src="app.js"></script>
</body>
No change to the service or registation.
Next the critical component that gets the screen width and defers primary content rendering.
@if (_preRendering)
{
//Do whatever you want to inform the user that we are prerendering
<div class="alert alert-info m-5">Pre-Rendering</div>
}
// only render the content when we have the AppState data.
@if (_ModeIsSet)
{
@ChildContent
}
@code {
[Parameter] public RenderFragment? ChildContent { get; set; }
[CascadingParameter] private HttpContext? HttpContext { get; set; }
[Inject] private AppState _appState { get; set; } = default!;
[Inject] private IJSRuntime _js { get; set; } = default!;
private bool _ModeIsSet;
private bool _preRendering => HttpContext is not null;
protected async override Task OnInitializedAsync()
{
// Don't do anything when pre-rendering
if (_preRendering)
return;
// Yield so the component renders and we have a JS context to query
await Task.Delay(10);
var width = await _js.InvokeAsync<int>("myApp.getScreenWidth");
_appState.IsMobile = width < 768;
// Set so the normal content is rendered on the SetParametersAsync completion render
_ModeIsSet = true;
}
}
Finally modify Routes.razor
:
<MobileDetect>
<Router AppAssembly="typeof(Program).Assembly">
<Found Context="routeData">
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
<FocusOnNavigate RouteData="routeData" Selector="h1" />
</Found>
</Router>
</MobileDetect>
My Demo Page:
@page "/"
@inject AppState AppState
<PageTitle>Home</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
<div class="alert alert-primary">@this.AppState.IsMobile</div>