写点什么

使用 WebSharper 和 F#开发移动应用

  • 2012-02-28
  • 本文字数:12687 字

    阅读完需:约 42 分钟

虽然开发移动应用程序是一件棘手的事情,但是只要在开始阶段拥有正确的方向和技术基础,一切都会变得不同。在许许多多的技术替代方案面前,移动应用开发人员会不断地意识到,专攻于某个特定的平台将不再是可行之道。传统的原生平台(iOS,Android,Windows Phone 7,Windows Mobile 等等)没必要搞得那么复杂,并且没必要固定到某个软件栈 (software stack),因为后者不仅学习曲线陡峭,而且需要解决许多问题才能摸清它们各自平台的内在联系。如果没有足够强的驱动力,在那些原生平台上进行开发的话,那么随后紧密控制的应用程序开发和销售渠道会让事情变得更糟糕。

不过还好,至少有两种方法可以摆脱在原生平台上开发的困境。一种方法是采用更加熟悉的编程语言和开发环境,并将结果转换为原生代码(这一般发生在 iOS 上的开发,如类似 MonoTouch 的解决方案)。在一定程度上,这种方法依赖于学习类似的复杂 API,以及在 API 之上进行特殊处理以完成正确映射,从而获得原生设备的能力。

另一种方法是选择基于 Web 的移动应用程序。虽然它们的开发环境有些蹩脚,但是消除了对特定平台相关技术的需求,并将应用程序放到基于通用的 Web 标准(如 HTML5,CSS3 和 JavaScript)基础之上,这大大简化了跨平台的扩展能力。然而我们从 Web 中学到的一件事情是:不能也不应当期望它所能做的事情以及提供的服务必须能被任何设备使用。你可以预期未来移动平台版本和操作系统会进一步模糊传统的“原生”和“Web”应用程序之间的界限。

目前,开发基于 Web 的跨平台移动应用程序已经有了像 PhoneGap Rhomobile ,和 AppMobi 的解决方案,它们依赖于使用 JavaScript API 暴露原生设备功能,并通过在原生的 Shell 应用程序中运行上述 API 编写的代码来渲染 Web 应用程序。这听起来像是一个不错的提议,但是前提是需要使用 JavaScript 开发。另外一种选择是基于领域专用语言 (Domain Specific Language, DSL)。此外,InfoQ 上有一篇文章讨论了移动Web 应用程序开发现状

WebSharper

WebSharper 旨在解决上面的一些问题。首先,它可以使用 F#开发整个 Web 和移动应用程序,整个开发过程不仅可以享受 F#简洁的语法和强大的函数式结构,还可以减少许多过去需要经常性编写的代码。其次,它为常见的 Web 相关的琐碎工作提供了一系列丰富的抽象及 eDSL 语法,例如组合 HTML、定义 Web 表单、管理所需资源、安全地处理 URL 以及其他许多工作。WebSharper 之所以特别适合于大型企业级应用程序开发,是因为这些抽象都是强类型的:例如,构造 Web 表单时产生错误数据类型,或尝试为错误的输入控件添加表格验证,都会造成编译期错误,这再次大大的缩短了开发时间。

利用 sitelet 构造站点

WebSharper 2.0 引入了 sitelet ,它是类型安全的头等网站元素。sitelet 定义在“action”联合类型之上,它包含了所表示站点中全部网页 / 内容的集合,还包含一个路由和一个控制器用作在动作 (action) 和真实的内容之间来回地映射 URL 请求。

(点击图片进行放大)

图1:WebSharper Visual Studio 模板中的样例网页

下面是从安装后的WebSharper 样例sitelet 应用程序模板中抽取的一个简单的Action 类型,它定义了图1 中元素较少的样例网页。

复制代码
<span color="#00b300">/// Actions that correspond to the different pages in the site.</span>
<span color="#0000ff">type</span> Action =
| Home
| Contact
| Protected
| Login <span color="#0000ff">of</span> option<Action>
| Logout
| Echo <span color="#0000ff">of</span> string

