Hi Derek, this is actually quite easy to do. First, have a look at one of the standard templates - the client-server app template:
1
dotnet new websharper-web -lang f# -n MyProject
Here, you can see that a master template (main.html
) is used (note the type provider call near the top of the file: type MainTemplate = Templating.Template<"Main.html">
), into which content is added when requested at different endpoints (Site.fs
).
If you don't want to inline your HTML content in F# code, you can put it into an HTML file (or main.html
), mark the relevant parts as templates (with ws-template
or ws-children-template
), and bring them in the same way.
I recently wrote a blog article about First steps: Using HTML templates, accessing form values, and wiring events, you might find this useful too, especially if you are looking to develop SPAs.
I hope this helps.
Hi Adam,
I think I have worked it out.
I now have
1 2
type MainTemplate = Templating.Template<"Main.html"> type LoginTemplate = Template<"Login.html">
... let LoginWidget = LoginTemplate.Login().Doc()
1 2 3 4 5
let LoginPage ctx = Templating.Main ctx EndPoint.Login "Login" [ h1 [] [text "Login"] Templating.LoginWidget ]
and that gets the content of the login html into the body of main.
Is this the correct approach? I am yet to try to wire up the login fields and button actions :-)
should I put the code for the login.html in it's own login.fs file (is this the recommended way?)
and is this use of main.html as a master page the way you would recommend structuring a multi-page app?
thanks so much Derek
Yep, it works pretty much like that. You will find that you rarely have just static content to plug in, and more likely you will want to take some arguments to your widgets, which in turn will instantiate the inner templates before sealing them off with .Doc()
.
As for code organization, use whatever works best - for simple inner templates, you might just use a single .fs
file, for widgets/templates involving more complex logic it might make more sense to break out into separate files.
And finally, what you have is precisely how you would implement multi-page apps whose pages are based on the same template.
Thanks again Adam, sorry this is a long message. I am making progress, though. I removed the outer div:
1 2 3
<div ws-template="Login" class="login-wrapper"> // login html </div>
as with that in place
1 2
let LoginWidget () = LoginTemplate.Login().Doc()
gives an error saying "method or object constructor 'Login' is not static." I did not get that error when there were no ws-var in the template.
Changing it to
1 2
let LoginWidget () = LoginTemplate().Doc()
gets rid of that error (but then I might as well remove the ws-templete="Login") no? Or am I misunderstanding something here. Still this is not my main issue.
I am using the login example page from the SPA example login app and applying that to my client server app. Unfortunately, this SPA example does not demo calling a server-side function.
My main issue at the moment, is though the click handler on the client side is called, (I see the console logs that I print) I cannot seem to get the server side method to be called.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
[<JavaScript>] module Client = ... type LoginTemplate = Template<"Login.html"> // this gets subs into the main.html body.* let LoginWidget () = LoginTemplate() ... .Login(fun e -> passwordValid := not (String.IsNullOrWhiteSpace e.Vars.Password.Value) emailValid := not (String.IsNullOrWhiteSpace e.Vars.Email.Value) if passwordValid.Value && emailValid.Value then Console.Log(sprintf "Your email is %s" e.Vars.Email.Value) // I see this message in browse console. Server.LoginClicked (e.Vars.Email.Value) |> ignore // I have tried this async { // i have tried and this (Server.LoginClicked e.Vars.Email.Value) |> ignore } |> ignore else Console.Log("invalid input") e.Event.PreventDefault()
// * putting login html into main html
1 2 3 4 5
let LoginPage ctx = Templating.Main ctx EndPoint.Login "Login" [ h1 [] [text "Login"] div [] [client <@ Client.LoginWidget() @>] ]
and the server LoginClicked method looks like this (I have also added break points but they do not get triggered):
1 2 3 4 5 6 7 8 9
module Server = ... [<Rpc>] let LoginClicked (input: string) = File.WriteAllText("c:\\temp.txt", input) // file does not get writen to Debug.Print("about to loggin: " + input) // not sure where this async { return input + " logged in" }
And I have tried qutie a few variants of that, but nothing happens, no file is written to, no break points trigger. etc. Basically, I cannot get the server side function to be called. I am clearly missing something.
Also the call to e.Event.PreventDefault()
seems to stop the nice tooltip warning about the email missing @, if I remove that call I get the tool-tip.
How do I access the JS validation in the Click handler so I can check if that is valid or not before trying to call the server side function, that way I guess I can also decide not to call PreventDefault
and thus still get the tool-tip message.
Oh! And what is the difference between .Doc()
and .Bind()
? I noticed in the SPA example it used .Bind()
, but I get and error when I change Doc()
to Bind()
in my client-server app.
I have also tried code like this in my Login click handler (to try to mimic the SPA example):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
let Login = Var.Create "" // Login is the name of the Button in the login.html file let submit = Submitter.CreateOption Login.View let vLogin = submit.View.MapAsync(function | None -> async { return "" } | Some input -> Server.LoginClicked input ) .... .Login(fun e -> ... Console.Log("server click") (Server.LoginClicked e.Vars.Email.Value) |> ignore Console.Log("vLogin click") vLogin |> ignore Console.Log("submit click") submit.Trigger |> ignore Console.Log("async submit click") async { submit.Trigger |> ignore } |> ignore Console.Log("check") ... )
and though all the console logs get displayed, none of the attempts to trigger the server side method are working.
I am admitably very new to F# and compeletely new to WebSharper, and I do thank you for your help so far.
What would be really handy is an example app that contained a bit more detail about the complete page life cycle. E.g. using templates (as this is better than embedded html), having fields and buttons on the page, "with the holes wired up," doing some client side checks on data before calling server method, submitting data to a server-side function (e.g. email and password), handling success and error case (error case being when function not successfully called, e.g. due to timeout so user can be notified of problem), displaying data returned from server side function in the browser, directing user to new page (in case of multiple page app), e.g. successful login page with simple welcome message "Hi <user> you are logged in."
This would give a more complete picture of the pluming required for a complete round trip.
At present it is really hard to find all this info and like I say I am having a real problem tring to pull bits together and get a really simple scenario to work.
I note the example Client Server app does show a server side method being called and a response to the client, but that is using in place HTML and not templates and though that bit is working in my sample app, I am having a real problem trying to work out what needs to change to get my .Login click handler to call the server side code.
Once again sorry for such a long message.
Cheers, Derek
Hi Derek, I had a quick look and I think I know what you are missing. First, your RPC function is fine - Server.LoginClicked
takes a string and returns Async<string>
. However, the way you are trying to call it is not. Consider what you tried:
Server.LoginClicked (e.Vars.Email.Value) |> ignore
- This creates an Async value but throws it away immediately. 2.`fsharp async { (Server.LoginClicked e.Vars.Email.Value) |> ignore } |> ignore
` This does the same, but asyncronously - so again, nothing is evaluated.
What you need instead is explicitly evaluating/starting the async function:
1 2 3 4 5
async { let! res = Server.LoginClicked e.Vars.Email.Value // do something with res return () } |> Async.StartImmediate
However, that's not all. The SPA project template (as opposed to the client-server one - websharper-web
) by default is not set up to serve RPC functions, which is something we should really change and will do so in the next release. In order to enable this, add WebSharper.AspNetCore
to your project references, open WebSharper.AspNetCore
, and add this line to Startup.fs
to prepare the server to respond to WebSharper RPC functions:
1 2 3 4 5 6 7
member this.Configure(app: IApplicationBuilder, env: IHostingEnvironment) = ... app.UseDefaultFiles() .UseStaticFiles() .UseWebSharper(env) // Add this line .Run(fun context -> ...
Hope this helps.
Thanks Adam,
Changing the way the on LoginClicked method was called did the trick.
I am using the client-server app so I did not need the SPA config stuff. Though that is good to know.
Thanks again Derek
Hi Adam, thanks for that.
I had actually looked at that example, but what I cannot figure out is if I have something like
1 2
type MainTemplate = Templating.Template<"Main.html"> type LoginTemplate = Template<"Login.html">
how do I insert the login template into the body of the main template?
So I need to be able to somehow get the "login" stuff into
1 2 3 4 5 6 7 8
let Main ctx action (title: string) (body: Doc list) = Content.Page( MainTemplate() .Title(title) .MenuBar(MenuBar ctx action) .Body(body) // I assume I have to get login into here .Doc() )
In general I am hoping to be able to substitute different partial-templates into the body of main and keep each of the partial-templates in it's own file so that they can be managed separately.
Or are you saying that if I am to "embed" a partial-template into the body of main.html that i have to put the partial-templates into main.html as well, and subs that into body at runtime
In my login.html that I am trying to use I had wrapped it in
I am trying to use main.html like a "master page" so that it contains the boilerplate headers, nav, etc. and then depending on the requested endpoint subs the relevant partial temple as required into main's body.
also I have been reading https://developers.websharper.com/docs/v4.x/fs/ui and the section on Instantiating Templates makes mention of <ws-MyTpl Inner="Outer"> but then the example does not use that, I am not sure if this is relevant to what I am trying to do.
Sorry I am probably missing something obvious but as I say, I am very new to all this
Thanks again Derek
Topic tags
- f# × 3705
- websharper × 1897
- compiler × 286
- functional × 201
- ui next × 139
- c# × 121
- classes × 97
- web × 97
- .net × 84
- book × 84
- async × 76
- ui.next × 67
- bug × 54
- core × 49
- website × 49
- server × 45
- parallel × 43
- ui × 43
- enhancement × 41
- parsing × 41
- testing × 41
- trywebsharper × 41
- typescript × 37
- html × 35
- javascript × 35
- owin × 35
- asynchronous × 30
- monad × 28
- ocaml × 28
- tutorial × 27
- warp × 27
- haskell × 26
- sitelet × 25
- linq × 22
- workflows × 22
- wpf × 20
- fpish × 19
- introduction × 19
- silverlight × 19
- sitelets × 19
- monodevelop × 17
- rpc × 17
- suave × 17
- piglets × 16
- collections × 15
- feature request × 15
- jquery × 15
- templates × 15
- getting started × 14
- pipeline × 14
- kendoui × 13
- reactive × 12
- 4.1.0.171 × 11
- monads × 11
- opinion × 10
- 4.0.190.100-rc × 9
- deployment × 9
- fixed × 9
- formlets × 9
- in × 9
- json × 9
- plugin × 9
- proposal × 9
- scheme × 9
- solid × 9
- basics × 8
- concurrent × 8
- highcharts × 8
- how-to × 8
- python × 8
- 4.1.1.175 × 7
- complexity × 7
- documentation × 7
- visual studio × 7
- 4.1.2.178 × 6
- lisp × 6
- real-world × 6
- released in 4.0.192.103-rc × 6
- remoting × 6
- resources × 6
- scala × 6
- websharper ui.next × 6
- workshop × 6
- xaml × 6
- 4.0.193.110 × 5
- 4.2.3.236 × 5
- aspnetmvc × 5
- authentication × 5
- azure × 5
- bootstrap × 5
- conference × 5
- dsl × 5
- formlet × 5
- java × 5
- list × 5
- metaprogramming × 5
- ml × 5
- released in Zafir.4.0.188.91-beta10 × 5
- sql × 5
- visualstudio × 5
- websharper.forms × 5
- zafir × 5
- 4.0.192.106 × 4
- 4.0.195.127 × 4
- 4.1.0.38 × 4
- 4.2.1.86 × 4
- 4.2.6.118 × 4
- css × 4
- example × 4
- extensions × 4
- fsi × 4
- fsx × 4
- html5 × 4
- jqueryui × 4
- lift × 4
- reflection × 4
- remote × 4
- rest × 4
- spa × 4
- teaching × 4
- template × 4
- websocket × 4
- wontfix × 4
- 4.0.196.147 × 3
- 4.1.0.34 × 3
- 4.1.6.207 × 3
- 4.2.1.223-beta × 3
- 4.2.11.258 × 3
- 4.2.4.114 × 3
- 4.2.4.247 × 3
- 4.2.5.115 × 3
- 4.2.6.253 × 3
- 4.2.9.256 × 3
- ajax × 3
- alt.net × 3
- aml × 3
- asp.net mvc × 3
- canvas × 3
- cloudsharper × 3
- compilation × 3
- database × 3
- erlang × 3
- events × 3
- extension × 3
- file upload × 3
- forums × 3
- inline × 3
- issue × 3
- kendo × 3
- macro × 3
- mono × 3
- msbuild × 3
- mvc × 3
- pattern × 3
- piglet × 3
- released in Zafir.4.0.187.90-beta10 × 3
- svg × 3
- type provider × 3
- view × 3
- 4.1.1.64 × 2
- 4.1.5.203 × 2
- 4.1.7.232 × 2
- 4.2.10.257 × 2
- 4.2.3.111 × 2
- 4.2.5.249 × 2
- android × 2
- asp.net × 2
- beginner × 2
- blog × 2
- chart × 2
- client × 2
- client server app × 2
- clojure × 2
- computation expressions × 2
- constructor × 2
- corporate × 2
- courses × 2
- cufp × 2
- d3 × 2
- debugging × 2
- direct × 2
- discriminated union × 2
- docs × 2
- elm × 2
- endpoint × 2
- endpoints × 2
- enterprise × 2
- entity framework × 2
- event × 2
- f# interactive × 2
- fable × 2
- flowlet × 2
- formdata × 2
- forms × 2
- fsc × 2
- google maps × 2
- hosting × 2
- http × 2
- https × 2
- iis 8.0 × 2
- install × 2
- interactive × 2
- interface × 2
- iphone × 2
- iteratee × 2
- jobs × 2
- jquery mobile × 2
- keynote × 2
- lens × 2
- lenses × 2
- linux × 2
- listmodel × 2
- mac × 2
- numeric × 2
- oauth × 2
- obfuscation × 2
- offline × 2
- oop × 2
- osx × 2
- packaging × 2
- pattern matching × 2
- performance × 2
- pipelines × 2
- q&a × 2
- quotation × 2
- reference × 2
- released in Zafir.4.0.185.88-beta10 × 2
- rx × 2
- script × 2
- security × 2
- self host × 2
- seq × 2
- sockets × 2
- stm × 2
- tcp × 2
- trie × 2
- tutorials × 2
- type × 2
- url × 2
- var × 2
- websharper.charting × 2
- websharper4 × 2
- websockets × 2
- wig × 2
- xna × 2
- zh × 2
- .net interop × 1
- 2012 × 1
- 4.0.194.126 × 1
- 4.1.3.184 × 1
- 4.1.4.189 × 1
- 4.2.0.214-beta × 1
- 4.2.12.259 × 1
- 4.2.2.231-beta × 1
- 4.2.8.255 × 1
- Canvas Sample Example × 1
- DynamicStyle Animated Style × 1
- Fixed in 4.0.190.100-rc × 1
- Released in Zafir.UI.Next.4.0.169.79-beta10 × 1
- SvgDynamicAttribute × 1
- WebComponent × 1
- abstract class × 1
- accumulator × 1
- active pattern × 1
- actor × 1
- addin × 1
- agents × 1
- aggregation × 1
- agile × 1
- alter session × 1
- animation × 1
- anonymous object × 1
- apache × 1
- api × 1
- appcelerator × 1
- architecture × 1
- array × 1
- arrays × 1
- asp.net 4.5 × 1
- asp.net core × 1
- asp.net integration × 1
- asp.net mvc 4 × 1
- asp.net web api × 1
- aspnet × 1
- ast × 1
- attributes × 1
- authorization × 1
- b-tree × 1
- back button × 1
- badimageformatexception × 1
- bash script × 1
- batching × 1
- binding-vars × 1
- bistro × 1
- body × 1
- bundle × 1
- camtasia studio × 1
- cas protocol × 1
- charts × 1
- clarity × 1
- class × 1
- cli × 1
- clipboard × 1
- clojurescript × 1
- closures × 1
- cloud × 1
- cms × 1
- coding diacritics × 1
- color highlighting × 1
- color zones × 1
- combinator × 1
- combinators × 1
- compile × 1
- compile code on server × 1
- config × 1
- confirm × 1
- content × 1
- context × 1
- context.usersession × 1
- continuation-passing style × 1
- coords × 1
- cordova × 1
- cors × 1
- coursera × 1
- cross-domain × 1
- csla × 1
- current_schema × 1
- custom content × 1
- data × 1
- data grid × 1
- datetime × 1
- debug × 1
- declarative × 1
- delete × 1
- devexpress × 1
- dhtmlx × 1
- dictionary × 1
- directattribute × 1
- disqus × 1
- distance × 1
- do binding × 1
- doc elt ui.next upgrade × 1
- docker × 1
- dojo × 1
- dol × 1
- dom × 1
- domain × 1
- du × 1
- duf-101 × 1
- dynamic × 1
- eastern language × 1
- eclipse × 1
- edsl × 1
- em algorithm × 1
- emacs × 1
- emotion × 1
- enums × 1
- error × 1
- etw × 1
- euclidean × 1
- eventhandlerlist × 1
- examples × 1
- ext js × 1
- extension methods × 1
- extra × 1
- facet pattern × 1
- failed to translate × 1
- fake × 1
- fantomas × 1
- fear × 1
- float × 1
- form × 1
- form-data × 1
- forum × 1
- fp × 1
- frank × 1
- fsdoc × 1
- fsharp × 1
- fsharp.core × 1
- fsharp.powerpack × 1
- fsharpx × 1
- fsunit × 1
- function × 1
- functional style × 1
- game × 1
- games × 1
- gc × 1
- generic × 1
- geometry × 1
- getlastwin32error × 1
- getting-started × 1
- google × 1
- google.maps × 1
- grid × 1
- group × 1
- guide × 1
- hash × 1
- headers × 1
- hello world example × 1
- heroku × 1
- highchart × 1
- history × 1
- how to × 1
- html-templating × 1
- http405 × 1
- httpcontext × 1
- hubfs × 1
- i18n × 1
- ie 8 × 1
- if-doc × 1
- iis × 1
- image × 1
- images × 1
- inheritance × 1
- initialize × 1
- input × 1
- install "visual studio" × 1
- installer × 1
- int64 × 1
- interfaces × 1
- internet explorer × 1
- interop × 1
- interpreter × 1
- io × 1
- iobservable × 1
- ios × 1
- iot × 1
- ipad × 1
- isomorphic × 1
- javascript optimization × 1
- javascript semanticui resources × 1
- jquery-plugin × 1
- jquery-ui × 1
- jquery-ui-datepicker × 1
- js × 1
- kendo datasource × 1
- kendochart × 1
- kendoui compiler × 1
- knockout × 1
- l10n × 1
- learning × 1
- library × 1
- libs × 1
- license × 1
- licensing × 1
- lineserieszonescfg × 1
- local setting × 1
- localization × 1
- logging × 1
- loop × 1
- macros × 1
- mailboxprocessor × 1
- mapping × 1
- maps × 1
- markerclusterer × 1
- markup × 1
- marshal × 1
- math × 1
- mathjax × 1
- message × 1
- message passing × 1
- message-passing × 1
- meta × 1
- metro style × 1
- micro orm × 1
- minimum-requirements × 1
- mix × 1
- mobile installation × 1
- mod_mono × 1
- modal × 1
- module × 1
- mouseevent × 1
- mouseposition × 1
- multidimensional × 1
- multiline × 1
- multithreading × 1
- mysql × 1
- mysqlclient × 1
- nancy × 1
- native × 1
- nested × 1
- nested loops × 1
- node × 1
- nunit × 1
- object relation mapper × 1
- object-oriented × 1
- om × 1
- onboarding × 1
- onclick × 1
- optimization × 1
- option × 1
- orm × 1
- os x × 1
- output-path × 1
- override × 1
- paper × 1
- parameter × 1
- persistence × 1
- persistent data structure × 1
- phonegap × 1
- pola × 1
- post × 1
- powerpack × 1
- prefix tree × 1
- principle of least authority × 1
- privacy × 1
- private × 1
- profile × 1
- programming × 1
- project × 1
- project euler × 1
- projekt_feladat × 1
- protected × 1
- provider × 1
- proxy × 1
- ptvs × 1
- public × 1
- pure f# × 1
- purescript × 1
- qna × 1
- quant × 1
- query sitelet × 1
- question × 1
- quotations × 1
- range × 1
- raphael × 1
- razor × 1
- rc × 1
- reactjs × 1
- real-time × 1
- ref × 1
- region × 1
- released in 4.0.190.100-rc × 1
- reporting × 1
- responsive design × 1
- rest api × 1
- rest sitelet × 1
- restful × 1
- round table × 1
- router × 1
- routing × 1
- rpc reverseproxy × 1
- runtime × 1
- sales × 1
- sample × 1
- sampleapp × 1
- scriptcs × 1
- scripting × 1
- search × 1
- self hosted × 1
- semanticui × 1
- sequence × 1
- serialisation × 1
- service × 1
- session-state × 1
- sharepoint × 1
- signals × 1
- sitelet website × 1
- sitelet.protect × 1
- sitlets × 1
- slickgrid × 1
- source code × 1
- sqlentityconnection × 1
- ssl × 1
- standards × 1
- static content × 1
- stickynotes × 1
- streamreader × 1
- stress × 1
- strong name × 1
- structures × 1
- submitbutton × 1
- subscribe × 1
- svg example html5 websharper.ui.next × 1
- sweetalert × 1
- system.datetime × 1
- system.reflection.targetinvocationexception × 1
- table storage × 1
- targets × 1
- tdd × 1
- templates ui.next × 1
- templating × 1
- text parsing × 1
- three.js × 1
- time travel × 1
- tls × 1
- tooltip × 1
- tracing × 1
- tsunamiide × 1
- turkish × 1
- twitter-bootstrap × 1
- type erasure × 1
- type inference × 1
- type providers × 1
- type-providers × 1
- typeprovider × 1
- ui next forms × 1
- ui-next × 1
- ui.next jqueryui × 1
- ui.next charting × 1
- ui.next formlets × 1
- ui.next forms × 1
- ui.next suave visualstudio × 1
- ui.next templating × 1
- unicode × 1
- unittest client × 1
- upload × 1
- usersession × 1
- validation × 1
- vb × 1
- vb.net × 1
- vector × 1
- view.map × 1
- visal studio × 1
- visual f# × 1
- visual studio 11 × 1
- visual studio 2012 × 1
- visual studio shell × 1
- vs2017 compiler zafir × 1
- vsix × 1
- web api × 1
- web-scraping × 1
- webapi × 1
- webcomponents × 1
- webforms × 1
- webgl × 1
- webrtc × 1
- webshaper × 1
- websharper async × 1
- websharper codemirror × 1
- websharper f# google × 1
- websharper forms × 1
- websharper reactive × 1
- websharper rpc × 1
- websharper sitelets routing × 1
- websharper warp × 1
- websharper-interface-generator × 1
- websharper.chartsjs × 1
- websharper.com × 1
- websharper.exe × 1
- websharper.owin × 1
- websharper.ui.next × 1
- websharper.ui.next jquery × 1
- websockets iis × 1
- why-websharper × 1
- windows 7 × 1
- windows 8 × 1
- windows-phone × 1
- winrt × 1
- www.grabbitmedia.com × 1
- xamarin × 1
- xml × 1
- yeoman × 1
- yield × 1
- zafir beta × 1
- zafir websharper4 × 1
- zarovizsga × 1
![]() |
Copyright (c) 2011-2012 IntelliFactory. All rights reserved. Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy |
Built with WebSharper |
Hi, I am new to WebSharper and new to F#
I am wanting to create a client-server app and I am wondering what is the best approach when I want a "base" HTML page to use as a template for all other pages, so the base page would contain headers, footers, sidebar etc.
I was trying something like having a main.html as the "base page" and then wondering how I could "embed" a template for login.html (an HTML snippet) to use if the user clicks the "login" menu option (that is part of main)
I have looked through the Documentation but though I could see examples of "field templates" to use when we have multiple fields in the current document I could not figure out a way to embed another HTML snippet from another file into the main template.
Could anyone please point me at how to go about that.
OR should I actually created snippets for the header, footer, sidebar, JS scripts etc and somehow embed those in pages like, main.html, about.html, login.html etc. in which case could you point me at how I might go about that.
Sorry I know I am asking for quite a lot here.
Thanks Derek