Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable interactive render modes #348

Merged
merged 4 commits into from
Jan 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions paket.dependencies
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ nuget FSharp.Core >= 6.0 content: none
nuget Elmish >= 4.0.1 < 5.0
nuget Microsoft.AspNetCore.Components.WebAssembly >= 8.0.0
nuget Microsoft.JSInterop.WebAssembly >= 8.0.0
nuget Microsoft.AspNetCore.Components >= 8.0.0
nuget Microsoft.AspNetCore.Components.Web >= 8.0.0
nuget Microsoft.Extensions.Http >= 8.0.0
nuget FSharp.SystemTextJson >= 0.19.13
Expand Down
16 changes: 15 additions & 1 deletion src/Bolero.Server/Extensions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,9 @@ type ServerComponentsExtensions =
static member RenderBoleroScript(html: IHtmlHelper, config: IBoleroHostConfig) =
html.Raw(BoleroHostConfig.Body(config))

/// <summary>Configure the hosting of server-side and WebAssembly Bolero components.</summary>
/// <summary>
/// Configure the hosting of server-side and WebAssembly Bolero components using Bolero's legacy render mode handling.
/// </summary>
/// <param name="server">If true, use server-side Bolero; if false, use WebAssembly. Default is false.</param>
/// <param name="prerendered">If true, prerender the initial view in the served HTML. Default is true.</param>
/// <param name="devToggle">
Expand All @@ -111,9 +113,21 @@ type ServerComponentsExtensions =
else
this.AddSingleton(
{ new IBoleroHostConfig with
member _.IsInteractiveRender = false
member _.IsServer = server
member _.IsPrerendered = prerendered })

/// <summary>
/// Configure the hosting of Bolero components using interactive render modes.
/// </summary>
[<Extension>]
static member AddBoleroComponents(this: IServiceCollection) =
this.AddSingleton(
{ new IBoleroHostConfig with
member _.IsInteractiveRender = true
member _.IsServer = false
member _.IsPrerendered = false })

/// <summary>
/// Adds a route endpoint that will match requests for non-file-names with the lowest possible priority.
/// The request will be routed to a Bolero page.
Expand Down
24 changes: 20 additions & 4 deletions src/Bolero.Server/HostConfig.fs
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,24 @@ open Microsoft.AspNetCore.Http
open Microsoft.Extensions.Hosting

/// <summary>
/// The Bolero hosting configuration set by <see cref="M:Bolero.Server.ServerComponentsExtensions.AddBoleroHost" />.
/// The Bolero hosting configuration set by <see cref="M:Bolero.Server.ServerComponentsExtensions.AddBoleroHost" />
/// or <see cref="M:Bolero.Server.ServerComponentsExtensions.AddBoleroComponents" />.
/// </summary>
type IBoleroHostConfig =
/// <summary>If true, use server-side Bolero; if false, use WebAssembly.</summary>
/// <summary>
/// If true, use Bolero with interactive render modes, and bypass <see cref="P:IsServer"/> and <see cref="P:IsPrerendered"/>.
/// If false, use Bolero's legacy render mode handling.
/// </summary>
abstract IsInteractiveRender: bool
/// <summary>
/// If true, use server-side Bolero; if false, use WebAssembly.
/// Only applies if <see cref="IsInteractiveRender"/> is false.
/// </summary>
abstract IsServer: bool
/// <summary>If true, prerender the initial view in the served HTML.</summary>
/// <summary>
/// If true, prerender the initial view in the served HTML.
/// Only applies if <see cref="IsInteractiveRender"/> is false.
/// </summary>
abstract IsPrerendered: bool

/// <exclude />
Expand All @@ -56,7 +68,11 @@ type BoleroHostConfig(baseConfig: IBoleroHostBaseConfig, env: IHostEnvironment,
interface IBoleroHostConfig with
member this.IsServer = this.IsServer
member this.IsPrerendered = this.IsPrerendered
member this.IsInteractiveRender = false

static member internal Body(config: IBoleroHostConfig) =
let k = if config.IsServer then "server" else "webassembly"
let k =
if config.IsInteractiveRender then "web"
elif config.IsServer then "server"
else "webassembly"
$"""<script src="_framework/blazor.{k}.js"></script>"""
19 changes: 19 additions & 0 deletions src/Bolero.Server/Html.fs
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,22 @@ module Html =
/// preceded by the standard "html" doctype declaration.
/// </summary>
let doctypeHtml = DoctypeHtmlBuilder()

#if NET8_0_OR_GREATER
/// HTML attributes.
module attr =

/// <summary>
/// Define the render mode to use for this component.
/// Must be used on a component in a server-side page that uses interactive render modes.
/// </summary>
/// <param name="mode">
/// The render mode.
/// Usually one of the modes defined in <see cref="T:Microsoft.AspNetCore.Components.Web.RenderMode"/>.
/// </param>
/// <seealso cref="M:Bolero.Server.ServerComponentsExtensions.AddBoleroComponents(Microsoft.Extensions.DependencyInjection.IServiceCollection)"/>
let renderMode (mode: IComponentRenderMode) =
Attr(fun _ b i ->
b.AddComponentRenderMode(mode)
i)
#endif
1 change: 1 addition & 0 deletions src/Bolero/Bolero.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<Compile Include="Router.fs" />
<Compile Include="ProgramRun.fs" />
<Compile Include="Components.fs" />
<Compile Include="RenderMode.fs" />
<Compile Include="Attr.fs" />
<Compile Include="Node.fs" />
<Compile Include="Virtualize.fs" />
Expand Down
42 changes: 42 additions & 0 deletions src/Bolero/RenderMode.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
namespace Bolero
#if NET8_0_OR_GREATER

open System.Runtime.InteropServices
open Microsoft.AspNetCore.Components
open Microsoft.AspNetCore.Components.Web

type BoleroRenderMode =
/// <summary>
/// Render the component and run its interactivity on the server side.
/// </summary>
| Server = 1
/// <summary>
/// Render the component and run its interactivity on the client side.
/// </summary>
| WebAssembly = 2
/// <summary>
/// Automatically decide where to render the component and run its interactivity.
/// </summary>
| Auto = 3

/// <summary>
/// Define how a component is rendered in interactive render mode.
/// </summary>
type BoleroRenderModeAttribute
/// <summary>
/// Define how a component is rendered in interactive render mode.
/// </summary>
/// <param name="mode">The render mode.</param>
/// <param name="prerender">Whether to prerender the component in the HTML response.</param>
(mode, [<Optional; DefaultParameterValue true>] prerender: bool) =
inherit RenderModeAttribute()

/// <inheritdoc />
override val Mode : IComponentRenderMode =
match mode with
| BoleroRenderMode.Server -> InteractiveServerRenderMode(prerender)
| BoleroRenderMode.WebAssembly -> InteractiveWebAssemblyRenderMode(prerender)
| BoleroRenderMode.Auto -> InteractiveAutoRenderMode(prerender)
| _ -> failwith $"Invalid InteractiveRenderMode: {mode}"

#endif
1 change: 1 addition & 0 deletions src/Bolero/paket.references
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ FSharp.SystemTextJson

group net8
Elmish
Microsoft.AspNetCore.Components
Microsoft.AspNetCore.Components.WebAssembly
Microsoft.JSInterop.WebAssembly
Microsoft.Extensions.Http
Expand Down
26 changes: 19 additions & 7 deletions tests/Remoting.Server/Startup.fs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ open Bolero.Server
open FSharp.SystemTextJson.Swagger

module Page =
open Microsoft.AspNetCore.Components
open Microsoft.AspNetCore.Components.Web
open Bolero.Html
open Bolero.Server.Html

Expand All @@ -45,12 +47,17 @@ module Page =
``base`` { attr.href "/" }
}
body {
div { attr.id "main"; comp<MyApp> }
div { attr.id "main"; comp<MyApp> { attr.renderMode RenderMode.InteractiveAuto } }
script { attr.src "_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js" }
boleroScript
}
}

[<Route "/{*path}">]
type Page() =
inherit Bolero.Component()
override _.Render() = index

type MyApiHandler(log: ILogger<MyApiHandler>, ctx: IRemoteContext) =
inherit RemoteHandler<MyApi>()

Expand Down Expand Up @@ -91,14 +98,16 @@ type MyApiHandler(log: ILogger<MyApiHandler>, ctx: IRemoteContext) =
type Startup() =

member this.ConfigureServices(services: IServiceCollection) =
services.AddMvc() |> ignore
services.AddRazorComponents()
.AddInteractiveServerComponents()
.AddInteractiveWebAssemblyComponents()
|> ignore
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie()
|> ignore
services
.AddBoleroRemoting<MyApiHandler>()
.AddBoleroHost()
.AddServerSideBlazor()
.AddBoleroComponents()
|> ignore
services.AddSwaggerForSystemTextJson(JsonFSharpOptions()) |> ignore
services.AddEndpointsApiExplorer() |> ignore
Expand All @@ -110,13 +119,16 @@ type Startup() =
.UseSwaggerUI()
.UseRouting()
.UseAuthorization()
.UseBlazorFrameworkFiles()
.UseAntiforgery()
.UseEndpoints(fun endpoints ->
endpoints.MapBlazorHub() |> ignore
endpoints.MapBoleroRemoting()
.WithOpenApi()
|> ignore
endpoints.MapFallbackToBolero(Page.index) |> ignore)
endpoints.MapRazorComponents<Page.Page>()
.AddInteractiveServerRenderMode()
.AddInteractiveWebAssemblyRenderMode()
.AddAdditionalAssemblies(typeof<MyApp>.Assembly)
|> ignore)
|> ignore

if env.IsDevelopment() then
Expand Down
Loading