Skip to content

Commit 2e739c7

Browse files
authored
Merge pull request #6 from andrew-gaston/main
Add ServerOnly template
2 parents 573a648 + c3af680 commit 2e739c7

13 files changed

Lines changed: 347 additions & 0 deletions

File tree

templates/BlazorApp/BlazorApp/Components/Pages/From.fs renamed to templates/BlazorApp/BlazorApp/Components/Pages/Form.fs

File renamed without changes.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"$schema": "http://json.schemastore.org/template",
3+
"identity": "Fun.ServerOnly.App",
4+
"shortName": "fun-server-only",
5+
"name": "Fun.ServerOnly.App",
6+
"author": "slaveoftime",
7+
"classifications": [
8+
"Web",
9+
"F#",
10+
"blazor",
11+
"Fun.Blazor"
12+
],
13+
"type": {
14+
"language": "F#"
15+
},
16+
"tags": {
17+
"type": "project",
18+
"language": "F#"
19+
},
20+
"sourceName": "ServerOnlyApp",
21+
"preferNameDirectory": true
22+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
namespace ServerOnlyApp.Components
2+
3+
open Fun.Blazor
4+
open Microsoft.AspNetCore.Components.Web
5+
6+
7+
type App() =
8+
inherit FunComponent()
9+
10+
override _.Render() = fragment {
11+
doctype "html"
12+
html' {
13+
lang "EN"
14+
head {
15+
baseUrl "/"
16+
meta { charset "utf-8" }
17+
meta {
18+
name "viewport"
19+
content "width=device-width, initial-scale=1.0"
20+
}
21+
link {
22+
rel "icon"
23+
type' "image/png"
24+
href "favicon.png"
25+
}
26+
styleElt {
27+
ruleset ".active" {
28+
color "green"
29+
fontWeightBold
30+
}
31+
}
32+
HeadOutlet''
33+
}
34+
body {
35+
html.blazor<Routes> ()
36+
script { src "_framework/blazor.web.js" }
37+
}
38+
}
39+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
namespace ServerOnlyApp.Components.Layout
2+
3+
open Microsoft.AspNetCore.Components
4+
open Fun.Blazor
5+
6+
type MainLayout() as this =
7+
inherit LayoutComponentBase()
8+
9+
let content = div {
10+
NavMenu.Create()
11+
SectionOutlet'' { SectionName "header" }
12+
main { this.Body }
13+
}
14+
15+
override _.BuildRenderTree(builder) = content.Invoke(this, builder, 0) |> ignore
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
namespace ServerOnlyApp.Components.Layout
2+
3+
open Microsoft.AspNetCore.Components.Routing
4+
open Fun.Blazor
5+
6+
7+
[<AutoOpen>]
8+
module Extensions =
9+
open Fun.Blazor.Operators
10+
11+
type NavLink' with
12+
13+
[<CustomOperation "Href">]
14+
member inline _.Href([<InlineIfLambda>] render: AttrRenderFragment, url: string) = render ==> ("href" => url)
15+
16+
let NavLink'' = NavLink'()
17+
18+
19+
type NavMenu =
20+
static member Create() = nav {
21+
style {
22+
displayFlex
23+
alignItemsCenter
24+
gap 10
25+
}
26+
NavLink'' {
27+
Href ""
28+
Match NavLinkMatch.All
29+
"Home"
30+
}
31+
NavLink'' {
32+
Href "counter"
33+
"Counter"
34+
}
35+
NavLink'' {
36+
Href "form"
37+
"Form demo"
38+
}
39+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
namespace ServerOnlyApp.Components.Pages
2+
3+
open Microsoft.AspNetCore.Components
4+
open Microsoft.AspNetCore.Components.Web
5+
open Fun.Blazor
6+
7+
[<Route "/counter"; FunInteractiveServer>]
8+
type Counter() =
9+
inherit FunComponent()
10+
11+
let mutable count = 0
12+
13+
override _.Render() = fragment {
14+
PageTitle'' { "Counter" }
15+
SectionContent'' {
16+
SectionName "header"
17+
h1 { "Counter" }
18+
}
19+
p { $"Current count: {count}" }
20+
button {
21+
style { color "green" }
22+
onclick (fun _ -> count <- count + 1)
23+
"Click me"
24+
}
25+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
namespace ServerOnlyApp.Components.Pages
2+
3+
open System
4+
open System.Threading.Tasks
5+
open Microsoft.AspNetCore.Components
6+
open Microsoft.AspNetCore.Components.Web
7+
open Microsoft.AspNetCore.Components.Forms
8+
open Fun.Blazor
9+
10+
[<Route "/form"; FunInteractiveServer>]
11+
type Form() as this =
12+
inherit FunComponent()
13+
14+
let mutable isSubmitting = false
15+
16+
[<SupplyParameterFromForm>]
17+
member val Query: string = null with get, set
18+
19+
member _.Submit() = task {
20+
isSubmitting <- true
21+
this.StateHasChanged()
22+
23+
do! Task.Delay 1000
24+
isSubmitting <- false
25+
this.StateHasChanged()
26+
}
27+
28+
member _.FormView = form {
29+
onsubmit (ignore >> this.Submit)
30+
method "post"
31+
dataEnhance
32+
formName "person-info"
33+
html.blazor<AntiforgeryToken> ()
34+
input {
35+
type' InputTypes.text
36+
name (nameof this.Query)
37+
value this.Query
38+
}
39+
button {
40+
type' InputTypes.submit
41+
"Submit"
42+
}
43+
region {
44+
if String.IsNullOrEmpty this.Query || this.Query.Length > 5 then
45+
div {
46+
style { color "red" }
47+
$"{nameof this.Query} is not valid"
48+
}
49+
}
50+
}
51+
52+
override _.Render() = fragment {
53+
PageTitle'' { "Form demo" }
54+
SectionContent'' {
55+
SectionName "header"
56+
h1 { "Form demo" }
57+
}
58+
div {
59+
style { height "100vh" }
60+
a {
61+
href "form?#person-info"
62+
"check the form"
63+
}
64+
}
65+
h2 {
66+
id "person-info"
67+
"person info"
68+
}
69+
this.FormView
70+
region { if isSubmitting then progress.create () }
71+
div { style { height "100vh" } }
72+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
namespace ServerOnlyApp.Components.Pages
2+
3+
open System
4+
open System.Threading.Tasks
5+
open Microsoft.AspNetCore.Components
6+
open Microsoft.AspNetCore.Components.Web
7+
open Fun.Blazor
8+
9+
[<Route "/"; FunInteractiveServer>]
10+
type Home() as this =
11+
inherit FunComponent()
12+
13+
let mutable items = []
14+
15+
[<SupplyParameterFromQuery>]
16+
member val query = Nullable<int>() with get, set
17+
18+
member _.FilteredItems =
19+
if this.query.HasValue then
20+
items |> Seq.filter (fun x -> x > this.query.Value)
21+
else
22+
items
23+
24+
member _.MainContent = fragment {
25+
div {
26+
if this.query.HasValue then
27+
a {
28+
href "?query="
29+
"clear filter"
30+
}
31+
else
32+
a {
33+
style { color (if this.query.HasValue then "hotpink" else "grey") }
34+
href "?query=3"
35+
"filter: bigger than 3"
36+
}
37+
}
38+
ul {
39+
for i in this.FilteredItems do
40+
li {
41+
style { color "green" }
42+
$"item {i}"
43+
}
44+
}
45+
}
46+
47+
override _.OnInitializedAsync() = task {
48+
do! Task.Delay 1000
49+
items <- [ 1..5 ]
50+
this.StateHasChanged()
51+
52+
do! Task.Delay 1000
53+
items <- [ 1..10 ]
54+
this.StateHasChanged()
55+
}
56+
57+
override _.Render() = fragment {
58+
PageTitle'' { "Home" }
59+
SectionContent'' {
60+
SectionName "header"
61+
h1 { "Home" }
62+
}
63+
region { if items.IsEmpty then progress.create () else this.MainContent }
64+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
namespace ServerOnlyApp.Components
2+
3+
open System.Reflection
4+
open Fun.Blazor
5+
open ServerOnlyApp.Components.Layout
6+
7+
type Routes() =
8+
inherit FunComponent()
9+
10+
override _.Render() = Router'' {
11+
AppAssembly(Assembly.GetExecutingAssembly())
12+
Found(fun routeData -> RouteView'' {
13+
RouteData routeData
14+
DefaultLayout typeof<MainLayout>
15+
})
16+
}

templates/ServerOnlyApp/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
This project is trying to follow the default blazor official template
2+
3+
```bash
4+
dotnet run --project .\ServerOnlyApp\ServerOnlyApp.fsproj
5+
```

0 commit comments

Comments
 (0)