Skip to content

Instantly share code, notes, and snippets.

Created December 30, 2017 00:52
Show Gist options
  • 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
module Templating =
open WebSharper.UI.Templating
type MainTemplate = Template<"Main.html", ClientLoad.FromDocument, ServerLoad.WhenChanged>
let CitiesDropdown (ctx: Context<EndPoint>) =
let ( => ) (txt: string) act =
.Link(ctx.Link act)
"Home" => EndPoint.Home
"Cities" => EndPoint.SPA SPA.Cities
let Main ctx (title: string) (banner: Doc) (body: Doc list) =
.Menu(CitiesDropdown ctx)
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.Slice (function | SPA spa -> Some spa | _ -> None) EndPoint.SPA
|> Router.Install SPA.Cities
| SPA.Cities ->
store |> (fun (city, _) ->
.Link(router.Link (EndPoint.SPA (SPA.City city)))
| SPA.City city ->
let message =
match List.tryFind (fst >> (=) city) store with
| None ->
| Some (_, adjective) ->
.BackLink(router.Link (EndPoint.SPA SPA.Cities))
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 () @> ]
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">
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="" />
main > .container {
font-size: 200%;
<nav class="navbar is-transparent" role="navigation" aria-label="dropdown navigation">
<h3 class="navbar-item" style="font-weight:bold;">
<a href="">WebSharper</a>
<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 ws-replace="Banner">
<section class="hero is-info" ws-template="HomeBanner">
<div class="hero-body">
<p class="title">Home</p>
<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>
<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>
<div ws-template="CityPage">
<div ws-replace="Message">
<div ws-template="NotFound">
<p>I don't know your city :(</p>
<div ws-template="Found">
<p>Your city is <b>${Kind}</b></p>
<p>Click <a href="${BackLink}">here</a> to return.</p>
<footer class="footer">
<div class="container">
<div class="content has-text-centered">
For an enhanced template that provides automatic GitHub deployment to Azure, fork from <a href="">GitHub</a>, or
read more <a href="">here</a>.
<script ws-replace="scripts"></script>
<script type="text/javascript">
var dropdown = document.querySelector('.has-dropdown');
dropdown.addEventListener('click', function (event) {
<?xml version="1.0" encoding="utf-8"?>
<package id="System.ValueTuple" version="4.3.0" targetFramework="net461" />
<package id="WebSharper" version="" targetFramework="net461" />
<package id="WebSharper.FSharp" version="" targetFramework="net461" />
<package id="WebSharper.UI" version="" targetFramework="net461" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment