阿里、蚂蚁、晟腾、中科加禾精彩分享 AI 基础设施洞见,现购票可享受 9 折优惠 |AICon 了解详情
写点什么

使用 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:003383
用户头像

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

关注

评论

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

maya软件在建模上有什么优势?

Finovy Cloud

maya 3D软件

国内外主流的8款 IT 项目管理平台

PingCode

项目管理工具 PingCode 项目研发管理软件

AIGC的阿克琉斯之踵

华为云开发者联盟

人工智能 AI 华为云 华为云开发者联盟 企业号 4 月 PK 榜

AutoCAD 2024 Mac版如何设置自动保存文件为低版本?

魔仙苹果mac堡

CAD制图软件 AutoCAD 2024 Mac版 AutoCAD使用技巧 保存文件为低版本

用数据分析的方法去做dba,维护好tidb数据库。

TiDB 社区干货传送门

6.x 实践

Mac人工智能图像降噪 Topaz Photo AI 安装激活教程

魔仙苹果mac堡

Topaz Photo AI下载 图像降噪 苹果mac软件下载 Topaz Photo AI mac Topaz Photo AI破解

超强版干货投递!Milvus 的部署心得、运维秘籍都在这里了!

Zilliz

Milvus Zilliz ChatGPT LLM zillizcloud

PCB阻焊桥存在的DFM(可制造性)问题,华秋一文告诉你

华秋电子

免费领取 | ONES 联合中国信通院发布《中国企业软件研发管理白皮书》

万事ONES

迁移prometheus数据

TiDB 社区干货传送门

迁移 实践案例 集群管理

TiDB迁移、升级与案例分享(TiDB v4.0.11 → v6.5.1)

TiDB 社区干货传送门

迁移 版本升级 安装 & 部署 扩/缩容 6.x 实践

即时通讯技术文集(第13期):Web端即时通讯技术精华合集 [共15篇]

JackJiang

网络编程 即时通讯 IM

迁移PD坑-cdc任务全部stop

TiDB 社区干货传送门

实践案例 集群管理 故障排查/诊断

python正则 | python小知识

AIWeker

Python python小知识 三周年连更

GitHub星标48k!蚂蚁金服开源的这份SpringBoot笔记

做梦都在改BUG

Java spring Spring Boot 框架

新特性解析丨TiDB 资源管控的设计思路与场景解析

TiDB 社区干货传送门

版本测评

微服务 - 注册中心和配置中心(Consul)

做梦都在改BUG

Java 微服务 注册中心 配置中心

tidb-loadbalance 客户端方式软负载均衡配置实践

TiDB 社区干货传送门

数据库架构设计 数据库连接

OneNote 2019 for Mac 中文版附激活工具

真大的脸盆

Mac Mac 软件 笔记应用

Karmada 多云容器编排引擎支持多调度组,助力成本优化

华为云开发者联盟

云原生 后端 华为云 华为云开发者联盟 企业号 4 月 PK 榜

苹果电脑臻选4K壁纸app:Dynamic Wallpaper

魔仙苹果mac堡

动态壁纸 Dynamic Wallpaper下载 苹果壁纸下载 mac壁纸一软件 高清壁纸下载

4 月 25 日直播预告 | 深入解读 Flink 1.17

Apache Flink

大数据 flink 实时计算

面试官:Redis有什么持久化策略?

做梦都在改BUG

Java redis 缓存 面试 持久化

从零到跑通TPC-H:如何快速实现查询计划

MatrixOrigin

分布式数据库 MatrixOrigin MatrixOne TPC-H

Mysql 连接查询

谷歌 Chrome 正式发布 WebGPU!Orillusion开源倒计时!

Orillusion

开源 WebGL 元宇宙 web3d #WebGPU

Parallels Desktop PD 18虚拟机关闭、停止、中止和暂停操作的区别

魔仙苹果mac堡

pd虚拟机 Parallels Desktop PD18虚拟机操作

Neuron 2.4.0发布:体验下一代工业物联网连接和管理

EMQ映云科技

UI 物联网 IoT neuron 企业号 4 月 PK 榜

【Linux】之创建普通用户并禁止root用户远程登陆

A-刘晨阳

Linux 三周年连更 用户名

阿里P8推荐学习的44个微服务架构设计模式,真的太香了!

做梦都在改BUG

Java 架构 微服务 设计模式

WordPress 使用 TiDB Cloud 替换 MySQL

TiDB 社区干货传送门

迁移 实践案例 版本测评 应用适配

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