根据 sitelet 的服务目的(如 REST 服务),可以在其中加入任意的内容,如返回任意的包含 XML 或 HTML 内容的文件。如果需要对 URL 空间进行细粒度的控制、需要它们能够自动的从行动类型中推测出、或是使用其中一种策略通过把更小的 sitelet 结合在一起以满足两种需求,那么可以通过手工构造路由和控制器,

sitelet 还带有一个类型安全的模板语言,该语言基于 XML 标记并使用特殊的占位符。当你将后缀为.template.xml 的文件加入到 WebSharper Visual Studio 解决方案中时,它们会被自动地转换为 F#代码并包含在构建列表中。

下面显示了同样是来自于样例 sitelet 应用程序模板中的 Skin.template.xml 中的模板标注:

复制代码
<span color="#0000ff"><<span color="#800000">html</span> <span color="#ff0000">xmlns</span>=<span color="#000000">"</span>http://www.w3.org/1999/xhtml<span color="#000000">"</span>></span>
<span color="#0000ff"><<span color="#800000">head</span>></span>
<span color="#0000ff"><<span color="#800000">title</span>gt;<span color="#000000">Your site title</span></<span color="#800000">title</span>></span>
<span color="#0000ff"><<span color="#800000">link</span> <span color="#ff0000">href</span>=<span color="#000000">"</span>~/themes/reset.css<span color="#000000">"</span> <span color="#ff0000">rel</span>=<span color="#000000">"</span>stylesheet<span color="#000000">"</span> <span color="#ff0000">type</span>=<span color="#000000">"</span>text/css<span color="#000000">"</span> /></span>
<span color="#0000ff"><<span color="#800000">link</span> <span color="#ff0000">href</span>=<span color="#000000">"</span>~/themes/site.css<span color="#000000">"</span> <span color="#ff0000">rel</span>=<span color="#000000">"</span>stylesheet<span color="#000000">"</span> <span color="#ff0000">type</span>=<span color="#000000">"</span>text/css<span color="#000000">"</span> /></span>
<span color="#0000ff"></<span color="#800000">head</span>></span>
<span color="#0000ff"><<span color="#800000">body</span>></span>
<span color="#0000ff"><<span color="#800000">div</span>></span>
<span color="#0000ff"><<span color="#800000">div </span><span color="#ff0000">id</span>=<span color="#000000">"</span>loginInfo<span color="#000000">"</span>><span color="#000000">${LoginInfo}</span></<span color="#800000">div</span>></span><p><span color="#0000ff"><<span color="#800000">div </span><span color="#ff0000">id</span>=<span color="#000000">"</span>header<span color="#000000">"</span>></span></p><p><span color="#0000ff"><<span color="#800000">div</span> <span color="#ff0000">id</span>=<span color="#000000">"</span>banner<span color="#000000">"</span>><span color="#000000">${Banner}</span></<span color="#800000">div</span>></span><span color="#0000ff"><<span color="#800000">div</span> <span color="#ff0000">id</span>=<span color="#000000">"</span>menu<span color="#000000">"</span>><span color="#000000">${Menu}</span></<span color="#800000">div</span>></span><span color="#0000ff"><<span color="#800000">div</span> <span color="#ff0000">class</span>=<span color="#000000">"</span>closer<span color="#000000">"</span>></<span color="#800000">div</span>></span><span color="#0000ff"></<span color="#800000">div</span>></span></p><p><span color="#0000ff"><<span color="#800000">div</span> <span color="#ff0000">id</span>=<span color="#000000">"</span>main-container<span color="#000000">"</span>></span></p><p><span color="#0000ff"><<span color="#800000">div</span> <span color="#ff0000">id</span>=<span color="#000000">"</span>main<span color="#000000">"</span>><span color="#000000">${Main}</span></<span color="#800000">div</span>></span><span color="#0000ff"><<span color="#800000">div</span> <span color="#ff0000">id</span>=<span color="#000000">"</span>sidebar<span color="#000000">"</span>><span color="#000000">${Sidebar}</span></<span color="#800000">div</span>></span><span color="#0000ff"><<span color="#800000">div</span> <span color="#ff0000">class</span>=<span color="#000000">"</span>closer<span color="#000000">"</span>></<span color="#800000">div</span>></span><span color="#0000ff"></<span color="#800000">div</span>></span></p><p><span color="#0000ff"><<span color="#800000">div</span> <span color="#ff0000">id</span>=<span color="#000000">"</span>footer<span color="#000000">"</span>><span color="#000000">${Footer}</span></<span color="#800000">div</span>></span></p><p><span color="#0000ff"></<span color="#800000">div</span>></span><span color="#0000ff"></<span color="#800000">body</span>></span><span color="#0000ff"></<span color="#800000">html</span>></span></p>

上述模板会在默认的命名空间下创建一个叫做 Templates.Skin 的模块,用以组合标记片段到占位符中。考虑下面的函数,它接受标题 (title) 和网页主要内容 (main) 作为参数,并使用生成的模板函数构造出网页:

复制代码
<span color="#00b300">/// A template function that renders a page with a menu bar, based on the Skin template.</span>
<span color="#0000ff">let</span> Template title main : Content<Action> =
<span color="#0000ff">let</span> menu (ctx: Context<Action>)=
[
A [Action.Home |> ctx.Link |> HRef] -< [Text <span color="#800000">"Home"</span>]
A [Action.Contact |> ctx.Link |> HRef] -< [Text <span color="#800000">"Contact"</span>]
A [Action.Echo <span color="#800000">"Hello"</span> |> ctx.Link |> HRef] -< [Text <span color="#800000">"Say Hello"</span>]
A [Action.Protected |> ctx.Link |> RandomizeUrl |> HRef] -< [Text <span color="#800000">"Protected"</span>]
A [<span color="#800000">"~/LegacyPage.aspx"</span> |> ctx.ResolveUrl |> HRef] -< [Text <span color="#800000">"ASPX Page"</span>]
]
|> List.map (<span color="#0000ff">fun <span color="#000000">link</span> -></span>
Label [Class <span color="#800000">"menu-item"</span>] -< [link]
)
Templates.Skin.Skin (Some title)
{
LoginInfo = Widgets.LoginInfo
Banner = <span color="#0000ff">fun <span color="#000000">ctx</span> -></span> [H2 [Text title]]
Menu = menu
Main = main
Sidebar = <span color="#0000ff">fun <span color="#000000">ctx</span> -></span> [Text <span color="#800000">"Put your side bar here"</span>]
Footer = <span color="#0000ff">fun <span color="#000000">ctx</span> -></span> [Text <span color="#800000">"Your website. Copyright (c) 2011 YourCompany.com"</span>]
}

这里的 main 是一个生成 XML/HTML 元素列表的函数,它与内部处理菜单的 meanu 函数类似。另外,还要注意一下 context 对象是怎样利用管道运算符将各种不同的 Action 映射到安全的 URL 上的(注:管道|> 操作符用来像函数发送参数,例如 x |> f 等同于 f(x))。

你还可以定义各种小型的抽象类型,使你的应用程序代码变得更加简洁。下面是一个链接操作符 (=>),用作创建超链接:

复制代码
<span color="#00b300">/// A helper function to create a hyperlink</span>
<span color="#0000ff">let</span> ( => ) title href =
A [HRef href] -< [Text title]

现在你可以在 sitelet 中定义主页了,如下:

复制代码
<span color="#00b300">/// The pages of this website.</span>
<span color="#0000ff">module</span> Pages =<p><span color="#00b300">/// The home page.</span><br></br><span color="#0000ff">let</span> HomePage : Content<Action> =<br></br> Template <span color="#800000">"Home"</span> <| <span color="#0000ff">fun <span color="#000000">ctx</span> -></span><br></br> [<br></br> H1 [Text <span color="#800000">"Welcome to our site!"</span>]<br></br><span color="#800000">"Let us know how we can contact you"</span> => ctx.Link Action.Contact<br></br> ]<br></br> ...</p>

一旦定义好所有的页面,就可以创建一个 sitelet 来显示网站了。下面显示了三个更小的 sitelet 的组合:

复制代码
<span color="#0000ff">let</span> EntireSite =
<p><span color="#00b300">// A simple sitelet for the home page, available at the root of the application.</span><br></br><span color="#0000ff">let</span> home =<br></br> Sitelet.Content "/" Action.Home Pages.HomePage</p><p><span color="#00b300">// An automatically inferred sitelet created for the basic parts of the application.</span><br></br><span color="#0000ff">let</span> basic =<br></br> Sitelet.Infer <| <span color="#0000ff">fun</span> action <span color="#0000ff">-></span><br></br><span color="#0000ff">match</span> action <span color="#0000ff">with</span><br></br> | Action.Contact <span color="#0000ff">-></span> Pages.ContactPage<br></br> | Action.Echo param <span color="#0000ff">-></span> Pages.EchoPage param<br></br> | Action.Login action <span color="#0000ff">-></span> Pages.LoginPage action<br></br> | Action.Logout <span color="#0000ff">-></span></p><p><span color="#00b300">// Logout user and redirect to home</span> UserSession.Logout ()</p><br></br> Content.Redirect Action.Home<br></br> | Action.Home <span color="#0000ff">-></span> Content.Redirect Action.Home<br></br> | Action.Protected <span color="#0000ff">-></span> Content.ServerError<p><span color="#00b300">// A sitelet for the protected content that requires users to log in first.</span><br></br><span color="#0000ff">let</span> authenticated =<br></br><span color="#0000ff">let</span> filter : Sitelet.Filter<Action> =<br></br> {<br></br> VerifyUser = <span color="#0000ff">fun</span> _ <span color="#0000ff">-></span> <span color="#0000ff">true</span><br></br> LoginRedirect = Some >> Action.Login<br></br> }</p><p>Sitelet.Protect filter<br></br> <| Sitelet.Content <span color="#800000">"/protected"</span> Action.Protected Pages.ProtectedPage</p><p><span color="#00b300">// Compose the above sitelets into a larger one.</span><br></br> Sitelet.Sum<br></br> [<br></br> home<br></br> authenticated<br></br> basic<br></br> ]</p>

借助上面的 sitelet,你所需要做的就是标注它为 sitelet,然后,你瞧,你的站点可以在基于 ASP.NET 的 Web 容器里工作了(WebSharper Visual Studio 模板提供了必要的 Web.config 改动):

复制代码
<span color="#00b300">/// Expose the main sitelet so it can be served.</span>
<span color="#00b300">/// This needs an IWebsite type and an assembly level annotation.</span>
<span color="#0000ff">type</span> SampleWebsite() =
<span color="#0000ff">interface</span> IWebsite<SampleSite.Action> <span color="#0000ff">with</span>
<span color="#0000ff">member</span> this.Sitelet = EntireSite
<span color="#0000ff">member</span> this.Actions = []
<p>[<assembly: WebsiteAttribute(typeof<SampleWebsite>)>]<br></br><span color="#0000ff">do</span> ()</p>

Formlet —— 组合一流的类型安全表单

Formlet 是最近一套来自学术界的形式体系,它是 WebSharper 不可分割的一部分。而 WebSharper 则是最初实现 Formlet 的几个框架之一。Formlet 代表了一流的、类型安全的、可组合的数据表单,它与你可能一直在用的 ASP.NET 或其他 Web 框架中的非严格类型的方法有着很大的不同。WebSharper 实现中包含了 _ 从属 formlet_,其中 formlet 的一部分从属于另一部分,例如从属于多选项的下拉框或是输入框中的输入值;_flowlets_ 是一种定制的布局,它用来在一个 formlet 计算表达式或 F#一元结构中以一种类似向导的顺序方式一步步渲染每一个 formlet。

下面是一个简单的 formlet,它返回一个字符串值,其中各种不同的增强被增量式应用于其上:

复制代码
<span color="#0000ff">let</span> nameF =
Controls.Input ""
|> Validator.IsNotEmpty <span color="#800000">"Empty name not allowed"</span>
|> Enhance.WithValidationIcon
|> Enhance.WithTextLabel <span color="#800000">"Name"</span>

Formlet 可以被映射到任意类型的返回值上,例如一个百分比输入控件可能会返回 0 到 100 之间的浮点数值,或者一个组合框可能会生成可区分联合 (discriminated union) 中的某种类型(可能有也可能没有标记值)。你可以用许多方式将多个较小的 formelet 组合成更大的 formlet。最简单的方法是使用 Formlet.Yield 函数将任意类型的值封装成该类型的 formlet,并结合 <*> 操作符组合两个(或通过连续调用组合多个)formlet:

复制代码
Formlet.Yield (<span color="#0000ff">fun</span> v1 v2 ... vn <span color="#0000ff">-></span> <compose all vs>)
<*> formlet1
<*> formlet2
...
<*> formletn

下面的例子展示了 Formlet 如何获取个人信息(姓名和邮件),并进行基本的客户端验证:

复制代码
<span color="#0000ff">type</span> Person = {
Name: string
Email: string
}
<p>[<JavaScript>]<br></br><span color="#0000ff">let</span> PersonFormlet () : Formlet<Person> =<br></br><span color="#0000ff">let</span> nameF =<br></br> Controls.Input <span color="#800000">""</span><br></br> |> Validator.IsNotEmpty <span color="#800000">"Empty name not allowed"</span><br></br> |> Enhance.WithValidationIcon<br></br> |> Enhance.WithTextLabel <span color="#800000">"Name"</span><br></br><span color="#0000ff">let</span> emailF =<br></br> Controls.Input <span color="#800000">""</span><br></br> |> Validator.IsEmail <span color="#800000">"Please enter valid email address"</span><br></br> |> Enhance.WithValidationIcon<br></br> |> Enhance.WithTextLabel <span color="#800000">"Email"</span><br></br> Formlet.Yield (<span color="#0000ff">fun</span> name email <span color="#0000ff">-></span> { Name = name; Email = email })<br></br> <*> nameF<br></br> <*> emailF<br></br> |> Enhance.WithSubmitAndResetButtons<br></br> |> Enhance.WithLegend <span color="#800000">"Add a New Person"</span><br></br> |> Enhance.WithFormContainer</p>

图 2 显示了嵌入在 sitelet 页面后的结果。注意页面样式是由从属的 CSS 资源提供的,它会在引用 formlet 代码时自动加载到页面中(事实上,准确地说是发生在调用 Enhance.WithFormContainer 时)。WebSharper 中高级的依赖性跟踪功能会在页面处于服务状态时为其自动收集所依赖的资源。这个功能非常便利,它为使用各种不同的 WebSharper 扩展和使用第三方的 JavaScript 库节省了大量的时间和精力,并且它从根本上消除了手工跟踪页面所需资源的需要。

图 2:包含验证以及各种增强的简单 formlet

上面 formlet 例子中的 [] 标注指示 WebSharper 将代码段翻译为 JavaScript。每个控件中增强的验证器均为 WebSharper formlet 库的一部分,并且它们提供客户端验证,因此 Validator.IsEmail 将确保在 formlet 在到达一种可接受状态前只键入了合法的邮件地址。你还可以调用自定义的函数或者通过进一步加强手头的 formlet 来提供额外的验证。如果某个函数被标记为 [] 并从客户端代码中调用,那么 WebSharper 将会生成代码执行 RPC(远程过程调用)并自动处理客户端和服务端的值传递。你可以无缝使用任意复杂的 F#对象,如嵌套列表 (nested list)、映射 (map)、集合 (set) 或序列 (sequence),而不用担心它们在内部被如何映射。这统一了客户端和服务端代码,并且大大地减少了开发时间。事实上,客户端和服务端代码在开发过程中通常位于同一个 F#文件中,只是它们被组织进同一命名空间下的不同模块中。

许多 WebSharper 模式可以用来开发客户端 - 服务器应用程序,我们通常建议使用 sitelet 和 formlet 一起工作,并提供各种编码指导来最大限度地提高开发人员的工作效率,但是你也可以借助 WebSharper 在大量的 ASP.NET 代码基础上开发混合型的应用程序,或者基于 WebSharper 的功能改善现有的 ASP.NET 应用程序。

从抽象中构建来满足所需

有时,你可能需要跳出标准 WebSharper formlet 库的范围来为应用程序实现表单(或者整个 UI)。例如,你可能想要使用不同的输入控件来渲染 formlet,因为简单的 CSS 重写可能不能够满足你所想要的外观和感觉。其它时候,你想重用现有的 JavaScript 控件库,如 Ext JS YUI ,或是 jQuery UI 来得到更精细的外观和感觉。WebSharper 为上述的第三方库提供了大量的扩展包,其中一些扩展包还提供了formlet 抽象。

下面的简短例子在jQuery 移动扩展中使用了Formlet,通过在Formlet.Do 中使用flowlet 布局以及组合熟悉的Formlet.Yield 一起完成了两个步骤的登录序列:

复制代码
<span color="#0000ff">let</span> loginSequenceF =
Formlet.Do {
<span color="#0000ff">let!</span> username, password, remember =
Formlet.Yield (<span color="#0000ff">fun</span> user pass remember <span color="#0000ff">-></span> user, pass, remember)
<*> (Controls.TextField <span color="#800000">""</span> Theme.C <span color="#800000">"Username: "</span>
|> Validator.IsNotEmpty <span color="#800000">"Username cannot be empty!"</span>)
<*> (Controls.Password <span color="#800000">""</span> Theme.C <span color="#800000">"Password: "</span>
|> Validator.IsRegexMatch <span color="#800000">"^[1-4]{4,}[0-9]$" "The password is wrong!"</span>)
<*> Controls.Checkbox true Theme.C <span color="#800000">"Keep me logged in "</span>
|> Enhance.WithSubmitButton <span color="#800000">"Log in"</span> Theme.C
<span color="#0000ff">let</span> rememberText =
<span color="#0000ff">if</span> remember <span color="#0000ff">then</span> <span color="#800000">""</span> <span color="#0000ff">else</span> <span color="#800000">"not "</span>
<span color="#0000ff">do!</span> Formlet.OfElement <span color="#0000ff">(fun <span color="#000000">_</span> -></span>
Div [
H3 [Text <span color="#800000">("Welcome "</span> + username + <span color="#800000">"!"</span>)]
P [Text (<span color="#800000">"We will "</span> + rememberText + <span color="#800000">"keep you logged in."</span>)]
])
}
|> Formlet.Flowlet

你可以使用必要的 jQuery 移动功能将登录序列组合进 HTML 标记中(可以使用多几行的代码将其很好地抽象出来),然后添加到 sitelet 页面上:

复制代码
Div [HTML5.Attr.Data <span color="#800000">"role" "page"</span>] -< [
Div [HTML5.Attr.Data <span color="#800000">"role" "header"</span>] -< [
H1 [Text <span color="#800000">"WebSharper Formlets for jQuery Mobile"</span>]>
]
<p>Div [HTML5.Attr.Data <span color="#800000">"role" "content"</span>] -< [<br></br> loginSequenceF<br></br> ]</p><p>Div [HTML5.Attr.Data <span color="#800000">"role" "footer"</span>] -< [<br></br> P [Attr.Style <span color="#800000">"text-align: center;"</span>] -< [Text <span color="#800000">"IntelliFactory"</span>]<br></br> ]<br></br>]</p>

一旦你在 WebSharper 移动项目中调整移动配置文件,产生了 Android 包(也可以选择 Windows Phone 7),那么将其安装至手机,你会看到如图 3 所示的界面:

图 3:运行在 Android 上的 jQuery 移动 formlet

使用 WebSharper 移动 API 和第三方地图控件

Formlet 和 sitelet 大大简化了 Web 开发和移动开发,并且提供了健壮、类型安全且可组合的抽象来为应用程序的部分模块进行建模。WebSharpe 中的另一个基础抽象是 _pagelets_,它由多个 formlet 搭建而成。pagelet 代表了一流的、可组合的客户端标注及行为。WebSharper 的 pagelet 不仅与 ASP.NET 控件兼容,还可以直接嵌入到 ASP.NET 标记中。

下面的例子是实现了地图控件的 pagelet,运行结果如图 4 所示:

复制代码
<span color="#0000ff">open</span> IntelliFactory.WebSharper
<span color="#0000ff">open</span> IntelliFactory.WebSharper.Bing
<span color="#0000ff">open</span> IntelliFactory.WebSharper.Html
<span color="#0000ff">open</span> IntelliFactory.WebSharper.JQuery
<span color="#0000ff">open</span> IntelliFactory.WebSharper.Mobile
<p><span color="#0000ff">type</span> CurrentLocationControl() =<br></br><span color="#0000ff">inherit</span> Web.Control()</p><p>[<JavaScript>]<br></br><span color="#0000ff">override</span> this.Body =<br></br><span color="#0000ff">let</span> screenWidth = JQuery.Of("body").Width()</p><p><span color="#0000ff">let</span> MapOptions =<br></br> Bing.MapViewOptions(<br></br> Credentials = bingMapsKey,<br></br> Width = screenWidth - 10,<br></br> Height = screenWidth - 10,<br></br> Zoom = 16)</p><p><span color="#0000ff">let</span> label = H2 []</p><p><span color="#0000ff">let</span> setMap (map : Bing.Map) =<br></br><span color="#0000ff">let</span> updateLocation() =</p><p><span color="#00b300">// Gets the current location</span><br></br><span color="#0000ff">let</span> loc = Mobile.GetLocation()</p><p><span color="#00b300">// Sets the label to be the address of the current location</span><br></br> Rest.RequestLocationByPoint(<<your-bingmaps-key>>, loc.Lat, loc.Long, [<span color="#800000">"Address"</span>],<br></br><span color="#0000ff">fun</span> result <span color="#0000ff">-></span><br></br><span color="#0000ff">let</span> locInfo = result.ResourceSets.[0].Resources.[0]<br></br> label.Text <- <span color="#800000">"You are currently at "</span> + JavaScript.Get <span color="#800000">"name"</span> locInfo)</p><p><span color="#00b300">// Adds a pushpin at the current location</span><br></br><span color="#0000ff">let</span> loc = Bing.Location(loc.Lat, loc.Long)<br></br><span color="#0000ff">let</span> pin = Bing.Pushpin loc<br></br> map.Entities.Clear()<br></br> map.Entities.Push pin<br></br> map.SetView(Bing.ViewOptions(Center = loc))</p><p><span color="#00b300">// Keep updating your location regularly</span><br></br> JavaScript.SetInterval updateLocation 1000 |> ignore</p><p><span color="#0000ff">let</span> map =<br></br> Div []<br></br> |>! OnAfterRender (<span color="#0000ff">fun</span> this <span color="#0000ff">-></span></p><p><span color="#00b300">// Renders a Bing Maps control</span><span color="#0000ff">let</span> map = Bing.Map(this.Body, MapOptions)</p><br></br> map.SetMapType(Bing.MapTypeId.Road)<br></br> setMap map)<p><span color="#00b300">// Returns the HTML markup for this control</span><br></br> Div [<br></br> label<br></br> Br []<br></br> map<br></br> ] :> _</p>

图 4:通过 Bing 地图控件和地址栏显示当前位置信息

该控件使用 WebSharper 移动 API 获取当前 GPS 位置。IntelliFactory.WebSharper.Mobile 命名空间下还有许多实用工具用于底层移动设备间的交互,包括获取加速计数据,访问摄像头的能力以及显示原生的警告信息。未来版本的 WebSharper 移动 API 也将会包含平台相关的扩展,如蓝牙通信能力等等。

总结

如果你还没有用过 X-to-JavaScript 工具来帮助编写 Web 和移动应用程序的话,你也许想知道为什么它们的数量会有如此之多,以及是什么原因让人们想要去使用它们。WebSharper 是一种针对 F#的健壮的 Web 开放框架,它正在被一些企业级应用程序积极使用。WebSharper 解决了许多 Web 和移动开发中经常遇到的问题,并且提供了众多的功能,如安全 URL,自动资源跟踪,客户端标注及功能中提供的类型安全且可组合的抽象,带有客户端验证的声明式 Web 表单以及网站价值。

WebSharper 2.3.28+ 更新和后续的 2.4 发布版本包含了用于移动 Web 开发的 Visual Studio 模板,使用模板你可以快速尝试和实验本文中的两个例子。你也可以在这里这里下载到源代码,包括最后生成的Android 包。

关于作者

Adam Granicz 是 F#的资深业内人士和核心社区成员,他与人合著过三本 F#书籍,包括与 F#的语言设计者 Don Syme 合著的《Expert F# 2.0》。他的公司 IntelliFactory 专注于高级 F#项目咨询,并为使用 F# 进行 Web、移动以及云端应用程序开发塑造未来,公司还开发了 F#的首个 Web 开发框架——WebSharper。你可以通过 granicz.adam {at} intellifactory.com 与他联系,还可以关注他的 Twitter ,或者在函数式编程天堂 FPish 里找到他。

查看英文原文: F# mobile development with WebSharper


感谢侯伯薇对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ )或者腾讯微博( @InfoQ )关注我们,并与我们的编辑和其他读者朋友交流。

2012-02-28 00:003511
用户头像

发布了 125 篇内容, 共 37.3 次阅读, 收获喜欢 5 次。

关注

评论

发布
暂无评论
发现更多内容

vue实战中的一些小技巧

yyds2026

Vue

vue实战-深入响应式数据原理

yyds2026

Vue

引迈信息低代码怎么样?靠谱吗?

优秀

低代码 低代码平台

传统 Web 框架部署与迁移

阿里巴巴云原生

阿里云 Serverless 云原生

Apache EventMesh事件驱动分布式运行时

EventMesh布道师

Serverless Faas EDA workflow eventmesh

HarmonyOS 3.1版本发布,全面进入声明式开发

HarmonyOS开发者

HarmonyOS

ElasticSearch深度分页详解

京东科技开发者

数据库 elasticsearch 分布式搜索引擎 分布式实时搜索引擎

Linux系统中CPU占用率较高问题排查思路与解决方法

A-刘晨阳

Linux 运维 cpu 11月月更

详细解读 React useCallback & useMemo

夏天的味道123

React

zabbix添加自定义监控项&告警(邮件)

A-刘晨阳

Linux 运维 zabbix 11月月更

质量评估模型助力风险决策水平提升

百度Geek说

机器学习 企业号十月 PK 榜 智能测试 质量评估模型

详解React的Transition工作原理原理

夏天的味道123

React

浅谈HTTP缓存与CDN缓存的那点事

京东科技开发者

缓存 性能 Web CDN HTTP缓存

vue实战-完全掌握Vue自定义指令

yyds2026

Vue

如何使用ModelBox快速提升AI应用性能

华为云开发者联盟

人工智能 华为云 ModelBox

解读数仓常用模糊查询的优化方法

华为云开发者联盟

数据库 后端 华为云

【docker】导入镜像报错磁盘空间不足的解决方法 && 【docker】修改默认的存储路径

A-刘晨阳

Docker Linux 运维 11月月更

Linux系统保存文件命令的详细介绍

源字节1号

软件开发 前端开发 后端开发 小程序开发

数据中台选型必读(五):中台建设本质就是构建企业的公共数据层

雨果

数据中台

技术分享| Etcd如何实现分布式负载均衡及分布式通知与协调

anyRTC开发者

分布式 etcd 通知 式负载均衡 协调

经常被问到的react-router实现原理详解

夏天的味道123

React

一汽集团数字化转型细节分析:明确如何转型事半功倍

雨果

数字化转型

会用postman不算牛,会用Eolink才是真的牛

陈橘又青

API

可防离职员工冒用身份,合合信息名片全能王与钉钉用数字名片打造安全“围栏”

合合技术团队

人工智能 大数据 钉钉 合合信息 名片

龙蜥理事长马涛荣获 “2022 年度开源人物”

OpenAnolis小助手

开源 操作系统 龙蜥社区 理事长 2022云栖大会

启科量子 QuSprout 或将启动开源计划

启科量子开发者官方号

人工智能 框架 算力 超算 #量子计算

Apache Pulsar 社区年度峰会 Pulsar Summit Asia 2022 即将召开

腾源会

大数据 开源

用了8年MQ!聊聊消息队列的技术选型,哪个最香!

小小怪下士

Java RocketMQ RabbitMQ 消息队列

阿里 CTO 程立:今年双 11,全面深度用云

云布道师

云计算 阿里巴巴 天猫

国产数据库肇始之独具特色的场景需求

亚信AntDB数据库

数据库 AntDB 国产数据库 AntDB数据库

字节跳动基于ClickHouse优化实践之“资源隔离”

字节跳动数据平台

大数据 Clickhouse

使用WebSharper和F#开发移动应用_.NET_Adam Granicz_InfoQ精选文章