使用 AngularJS 构建 Firefox OS 应用

当你开始为 Firefox OS 开发时,你可能会对提供的工具感到失望。没有标准的 UI 工具包,也没有所有应用都构建在其上的 JavaScript 框架。这并非本质上是一个糟糕的情况,因为 Firefox OS 本质上是 Web;因此,它让你在使用的工具链方面拥有完全的自由。这使我们能够在 Firefox OS 上使用任何新兴的技术。缺点是你可能会错过在 Android 或 iOS 上习惯的一些东西,比如内置模板、视图过渡和 UI 组件。

Telenor Digital,我们决定构建一个现成的应用框架来解决这些缺点,该框架构建在 AngularJS(Google 的 MVW 框架)之上。该模板是我们为 Firefox OS 构建的内部应用程序迭代的结果,并解决了以下问题

  1. 构建在 AngularJS 之上,提供数据绑定、模板、路由和代码结构
  2. 内置一组 UI 组件
    和 Firefox OS 风格的过渡效果
  3. 能够将应用发布为移动网页(托管应用),
    或作为 Firefox OS 应用商店的打包应用
  4. 优先考虑离线功能。每个构建在模板之上的应用都能离线工作,
    即使托管在您自己的 Web 服务器上。
  5. 一个构建系统,可以使用一条命令创建发布版本,
    进行代码压缩和模板缓存以获得最佳性能

让我们看看演示应用是什么样子的。这是一个标准的 CRUD 应用,展示了列表-详情模式:http://janjongboom.com/ffos-list-detail/。您可以点击项目进入详情视图,可以编辑项目或添加新项目。“+”按钮是安装按钮(仅在 Firefox 中可见),允许您将应用添加到手机(Android/FxOS)。

获取代码

要开始构建,请执行以下操作

  • git clone git@github.com:comoyo/ffos-list-detail.git
  • npm install
  • ./node_modules/bower/bin/bower install
  • 现在,您可以在任何浏览器中打开www/index.html,或使用应用管理器并将
    www文件夹添加为打包应用。

结构

应用位于 www/ 文件夹中,并由以下子文件夹组成

  • components/,第三方库,通过 bower 加载
  • css/,样式表。将应用使用的所有样式列在
    css/main.css.
    中。为了获得最佳性能,它们将合并成一个大型样式表。
  • img/,包含应用的三种格式的图标。
  • js/,我们的代码
    • controllers/,将数据绑定到 UI 的代码
    • lib/,不在 bower 中的外部库
    • services/,数据提供程序或未绑定到 UI 的代码
    • app.js,应用的起点,包含全局配置(如路由)
    • main.js,基于 RequireJS 的引导文件。
      列出我们使用的所有 JavaScript 文件。当您创建新的 JS 文件时,将其添加到此处。
  • views/,视图模板
  • index.html,我们加载应用的引导文件。您可能永远不会接触它。
  • manifest.appcache,AppCache 文件。
    您需要在此处列出应用需要的所有图像和其他资源(CSS/JS 除外),
    以启用托管应用的离线功能。
  • manifest.webapp,Firefox OS 应用清单 文件。

在开发过程中,您无需设置任何构建链,您可以随意编辑 www 中的文件并刷新 index.html。这就是 Web 的强大之处:-) 当然,如果您在应用管理器中进行开发,请按“更新”以刷新应用。

现在让我们向此应用添加一些新功能,以便我们可以了解在实践中如何开发新功能。

添加一个新按钮

假设我们要添加一个显示应用构建者的版权信息屏幕。首先,我们需要在某个地方添加一个按钮。在本例中,我们将其放在应用的主屏幕上。视图代码位于www/views/list.html

您看到的组件来自 Firefox OS 构建块,这些块与用于构建 Firefox OS 本身相同的块。让我们在屏幕底部(</ul></section>下方)添加一个新按钮

Credits

这里重要的是ng-tap属性。当我们点击此项目时,我们将转到/creditsURL,并使用动画popup。有四种内置动画:forwardbackwardpopuppopdown;但是您可以使用简单的 CSS 创建自己的动画

现在,当我们查看它时,它看起来还不像一个按钮,因为我们还没有说明需要按钮构建块。转到css/main.css并添加以下行以使其外观美观

@import url("../components/building-blocks/style/buttons.css");

所有这些都始终记录在构建块网站的页面上。

连接它

但是,当我们点击按钮时,什么也不会发生(好吧,我们会重定向回列表视图),这是因为我们还没有监听/creditsURL。要解决此问题,我们需要创建一个路由处理程序(就像在任何 MV*服务器端框架中一样)。在js/app.js中打开路由列表,并为creditsURL添加一个处理程序(在otherwise处理程序之前)

.when('/credits', {
  templateUrl: 'views/credits.html',
  controller: 'CreditsCtrl'
})

在这里,我们告诉想要咨询哪个控制器(使用 JS 代码)以及哪个视图(使用 HTML)属于该控制器。让我们先创建视图。在views文件夹中添加一个名为credits.html的新文件。

back

Credits

This application is made by {{ name }}.

要设置此视图的样式,我们可以在css/app.css中添加一些内容,例如,添加一些填充并使文本更大

.view.credits {
  padding: 1.5rem;
  font-size: 2rem;
}

现在,编写一个简单的控制器来填充{{ name }}的内容,使用标准的 AngularJS 数据绑定。在www/js/controllers中添加一个名为credits.js的新文件

/* We use RequireJS AMD style modules to get a reference to the app object */
define(['app'], function(app) {
  /* Tell that we're defining a controller with name
    CreditsCtrl, and with dependencies on $scope, we specify this as string
    to not break when minifying
  */
  app.controller('CreditsCtrl', ['$scope',
    /* Implementation. AngularJS does dependency injection to fill the $scope var */
    function CreditsCtrl($scope) {
      /* Data binding to the view */
      $scope.name = 'Your name';
    }
  ]);
});

最后,告诉 RequireJS 我们有一个需要包含在构建中的新 JS 文件,方法是编辑js/main.js并在'js/controllers/edit.js'上方添加一行

'js/controllers/credits.js',

现在,当我们点击应用中的按钮时,一切按预期工作。视图弹出,我们有数据,并且可以通过点击后退按钮将其关闭。同样很棒的是,当您将 URL 发送给其他人(例如http://your/url/index.html#/credits)时,他们默认会转到相同的视图。这是因为我们默认通过 URL 进行正确的状态管理。

与第三方数据源通信

该应用目前仅与静态数据通信,因此我们希望将其连接到真实的数据源。在本例中,项目列表应来自 GitHub 上 mozilla-b2g 的项目页面。他们的 API 位于:https://api.github.com/users/mozilla-b2g/repos

AngularJS 有一个服务的概念,它将数据从控制器中抽象出来。对于此应用,我们有一个数据库服务,该服务目前返回内存中的数据。我们可以修改服务以改为与 Web 服务通信。清除www/js/services/database.js并将其内容替换为

/*global define */
"use strict";
define(['app'], function(app) {
  /* Add a new factory called database, with a dependency on http */
  app.factory('database', ['http', function(http) {
    var getItems = function() {
      /* getItems makes a HTTP get call to github */
      return http.get('https://api.github.com/users/mozilla-b2g/repos', {
        // this is the cache configuration, we want to always cache requests
        // because it gives better UX. Plus when there is no internet, we can
        // get the data from cache and not break for the user...
        idbCache: {
          cacheKey: 'api.index',
          // expiration time in ms. from now (this is 5 minutes)
          // This is only obeyed if there is an internet connection!
          expiresInMs: 5 * 60 * 1000
        }
      }).then(function(res) {
        // Format it, sort it and map it to have the same format as our previous in mem dataset
        return res.data.sort(function(a, b) {
          return a.stargazers_count < b.stargazers_count;
        }).map(function(item) {
          return {
            title: item.name,
            description: item.description,
            id: item.name
          };
        });
      });
    };

    // Similar story but now for just one item
    var getItemById = function(id) {
      return http.get('https://api.github.com/repos/mozilla-b2g/device-flatfish', {
        idbCache: {
          cacheKey: 'api.detail.' + id,
          expiresInMs: 10 * 60 * 1000
        }
      }).then(function(res) {
        var repo = res.data;
        return {
          title: repo.name,
          description: repo.description,
          id: repo.name,
          date: new Date((repo.pushed_at || "").replace(/-/g,"/").replace(/[TZ]/g," "))
        };
      });
    };

    return {
      getItems: getItems,
      getItemById: getItemById
    };
  }]);
});

但是,此 API 现在是异步的,但这对 Angular 来说并不重要。如果将数据绑定到 Promise,则 Angular 将等待 Promise 解析,直到数据绑定发生。

这里的美妙之处在于,即使没有 Internet 连接,数据仍将加载(只要它至少加载过一次),并且数据会自动缓存。控制器无需为此担心。

发布应用

这是我们快速向此应用添加一些功能的两种方法。首先,添加一个新按钮和一个新视图;其次,显示服务器数据的绑定和离线缓存。请注意,此应用模板可用于不仅仅是列表到详情的应用,您拥有 AngularJS 的全部功能!

现在,当我们想要与世界其他地区共享此应用时,我们可以选择两种方法

  • 创建托管应用。这是一个位于您自己的服务器上的应用,就像任何移动网站一样。托管应用仍可以在应用商店中发布,并且可以离线工作,但由于安全限制,无法使用 Firefox OS 中的所有 API
  • 创建打包应用。这是一个 ZIP 文件,类似于 Android 上的 APK 文件,其中包含应用的所有资源,并通过应用商店分发。

这两种应用都可以使用我们的构建脚本生成。该脚本将创建一个名为dist/的新文件夹,其中列出应用需要的所有文件。如果要将应用发布到您自己的服务器,只需复制文件夹的内容即可。如果要将应用发布为打包应用,请将内容压缩为 ZIP 并发布到应用商店。

要构建,请运行

  • 打包:node build.js
  • 托管:node build.js appcache

编码愉快!

关于 Jan Jongboom

Jan Jongboom 是 Telenor Digital 的战略工程师,负责物联网工作。

Jan Jongboom 的更多文章…

关于 Robert Nyman [荣誉编辑]

Mozilla Hacks 的技术布道师和编辑。发表演讲并撰写有关 HTML5、JavaScript 和开放 Web 的博客文章。Robert 是 HTML5 和开放 Web 的坚定支持者,自 1999 年以来一直从事 Web 前端开发工作 - 在瑞典和纽约市。他还定期在 http://robertnyman.com 上撰写博客文章,并且喜欢旅行和结识新朋友。

Robert Nyman [荣誉编辑] 的更多文章…


8 条评论

  1. Fawad Hassan

    不错的教程,但我个人不喜欢对 Firefox OS 应用使用大型框架,因为 Mozilla 目前的目标市场是中低端手机,我总是把性能放在首位。

    出于上述原因,我提倡使用原生 JavaScript 或小型框架/库(如 Backbone、Mithril 等)来开发 Firefox OS 应用。在以下教程中,使用 Web Components 和原生 JavaScript 开发了一个简单的联系人查看器应用。

    http://www.ifadey.com/2014/07/firefox-os-contact-app-using-vanilla-javascript/

    2014 年 7 月 30 日 07:28

    1. Jan Jongboom

      完全正确。但也很容易忽略性能并做一些愚蠢的事情;制作漂亮的视图过渡往往会出错。我们这里提供的应用在最低端的设备上也能快速运行并流畅地进行动画。

      2014 年 7 月 30 日 08:13

  2. Jesus Israel Perales Martinez

    Hola,我使用 jQuery 开发了一个 Firefox OS 应用,因此我决定切换到 Angular,但我在使用 $http 服务时遇到问题,因为在向外部服务器发出请求时,由于 Firefox OS 的安全策略,服务器不允许我这样做。你们知道 B2G 是否会解决这个问题吗?或者您认为 Angular 的开发人员应该修改他们的代码吗?
    更多信息

    https://github.com/angular/angular.js/pull/7903#issuecomment-49966671

    问候。

    PS:这是我的应用
    https://marketplace.firefox.com/app/clima?src=search

    2014 年 7 月 31 日 09:54

    1. Robert Nyman [编辑]

      我的西班牙语水平有限,所以这里我将用英语回复。就此寻求帮助的最佳场所应为 Stack Overflow 上的 firefox-os 标签。祝你好运!

      2014 年 8 月 1 日 02:31

  3. Hu

    我有点跑题了,但我认为如果 Mozilla 能专注于构建一个基于现代 Web 技术的框架,并在所有设备和 Mozilla 产品中保持一致的感觉,那将会很棒。

    例如,Google 正在使用 Polymer 迈出一步:他们在 Polymer 中实现了他们的“Material Design”,开发了一个与 Polymer 配套的视觉 UI 编辑器,并在 Android、他们的 Web 服务(例如新的 GMap 网站)以及很快将在 Chrome OS 和他们的其他产品中实现了它。我们可以感觉到 Google 正在准备一个未来,在这个未来中,Web 应用将成为一等公民,跨平台,并在其所有产品上具有相同的“Material Design”风格。

    在 Mozilla 方面,情况相当不明朗:我们有 Brick,它类似于 Polymer,因为它使用了 Web Components 和现代技术,但它似乎已经停止维护了(没有新闻,没有新组件,没有在 FFOS 上使用的示例,没有设计更新等等…)
    也有一些 Gaia 构建块,但它们不是基于 Web Components 的,也不构成一个真正的框架。
    https://apps.webmaker.org/designer,但这里同样没有使用 Brick 或 Gaia,甚至感觉不像一个 FFOS 应用。

    然后就没有明确的 Mozilla 设计/主题。我在一些 Mozilla 博客上看到过“变色龙”主题,桌面版开发者工具有浅色/深色主题,桌面版的新偏好设置面板也有主题,PDFjs 有旧的拟物化主题,FFOS 有扁平化主题,等等…

    感觉我们没有明确的设计规则或建议使用的框架/UI。确实,我们应该让开发者使用他们自己的框架或设计,但我们也应该构建一个推荐的统一框架,该框架符合 Mozilla 的设计,并且基于最新的、最推荐的 Web 技术。否则,FFOS 最终会变成 Bootstrap/Foundation/React/TopCoat/Gaia/Ionic/KendoUI/Framework7/WinJs 设计风格的大杂烩,这可能会非常难看。

    仅仅“指南”不足以带来良好的用户体验,因为用户体验还涉及组件、手势、动画、过渡等,这些无法在一个 CSS 文件中描述并适应任何底层的 MVC 框架,这就是为什么 Mozilla 开发自己的框架会很棒的原因。

    2014年8月13日 13:21

    1. Robert Nyman [编辑]

      感谢你的想法。那里有很多不同的问题需要解决。首先,我们欢迎 Google 使用 Polymer 的努力,并将其作为开发者的一个选择。但是,同时,我们不希望将开发者的选择限制在一个库或方法上(例如,有一个 Google 的,一个 Mozilla 的等等),而是努力确保所有方法都是可行的。我理解你的意思,这在设计和用户体验方面绝对是需要考虑的事情。

      关于 Brick,2.0 即将推出,你可以阅读 今年 7 月的最新更新

      关于我们产品之间以及它们各自 UI 的主题,我认为这对我们的设计和一致性来说是有效的输入,我将将其转发给我们的创意团队。

      2014年8月14日 03:40

      1. Hu

        非常感谢你的回复和链接。

        2014年8月19日 10:48

        1. Robert Nyman [编辑]

          当然!

          2014年8月19日 12:57

本文评论已关闭。