Skip to content

Instantly share code, notes, and snippets.

@granicz
Created December 30, 2017 00:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save granicz/9cd31128496fda6c0f6c228cce55d246 to your computer and use it in GitHub Desktop.
Save granicz/9cd31128496fda6c0f6c228cce55d246 to your computer and use it in GitHub Desktop.
Serving SPAs - templated version
namespace CSSPA
open WebSharper
open WebSharper.Sitelets
open WebSharper.UI
open WebSharper.UI.Server
open WebSharper.UI.Html
type SPA =
| [<EndPoint "cities">] Cities
| [<EndPoint "cities">] City of string
type EndPoint =
| [<EndPoint "/">] Home
| [<EndPoint "/spa">] SPA of SPA
[<AutoOpen>]
module Templating =
open WebSharper.UI.Templating
type MainTemplate = Template<"Main.html", ClientLoad.FromDocument, ServerLoad.WhenChanged>
let CitiesDropdown (ctx: Context<EndPoint>) =
let ( => ) (txt: string) act =
MainTemplate.MenuItem()
.Title(txt)
.Link(ctx.Link act)
.Doc()
[
"Home" => EndPoint.Home
"Cities" => EndPoint.SPA SPA.Cities
]
let Main ctx (title: string) (banner: Doc) (body: Doc list) =
Content.Page(
MainTemplate()
.Title(title)
.Menu(CitiesDropdown ctx)
.Banner(banner)
.Body(body)
.Doc()
)
[<JavaScript>]
module Client =
open WebSharper.UI.Client
let store =
[
"Budapest", "awesome"
"Paris", "famous"
"San Francisco", "expensive"
"London", "cosmopolitan"
"Singapore", "crowded"
]
let Main () =
let router = Router.Infer<EndPoint>()
let location =
router
|> Router.Slice (function | SPA spa -> Some spa | _ -> None) EndPoint.SPA
|> Router.Install SPA.Cities
location.View.Doc(function
| SPA.Cities ->
MainTemplate.CityLinks()
.Links(
store |> List.map (fun (city, _) ->
MainTemplate.CityLink()
.Link(router.Link (EndPoint.SPA (SPA.City city)))
.Title(city)
.Doc()
)
)
.Doc()
| SPA.City city ->
let message =
match List.tryFind (fst >> (=) city) store with
| None ->
MainTemplate.NotFound().Doc()
| Some (_, adjective) ->
MainTemplate.Found().Kind(adjective).Doc()
MainTemplate.CityPage()
.Name(city)
.Message(message)
.BackLink(router.Link (EndPoint.SPA SPA.Cities))
.Doc()
)
module Site =
let HomePage ctx =
Templating.Main ctx "Home" (MainTemplate.HomeBanner().Doc()) [
p [] [text "There is nothing here, select Cities from the menu."]
]
let CitiesPage ctx =
Templating.Main ctx "Cities" (MainTemplate.CitiesBanner().Doc()) [
div [] [client <@ Client.Main () @> ]
]
[<Website>]
let Main =
Application.MultiPage (fun ctx endpoint ->
match endpoint with
| EndPoint.Home ->
HomePage ctx
| EndPoint.SPA SPA.Cities
| EndPoint.SPA (SPA.City _) ->
CitiesPage ctx
)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>${Title}</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.1/css/bulma.min.css" />
<style>
main > .container {
font-size: 200%;
}
</style>
</head>
<body>
<nav class="navbar is-transparent" role="navigation" aria-label="dropdown navigation">
<h3 class="navbar-item" style="font-weight:bold;">
<a href="http://websharper.com">WebSharper</a>
</h3>
<div class="navbar-item has-dropdown">
<a class="navbar-link">Cities</a>
<div class="navbar-dropdown is-boxed" ws-hole="Menu">
<a ws-template="MenuItem" class="navbar-item" href="${Link}">${Title}</a>
</div>
</div>
</nav>
<div ws-replace="Banner">
<section class="hero is-info" ws-template="HomeBanner">
<div class="hero-body">
<p class="title">Home</p>
</div>
</section>
<section class="hero is-info" ws-template="CitiesBanner">
<div class="hero-body">
<p class="title">Cities</p>
<p class="subtitle">Pick your favorite city.</p>
</div>
</section>
</div>
<main class="hero-body">
<div class="container">
<div class="content">
<div ws-replace="Body">
<ul ws-template="CityLinks" ws-hole="Links">
<li ws-template="CityLink"><a href="${Link}">${Title}</a></li>
</ul>
<div ws-template="CityPage">
<h1>${Name}</h1>
<div ws-replace="Message">
<div ws-template="NotFound">
<p>I don't know your city :(</p>
</div>
<div ws-template="Found">
<p>Your city is <b>${Kind}</b></p>
</div>
</div>
<p>Click <a href="${BackLink}">here</a> to return.</p>
</div>
</div>
</div>
</div>
</main>
<footer class="footer">
<div class="container">
<div class="content has-text-centered">
<p>
For an enhanced template that provides automatic GitHub deployment to Azure, fork from <a href="https://github.com/intellifactory/ClientServer.Azure">GitHub</a>, or
read more <a href="http://websharper.com/blog-entry/4368/deploying-websharper-apps-to-azure-via-github">here</a>.
</p>
</div>
</div>
</footer>
<script ws-replace="scripts"></script>
<script type="text/javascript">
var dropdown = document.querySelector('.has-dropdown');
dropdown.addEventListener('click', function (event) {
event.stopPropagation();
dropdown.classList.toggle('is-active');
});
</script>
</body>
</html>
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="System.ValueTuple" version="4.3.0" targetFramework="net461" />
<package id="WebSharper" version="4.1.0.171" targetFramework="net461" />
<package id="WebSharper.FSharp" version="4.1.0.171" targetFramework="net461" />
<package id="WebSharper.UI" version="4.1.0.34" targetFramework="net461" />
</packages>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment