AICon 上海站|90%日程已就绪,解锁Al未来! 了解详情
写点什么

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

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

关注

评论

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

如何实现后台管理系统的权限路由和权限菜单

徐小夕

Java 大前端 编辑器 H5 数据可视化

《Python:Python编程简介:计算机编程和机器学习入门指南》

计算机与AI

Python

架构师训练营第 1 期第 7 周总结

owl

极客大学架构师训练营

价值超10亿美元的直播系统架构图是什么样子的?

冰河

系统架构 高并发 高性能 亿级流量 直播架构

DB-Engines 11月数据库排名:PostgreSQL坐稳同期涨幅榜冠军宝座

华章IT

数据库 postgresql

【云小课】版本管理发展史之Git+——代码托管

华为云开发者联盟

git 代码管理 托管

解决大中型浏览器(Chrome)插件开发痛点:自定义热更新方案——2.基于双缓存更新功能模块

梁龙先森

Java chrome 大前端 浏览器 技术方案

终于啃完了这份Java核心原理+框架“面试圣经”,成功五面上岸美团

Java架构追梦

Java 架构 面试 微服务 框架开发

重磅解读:K8s Cluster Autoscaler模块及对应华为云插件Deep Dive

华为云开发者联盟

容器 k8s 服务

【运维思考】如何做好云上运维服务?

嘉为蓝鲸

云计算 运维 数字化转型 数据中心 云服务

浅谈API网关(API Gateway)如何承载API经济生态链

华为云开发者联盟

API 网关

架构训练营 - 第7周课后作业 - 学习总结

Pudding

又一道比较运算符相关的面试题让我明白基础很重要

Gopher指北

Go 语言

爆料!前华为微服务专家纯手打500页落地架构实战笔记,已开源

996小迁

架构 面试 分布式 微服务 程序人生

mongodb 源码实现系列 - 网络传输层模块实现三

杨亚洲(专注MongoDB及高性能中间件)

MySQL mongodb 分布式 高性能 分布式数据库mongodb

阿里云官方推出操作系统“等保合规”镜像 -- Alibaba Cloud Linux 等保2.0三级版

阿里云基础软件团队

内核

百亿级数据分表后怎么分页查询?

艾小仙

Java MySQL 数据库 编程语言 分库分表

医疗界“最强大脑”落户杭州!阿里巴巴联合浙大一院共同打造

互联网

谈谈敏捷开发概念和迭代开发方案

Learun

敏捷开发

架构师训练营第一期 - week8

习习

【涂鸦物联网足迹】涂鸦云平台接口说明

IoT云工坊

人工智能 物联网 API sdk 云平台

揭秘在召唤师峡谷中移动路径选择逻辑?

华为云开发者联盟

算法 地图 最短路径

LeetCode题解:77. 组合,递归回溯,JavaScript,详细注释

Lee Chen

算法 大前端 LeetCode

终于啃完了Java核心原理+框架“面试圣经”成功五面上岸美团

小Q

Java 学习 编程 架构 面试

帮助企业摆脱困境,名企归乡工程师:能成功全靠有它!

Philips

敏捷开发

架构师训练营 - 第 7 周课后作业(1 期)

Pudding

会展云技术解读 | 面对突发事故,APP如何做好崩溃分析与性能监控?

京东科技开发者

云计算 云服务

每周一看:16份文档资料,程序员软硬实力全概览,总有一个适合你

小Q

Java 学习 程序员 架构 面试

Apache DolphinScheduler 是如何走进Apache的

代立冬

大数据 数据湖调度 DolphinScheduler Apache DolphinScheduler

技术分享:WebAssembly能否重新定义前端开发模式?

葡萄城技术团队

webassembly

移动安全加固助力 App 实现全面、有效的安全防护

蚂蚁集团移动开发平台 mPaaS

安全攻防 App风险 mPaaS

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