To do this you're going to need to read the configuration file during WebSharper compilation. The way to achieve this is using WebSharper macros.

For example here is a function that compiles to a literal string taken from App.config:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
open System.Configuration
open WebSharper
open WebSharper.JavaScript
module Q = WebSharper.Core.Quotations       // F# quotations
module C = WebSharper.Core.JavaScript.Core  // JavaScript "core"
module M = WebSharper.Core.Macros

// This type defines how the function `Config` below is compiled.
type private ConfigMacro() =
    interface M.IMacro with
        // This method will be called at compile time,
        // and translate any call to `Config "somekey"` to a JavaScript string literal.
        member this.Translate(q, tr) =
            match q with
            | Q.CallOrCallModule (_, [Q.Value (Q.String key)]) ->
                // Get the value from wherever you want, here I use the file Test.config in the source folder.
                let config =
                    ConfigurationManager.OpenMappedExeConfiguration(
                        ExeConfigurationFileMap(ExeConfigFilename = __SOURCE_DIRECTORY__ + "/Test.config"),
                        ConfigurationUserLevel.None)
                match config.AppSettings.Settings.[key].Value with
                | null -> failwithf "Config key not found: %s" key
                | value -> C.Constant (C.String value)
            | _ -> failwith "Config must be called with a literal string"

// This is the function to call from client-side code.
[<Macro(typeof<ConfigMacro>)>]
let Config (key: string) = X<string>
By on 10/15/2015 2:03 AM ()

Thanks for the reply Loic, this seems to open lots of possibilites!

In my case, I need to use different API urls depending on the config I am compiling. So doing a Macro will help but for the moment the only way I found was by using the compiler directives #if #endif.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    type private ConfigMacro () =
        interface M.IMacro with
            member this.Translate(q, tr) =
                match q with
                | Q.CallOrCallModule (_, [Q.Value (Q.String key)]) ->
                    let config =
                        ConfigurationManager.OpenMappedExeConfiguration(
                            ExeConfigurationFileMap(ExeConfigFilename = __SOURCE_DIRECTORY__ + "/Web.config"),
                            ConfigurationUserLevel.None)
                    #if DEV
                    let key = key + "dev"
                    #endif
                    #if TEST
                    let key = key + "test"
                    #endif
                    #if PROD
                    let key = key + "prod"
                    #endif

                    match config.AppSettings.Settings.[key].Value with
                    | null -> failwithf "Config key not found: %s" key
                    | value -> C.Constant (C.String value)
                        
                | _ -> failwith "Config must be called with a literal string"

But this feels like a hack. Is there a better way to apply condition for configs at build time? Also is it possible to return a record type for the value instead of a string? Thanks!

By on 10/15/2015 9:26 AM ()

After some testing it seems like I could use C.NewObject to return a record type.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type LogLevel =
| Minimal
| Normal
| Verbose

type Config = {
  Id: string
  ApiUrl: string
  LogLevel: LogLevel
}

... in config macro ...

C.NewObject [
  "Id", !~ (C.String "PROD")
  "ApiUrl", !~ (C.String "http://test")
  "LogLevel", C.NewObject [ "$", !~ (C.String "1") ]
]

Is there an easier way to parse a record type to a Core.JavaScript.Core.E?

By on 10/15/2015 4:15 PM ()
IntelliFactory Offices 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