为 Firefox OS 构建待办事项应用,第二部分

第一部分 中,我们开发了 Firefox OS 待办事项应用的用户界面。在本部分中,我们将使用 Backbone 编写 JavaScript 代码使其具有功能。

待办事项应用的源代码可在 GitHub 上获得

注意:我建议您在继续之前回顾一下 第一部分,因为我在 #view-todos 中为每个待办事项添加了一个编辑按钮,并且按钮 CSS 模块进行了少量更改以使其更易于重用。

所需库

第一步是使用 volo 添加所需的 js 库。在项目的根目录 (/fos-todo-app) 中执行以下所有 volo 命令。

添加 Requirejs/Text

Text 是一个加载程序插件,用于加载文本资源,如模板文件、CSS 等。

volo add requirejs/text

添加 Backbone.js

以下命令将添加 Backbone 及其依赖项,即 Underscore 和 jQuery。

volo add backbone

添加 Zepto.js

正如我在第一部分中提到的,我们将使用 Zepto 而不是 jQuery,因此我们需要添加 Zepto 并用它替换 jQuery。

volo add zepto

现在打开 /fos-todo-app/package.json 文件并从中删除以下行

"jquery": "github:jquery/jquery/2.0.2",

这将从我们的 volo 项目中删除 jQuery 依赖项。现在从 fos-todo-app/www/js/lib 目录中删除 jquery.js 文件。最后,我们需要告诉 Require.js 在请求 jQuery 时加载 Zepto。我们可以通过在 /fos-todo-app/www/js/app.js 中的 Require.js 配置中添加 map 属性来做到这一点。

requirejs.config({
  baseUrl: 'js/lib',

  // Map jquery to zepto
  map: { '*': { 'jquery': 'zepto' } },

  paths: {
    app: '../app'
  }
});

Backbone IndexedDB 适配器

在本教程中,我将使用 superfeedr 的 indexeddb 适配器

volo add superfeedr/indexeddb-backbonejs-adapter/master/backbone-indexeddb.js

添加路径

再次,打开 /fos-todo-app/www/js/app.js 文件以将以下路径添加到 Require.js 配置中。

paths: {
  app: '../app',
  models: '../app/models',
  collections: '../app/collections',
  views: '../app/views',
  templates: '../app/templates',
  db: '../app/db',
}

现在在 www/js/app 目录中创建上面路径中提到的所有目录(模型、集合、视图、模板、db)。此外,删除 www/js/app/main.js 文件,因为我们不会使用它。

注意:对于移动应用,路由并不像 Web 应用那样重要。因此,我们不会在本应用中使用 Backbone 路由。

IndexedDB 配置

www/js/app/db 中创建一个 TodosDB.js 文件,该文件返回一个包含 IndexedDB 相关配置的对象。

define(['backbone', 'backbone-indexeddb'], function (Backbone, idxdb) {
  Backbone.sync = idxdb.sync;

  return {
    id: 'todos-db',
    migrations: [{
      version: 1,
      migrate: function (transaction, next) {
        var store = transaction.db.createObjectStore('todos');
        next();
      }
    }]
  };
});

请注意,Backbone.sync 方法已替换为从 IndexedDB 适配器返回的 sync 方法。现在,它将与本地 IndexedDB 同步,而不是与服务器同步。

返回的对象中的 id 是数据库的唯一 ID。migrations 属性包含 versionmigrate 方法。一旦执行 migrate,数据库版本将更改为指定的值(在我们的例子中为 1)。migrate 方法由驱动程序调用以执行迁移。有关更多详细信息,请参阅 实现 标题下的内容。

待办事项模型

现在创建 www/js/app/models/Todo.js 文件并在其中添加以下代码。

define(['backbone', 'db/TodosDB'],
function (Backbone, TodosDB) {
  var Todo = Backbone.Model.extend({
      database: TodosDB,
      storeName: 'todos',

      defaults: {
        task: 'my task',
        completed: false,
        created_on: new Date()
      },

      validate: function (attrs) {
        if (!attrs.task) {
            return 'Task can't be empty';
        }
      }
  });

  return Todo;
});

在上面的模型中需要注意的只有 databasestoreName 属性。您只需将从 TodosDB 返回的数据库配置分配给 database 属性,而 storeName 是用于此模型对象的存储的名称。请记住,它与数据库配置中定义的唯一 ID 不同。

其余部分都是非常常规的 Backbone 内容。如果您了解 Backbone,则可以轻松理解正在发生的事情。

待办事项集合

现在在 www/app/js/collections/todos.js 中添加以下集合。

define(['backbone', 'models/Todo', 'db/TodosDB'],
function (Backbone, Todo, TodosDB) {
    var Todos = Backbone.Collection.extend({
      database: TodosDB,
      storeName: 'todos',
      model: Todo,
      comparator: function (m) {
        return m.get('created_on').getTime();
      }
    });

    var todos = new Todos();

    //fetch all records from IndexedDB
    todos.fetch();

    return todos;
});

每当模块返回实例时,我都会编写该文件以小写字母开头的名称,例如上面集合的 todos.js

确保在此集合和先前创建的模型中使用相同的 storeName。除此之外,我们希望我们的集合根据创建时间排序,因此添加了 comparator 方法,该方法使用 getTime 方法返回自 1970 年 1 月 1 日以来的毫秒数。

创建集合后,将创建一个新实例,并使用 todos.fetch() 获取所有记录,并返回 todos

待办事项模板

现在创建一个模板文件 www/js/app/templates/todo.htm 并在其中添加以下代码。




这与我们为任务编写的 HTML 代码相同,只有一个区别。添加了 Underscore 模板相关的代码以检查模型的 completed 属性并在 span 元素中打印 task 属性。

显示待办事项列表

现在,我将向您展示每个功能是如何实现的,而不是向您展示每个模块,因此您可能需要在不同的模块/文件之间切换。我们需要做的第一件事是使用 www/js/app/collections/todos.js 集合(从 IndexedDB 中获取)显示任务列表。

待办事项模型视图

要显示待办事项列表,我们首先需要为 www/js/app/models/Todo.js 模型创建一个视图。此视图将列表中的每个待办事项表示为 li 元素。创建一个新文件 www/js/app/views/Todo.js

define(['underscore', 'backbone', 'text!templates/todo.htm'],
function (_, Backbone, todoTempl) {
  var TodoView = Backbone.View.extend({
      tagName: 'li',

      template: _.template(todoTempl),

      //events: {},

      initialize: function () {
        this.$el.attr('role', 'listitem');
      },

      render: function () {
        var self = this;

        //--- render template ---//
        self.$el.html(self.template( self.model.toJSON() ));

        //--- cache DOM elements ---//
        self.$taskEdit = self.$('input[type=text]');
        self.$taskView = self.$('label');
        self.$btnEdit = self.$('.btn.edit');

        return self;
      }
  });

  return TodoView;
});

上面的视图将创建一个包含 todo.htm 模板中所需 HTML 的 li 元素。在渲染时。请注意,在 initialize 方法中,将 listitemrole 应用于 li 元素。此外,在渲染方法中缓存了一些 DOM 元素,因为稍后我们需要重复使用它们。

待办事项集合视图

现在,我们将为待办事项集合创建视图,该视图将表示 section#view-todos 中的 ul#todo-list。此视图将创建一个上面定义的 TodoView 的新实例并将其附加到自身。在 www/js/app/views/Todos.js 中添加以下代码

define(['backbone', 'views/Todo', 'collections/todos'],
function (Backbone, TodoView, todos) {
  var TodosView = Backbone.View.extend({
    tagName: 'ul',
    id: 'todo-list',
    className: 'todo-list reset-list',

    initialize: function () {
      this.collection = todos;

      this.listenTo(this.collection, 'add', this.addTodo);
      this.listenTo(this.collection, 'reset', this.render);
      this.listenTo(this.collection, 'sort', this.render);
    },

    addTodo: function (todo) {
      var todoView = new TodoView({model: todo}).render();
      this.$el.append(todoView.el);
    },

    render: function () {
      this.$el.html('');
      this.collection.each(this.addTodo, this);
      return this;
    }
  });

  return TodosView;
});

initialize 方法中,todos 集合被分配给 this.collection。请记住,此 todos 集合是从 app/collections/todos.js 模块返回的实例。现在,每当我们 require 此模块(在其他模块中)时,它将始终返回集合的相同实例。基本上它充当 单例

addTodo 方法用于通过将 todo 模型作为参数传递给 addTodo 来创建 TodoView (views/Todo.js) 的新实例。然后,它使用链中的 render 方法进行渲染,最后将其附加到 ul 元素。

Do render 方法重复调用 addTodo 以获取 this.collection 中的每个项目。

ViewTodos 视图

现在我们需要创建一个视图来表示 section#view-todos 区域。这是将 TodosView (app/views/Todos.js) 附加到的视图,它包含 footer 中的“添加”和“删除”按钮。为此视图创建 app/views/ViewTodos.js 文件。

define(['backbone', 'views/Todos', 'collections/todos'],
function (Backbone, TodosView, todos) {
  var ViewTodos = Backbone.View.extend({
    el: '#view-todos',

    //events: {},

    setBtnDelDisabled: function () {
      if (todos.length) {
        this.$btnDel.removeAttr('disabled');
      } else {
        this.$btnDel.attr('disabled', 'disabled');
      }
    },

    initialize: function () {
      //init todo list
      this.todosView = new TodosView().render();

      this.listenTo(todos, 'add', this.setBtnDelDisabled);
    },

    render: function () {
      this.$('.view-content').append(this.todosView.el);

      this.$btnDel = this.$('footer .btn.del');

      this.setBtnDelDisabled();
    }
  });

  return ViewTodos;
});

上面的视图不会创建新元素,因为未使用 tagName,而是使用 el 属性来选择现有的 DOM 元素 #view-todos

setBtnDelDisabled 方法非常简单。如果集合中不存在待办事项,它将禁用 this.$btnDel。否则,它通过删除 disabled 属性来启用删除按钮。this.$btnDel 包含 'footer .btn.del' 元素(在 render 方法中缓存)。

initialize 方法中,创建了 TodosView(待办事项列表视图)的新实例,对其进行了渲染,并将其分配给 this.todosView。然后 this 视图 (ViewTodos) 开始侦听 todos 集合上的 add 事件。每当在 todos 集合中添加新项目时,都会触发 setBtnDelDisabled

render 方法中,todosView 集合被附加到当前视图 (#view-todos)。然后 .btn.del$btnDel 中被缓存。最后,调用 setBtnDelDisabled 以根据 todos 集合中的项目数量启用或禁用 $btnDel

引导

现在我们需要创建一个 ViewTodos 的新实例并在 app.js 文件中对其进行渲染。将 requirejs(['app/main']); 语句替换为以下代码。

requirejs(['views/ViewTodos', 'collections/todos', 'zepto'], function (ViewTodos, todos, $) {
  new ViewTodos().render();

  //--- for testing only ---//
  window.todos = todos; //todos collection
  window.$ = $;         //zepto
});

todos 集合被设置为全局变量,以便您可以轻松测试您的应用是否成功地在该集合上执行操作(读取、添加、编辑、删除)。

测试

现在,在向我们的应用添加任何新功能之前,我们需要确保我们的代码在控制台中没有错误地运行。如果您在 Firefox 浏览器中进行测试,请确保使用某些 Web 服务器 (http://localhost/fos-todo-app) 运行它,因为在您尝试使用 file:/// 时,IndexedDB 会报错。

注意:我注意到,如果在“隐私”中将历史记录设置为“Firefox 将:记住历史记录”,则 Firefox 会抛出 IndexedDB InvalidStateError。因此,请确保在测试之前设置“记住历史记录”。这在 Chrome 中不会发生。此外,IndexedDB 在隐私浏览窗口中也会出现相同的错误。

在我的控制台中,当我第一次运行测试时,得到了以下输出。

opening database todos-db in version #1
onupgradeneeded = 0 => 1
migrate begin version from #0
migrate begin before version #1
migrate done before version #1
migrate begin migrate version #1
migrate done migrate version #1
migrate begin after version #1
migrate done after version #1
Migrated to 1
migrate setting transaction.oncomplete to finish  version #1
migrate done transaction.oncomplete version #1
Done migrating
execute : read on todos for undefined

只有当版本号与 app/db/TodosDB.js 中定义的版本号不同时,您才会获得上述输出,否则您会在控制台中看到一条读取消息。

添加待办事项

让我们在实现编辑、删除或标记为已完成功能之前先实现添加待办事项功能,因为通过添加待办事项,我们可以正确测试从 IndexedDB 添加和获取待办事项列表的功能。

ViewAdd 视图

在实现此视图之前,让我们回顾一下我们想要在其中实现的功能。

Firefox OS Todo App Add View

我们希望通过使用 CSS3 动画打开此视图。很明显,我们希望此视图在点击页脚中的“完成”按钮时在 todos 集合中添加一个新的待办事项。但最重要的是,在用户在文本框中输入一些文本之前,保持“完成”按钮处于禁用状态。当待办事项成功添加到 todos 集合并保存在 IndexedDB 中时,我们希望使用 CSS3 动画关闭此视图,并在动画结束时使用 aria-hidden 属性将其隐藏。类似地,页脚中的取消(叉号)按钮将执行相同的功能,但不会保存待办事项。

现在创建一个新的视图文件 app/views/ViewAdd.js,它将处理 section#view-add 区域。

define(['backbone', 'views/Todos', 'collections/todos'],
function (Backbone, TodosView, todos) {
  var ViewAdd = Backbone.View.extend({
    el: '#view-add',

    //--- Replace click with tap before creating final build ---//
    events: {
      //we assigned id to .btn.done so why not select it using that
      'click #btn-add-done' : 'addTodo',
      //'tap #btn-add-done' : 'addTodo',

      'click .btn.del'      : 'cancel',
      //'tap .btn.del' : 'cancel',

      'keyup #task'         : 'setAddBtnDisabled',
      'keypress #task'      : 'addTodo'
    },

    setAddBtnDisabled: function () {
      var taskLen = this.$task.val().length;

      taskLen ? this.$btnAdd.removeAttr('disabled')
              : this.$btnAdd.attr('disabled', 'disabled');
    },

    addTodo: function (e) {
      var task = this.$task.val();

      console.log('ViewAdd:addTodo');

      //if Done button is clicked or Enter key is pressed and
      //task must have length greater than 0
      if ((e.type === 'click' || e.keyCode === 13) && task.length) {
        todos.create({task: task, created_on: new Date()});
        this.cancel();
        return false;
      }

    },

    cancel: function () {
      this.$el.removeClass('slide-up-in').addClass('slide-down-out');
    },

    hideView: function (e) {
      var $target = $(e.target);

      if (e.animationName === 'slide-down-out') {
        $target.attr('aria-hidden', 'true');
      }
    },

    initialize: function () {
      this.$task = this.$('#task');
      this.$btnAdd = this.$('#btn-add-done');
      this.$el.on('animationend', this.hideView);
    },

    render: function () {
      this.$el.removeAttr('aria-hidden').removeClass('slide-down-out').addClass('slide-up-in');
      this.$task.val('');
      this.$btnAdd.attr('disabled', 'disabled');
      return this;
    }
  });

  return ViewAdd;
});

initialize 方法中,缓存了 #task 文本框和 #btn-add-done。在第三行,将 animationend 事件附加到当前视图的 DOM 元素 (#view-add)。每当 ViewAdd 上的动画结束时,它将触发 hideView 方法。

hideView 方法仅在 slide-down-out 动画结束时才使用 aria-hidden 隐藏视图,因为我们不希望在打开 (slide-up-in) 动画时隐藏视图。

render 方法中,通过删除 aria-hiddenslide-down-out 类并添加 slide-up-in 类来使视图可见,以通过对其进行动画处理来显示视图。然后清除 #task 中的文本,并禁用 $btnAdd,因为我们希望用户在 #task 中输入一些内容。

cancel 方法是当用户点击/点击 .btn.del 时触发的事件处理程序。此方法使用动画类关闭视图。

setAddBtnDisabled 处理程序在 keyup 事件上触发。此方法仅根据 #task 中的字符长度启用/禁用 $btnAdd。使用 keyup 事件是因为我们需要在用户释放键后获取字符串长度。

addTodo 处理程序在两个事件 ('click #btn-add-done''keypress #task') 上触发。在此方法中,仅当用户点击了 #btn-add-done 或按下了 Enter 键并且用户必须在 #task 中输入了一些文本时,才会保存新任务。在这种情况下,将在 todos 集合中添加新任务,并调用 cancel 方法以隐藏 #view-add

ViewAdd 渲染

如前所述,我们不会在我们的应用中使用路由。因此,我们需要在用户点击 ViewTodos 页脚中的添加按钮时从 ViewTodos 启动/渲染 ViewAdd。为此,通过将其添加到作为 ViewTodosdefine 的第一个参数传递的数组中来加载 ViewAdd

define(['backbone', 'views/Todos', 'views/ViewAdd', 'collections/todos'],
function (Backbone, TodosView, ViewAdd, todos) {
  //body...
});

然后取消 ViewTodosevents 属性的注释并在其中添加点击事件处理程序。

events: {
  //'tap footer .btn.add' : 'showAddView'
  'click footer .btn.add' : 'showAddView'
},

现在添加 showAddView 处理程序,它将渲染 ViewAdd

showAddView: function () {
  console.log('showAddView');

  this.viewAdd = new ViewAdd().render();

  //Redefine showAddView
  this.showAddView = function () {
    this.viewAdd.render();
  };

  //Remove existing events and reattach then
  //using this.events hash
  this.delegateEvents();
},

上面的视图创建了 ViewAdd 的一个新实例,调用 render 方法并将 ViewAdd 的实例分配给 this.viewAdd。然后它重新定义自身 (showAddView),其中仅调用 render 方法而无需创建其新实例。最后,修改后的方法需要重新绑定,这可以通过 Backbone 的 delegateEvents 方法来完成。

测试

现在尝试使用 ViewAdd 添加一些待办事项,并使用 console.log(todos.toJSON()) 在控制台中测试 todos 集合。

todos collection

注意:我注意到 Ubuntu 上的 Firefox 21 中存在一个错误。当渲染 ViewAdd (#view-add) 时,固定区域/视图中的固定 footer 无法正确动画处理。此外,有时它不会在 ViewAdd 中显示 footer。相同的代码在 Firefox OS 模拟器中运行良好。

编辑待办事项

现在让我们使编辑功能正常工作,但首先回顾一下它将如何工作。当用户点击每个待办事项旁边的编辑图标时,将出现一个文本框,显示与非编辑模式下显示的相同的待办事项文本,如下所示。

Todo Edit Mode

用户现在可以编辑文本并点击/敲击键盘上的 Enter 键以保存更改,或按 Esc 键以撤消更改并退出编辑模式。当然,Esc 键功能对手机用户没有用,因为 Firefox OS 屏幕键盘上没有 Esc 键。

我们将在 app/views/Todo.js 中实现此功能,因为 Todo 视图表示待办事项列表中的每个项目。在 Todo 视图中为编辑按钮的 click 和文本框的 keypress 添加事件。

events: {
  //'tap label'                      : 'editTodo',
  'click .btn.edit'                : 'editTodo',

  'keypress input[type=text]'      : 'saveTodo'
},

现在在 Todo 视图中添加 editTodo 方法。

editTodo: function () {
  var self = this;
  console.log('editTodo');

  self.$taskEdit.val(self.$taskView.find('span').text());

  self.$btnEdit.attr('aria-hidden', 'true');

  self.$taskEdit
      .removeAttr('aria-hidden')
      .removeClass('slide-left-out')
      .addClass('slide-right-in')
      .focus();

  self.$taskView.attr('aria-hidden', 'true');

  return false;
},

在进入编辑模式之前,span中的文本会被复制到文本框中。然后隐藏编辑按钮($btnEdit)。使用CSS3动画使文本框($taskEdit)可见,并使其获得焦点,以便用户可以直接编辑。最后,我们需要隐藏$taskView,以便用户只看到$taskEdit。现在用户处于编辑模式,可以编辑任务文本。

但是,当用户点击/按下回车键并返回查看模式时,我们也需要保存更改。类似地,按Esc键退出编辑模式而不保存更改。添加saveTodo处理程序来执行这些操作。

saveTodo: function (e) {
  var self = this;

  //save in case of enter/return
  if (e.keyCode === 13) {
    console.log('enter');
    self.$taskView.find('span').text(self.$taskEdit.val());

    self.model
        .set({
          task: self.$taskEdit.val()
        })
        .save();
  }

  //27 is for escape key
  if (e.keyCode === 13 || e.keyCode === 27) {
    self.$taskEdit
        .removeClass('slide-right-in')
        .addClass('slide-left-out');

    self.$btnEdit.removeAttr('aria-hidden');
  }
},

saveTodo事件处理程序非常简单。如果按回车键,则将文本从文本框复制到span并保存在模型中。如果按回车键或Esc键,则隐藏文本框($taskEdit)并显示编辑按钮($btnEdit)。但是您可能想知道为什么不显示$taskView?是的,我们需要在$taskEdit动画完成后显示它。为此,请在render方法中的return false;之前添加以下事件处理程序。

self.$taskEdit
    .on('animationend', function (e) {
      if (e.animationName === 'slide-left-out') {
        self.$taskEdit.attr('aria-hidden', 'true');
        self.$taskView.removeAttr('aria-hidden');
      }
    });

测试

尝试编辑不同的待办事项,并尝试刷新应用程序(在Firefox和Firefox OS模拟器中),以验证它是否保存在IndexedDB中。如果您正在测试Firefox浏览器,请记住强制刷新有时无法加载使用Require.js修改后的文件。

标记待办事项为已完成

我们还将在Todo视图中实现“标记为已完成”功能,我们在其中实现了编辑功能。首先,在复选框上添加一个事件处理程序,该处理程序将待办事项标记为已完成。

//'tap input[type=checkbox]'   : 'markCompleted',
'click input[type=checkbox]' : 'markCompleted',

现在在Todo视图中添加markCompleted处理程序。

markCompleted: function (e) {
  var isCompleted = !this.model.get('completed');
  console.log('markCompleted');

  this.model
      .set({
        completed: isCompleted
      })
      .save();
},

在这里,completed属性被反转并再次保存在模型中。

测试

尝试将不同的待办事项标记为已完成或未完成。然后在控制台中尝试todos.toJSON(),以确保它正在更改completed标志。

删除待办事项

我们将以动态的方式实现删除,为用户提供更多可能性。以下是删除的工作原理。如果用户将一个或多个待办事项标记为已完成,则删除按钮将充当“删除已完成项”,否则将充当“删除所有项”。

其实现将跨越多个文件。首先打开ViewTodos并为删除按钮添加一个处理程序。

//'tap footer .btn.del' : 'delTodos',
'click footer .btn.del' : 'delTodos',

添加delTodos处理程序。

delTodos: function () {
  /**
   * Other possible solution for following if condition is
   * this.$('#todo-list input[type=checkbox]:checked').length
   * But I didn't used it just to avoid DOM access
   */

  if (todos.where({completed: true}).length) {
    //function as Delete Completed

    if (confirm('Delete Completed Tasks?')) {
      this.todosView.delCompleted();
    }
  } else {
    if (confirm('Delete All Tasks?')) {
      //function as Delete All
      this.todosView.delAll();
    }
  }

  this.setBtnDelDisabled();

},

在此处理程序中,使用Backbone的where方法检查已完成的待办事项总数。如果有已完成的项,它将调用TodosViewdelCompleted方法(我们将在稍后实现)。否则,它将调用delAll方法。最后,调用setBtnDelDisabled,这在本教程中已讨论过。

现在打开app/view/Todos.js并在其中添加delCompleted方法。

delCompleted: function () {
  this.collection.remove(
    this.collection.where({completed: true})
  );
},

上述方法简单地删除了所有completed属性为true的模型。现在在同一视图中添加delAll方法。

delAll: function () {
  /**
   * We can remove all models using reset
   * this.collection.reset([]);
   *
   * But I used remove method because I want remove
   * event to get fired for each model
   */

  this.collection.remove(this.collection.models);
},

delAll简单地从集合中删除所有模型。

从DOM中删除

我们需要做的最后一件事是从其模型被删除的相应DOM元素中删除。在app/views/Todo.js视图的initialize方法中添加以下语句。

this.listenTo(this.model, 'remove', this.destroy);

Todo视图将开始监听其modelremove事件。如果模型触发remove事件,则将调用destroy方法。因此,在同一视图中添加destroy方法。

destroy: function () {
  this.$el.remove();
  this.model.destroy();
},

它只是从DOM中删除自身,并且destroy用于将其从IndexedDB中删除。

测试

尝试使用删除按钮删除一些待办事项。测试两种情况。添加一些待办事项并将其中一些标记为已完成。然后尝试使用删除按钮并确保通过刷新应用程序删除了这些项。类似地,尝试删除所有情况。不要将任何项目标记为已完成,然后按删除按钮。它必须删除列表中的所有项目。

添加/选择联系人Web活动

应用程序几乎完成了。我们已经实现了所有CRUD操作。最后我想向您展示的是Web活动。我们希望为用户提供一个选项,以便在ViewAdd#view-add)中的文本框中插入联系人信息。当用户点击ViewAdd中的插入联系人链接时,将触发Web联系人pick活动,因此我们需要为插入联系人链接添加一个事件处理程序。

'click #activities .add-contact' : 'addContact'

现在添加addContact处理程序。

addContact: function () {
  var self = this,
      pick = new MozActivity({
        name: 'pick',
        data: {
          type: 'webcontacts/contact'
        
}
      });

  pick.onsuccess = function () {

    var res = this.result;
    self.$task.val(self.$task.val() + res.name[0] + '(' + res.number + ')');
  };

  pick.onerror = function () {

    alert('ERROR: Unable to add contact!');
  };

  return false;
},

在上面的处理程序中,使用MozActivity触发pick活动。如果有多个应用程序可以处理联系人类型,则列表中将显示所有可以处理类型webcontacts的应用程序。如果成功选择了联系人,则将触发onsuccess处理程序,并且联系人信息将存储在this.result中。从result中,名称和号码信息将附加到#task文本框中。

测试

您无法在Firefox浏览器中测试此功能。您需要Firefox OS模拟器或一部真机才能测试它。

就是这样。我希望您喜欢本教程。请在评论中提供反馈。

关于 Fawad Hassan

我是一名JavaScript开发者,我喜欢处理单页应用程序、Firefox OS、基于PhoneGap的移动应用程序以及Node.js/Express.js后端。目前我在PLUMgrid(SDN相关的创业公司)工作,并且正在开发基于HTML5的网络操作系统GUI。除此之外,我还是Mozilla的支持者,在我的空闲时间里,我会推广Mozilla的产品。

更多Fawad Hassan的文章…

关于 Robert Nyman [荣誉编辑]

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

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


6条评论

  1. David R

    我无法下载“Firefox OS模拟器”,因为“Windows下载”按钮被禁用了(显示为灰色)。

    2013年7月5日 04:34

    1. Robert Nyman [编辑]

      您使用的是哪个版本的Firefox?它是一个Firefox扩展程序,可在较新版本中使用。

      2013年7月5日 05:27

  2. surkaiser

    您好,我非常感谢这篇文章,但我有一些问题……

    如何为Firefox OS应用程序使用数据库?……我了解一点IndexedDB……但实际上我不知道这是否是解决方案。

    你能帮我吗?

    谢谢!

    2013年7月10日 18:05

    1. Fawad Hassan

      您的意思是您想使用服务器端数据库还是客户端数据库?最常用的客户端存储(除了IndexedDB之外)是DOM存储。

      https://mdn.org.cn/en-US/docs/Web/Guide/DOM/Storage

      如果您想使用服务器端数据库(如MySQL、MongoDB等),则需要使用某种服务器端语言,例如PHP、Java、Ruby、Python或Node.js等。

      2013年7月11日 04:11

      1. surkaiser

        感谢您的回复!

        您的意思是可以用Firefox OS使用服务器端数据库?…

        2013年7月12日 13:48

        1. Fawad Hassan

          是的,您可以使用在常规Web开发中使用的任何东西。您可以使用Ajax调用到服务器端代码(该代码进一步与数据库通信)将记录存储在服务器端数据库中。就像常规Web应用程序一样。

          2013年7月13日 06:58

本文的评论已关闭。