在Backbone js中永久引用另一条数据

Permanently reference a piece of data from another in Backbone js

我正在使用 Backbone 创建一个网站,用户可以在其中与内容(书签、星标等)进行交互,我想要一种方法来存储用户在用户数据中交互的内容。很简单,但我正在寻找有关最佳方法的信息。

我构建了一个没有用户帐户的原型(数据加载到 localStorage),因此内容本身现在存储用户交互(在该内容的元数据中),但要将其开放给多个用户有一次,我想更改它,以便一旦用户保存了一段内容,该内容的 ID(或永久性的东西)就会保存到用户帐户数据中。

我目前正在使用 localStorage 适配器并在页面加载时加载内容,但是对于 Backbone,模型的 ID 每次都会更改。一旦我移动到带有用户帐户的 REST 数据库,我不确定如何将用户与之交互的内容保存在他们的帐户详细信息对象中,并能够在 UI.[= 中可靠地使用它11=]

任何帮助我指明正确方向的人都会很棒。其他人如何解决这个问题?自定义uid?来自数据库的ID?

谢谢,

保罗

更新: 在与 OP 的私下对话中,他解释说他需要在各种设备中使用多个站点实例来显示登录用户(即用户)的一致用户数据首选项应反映在所有设备上)。为了在客户端和服务器之间保持新鲜的 link,OP 将使用 socket.io。

解决方案概述

在这种情况下,基本上会传递三种类型的数据。将显示为单独视图的站点内容数据。用于识别用户的用户配置文件数据;基本上是一个唯一的用户 ID (UID)。最后,数据反映了用户收藏(加星标)的内容。

面临的挑战是在给定用户的所有并发 [​​=73=] 中保持用户选择数据最新。我将在下面描述的想法将解释如何在所有三个数据源之间建立通信 link,以便动态数据、用户选择可以持久保存并立即刷新。为此,我们将使用 Backbone 的事件聚合器,以及 Backbone 视图和模型提供的封装。

数据库结构

反映三组数据,数据库应该设计三个table:一个User Profiletable,一个Contenttable,和 User Content 查找 table。架构应如下所示,

                     User Profile                         Content
       ---------------------------------------   --------------------------
       | UID | Username | First | Last | ... |   | ID | Title| Date | ... |
       ---------------------------------------   --------------------------

                                User Content Lookup 
                                   ------------
                                   | UID | ID | 
                                   ------------

Front-end设计

我们必须设置三种类型的 Backbone object 来处理数据 round-robin。实例化我们的用户偏好的用户视图 object。每个内容项目的视图以处理事件委托和项目呈现。还有一个 Backbone collection object 将充当控制器,具有持久化和获取数据的逻辑。为了让每个人都能畅所欲言,我们将安装一个具有本地范围的事件聚合器,所有这些视图都可以订阅和发布。

用户和内容浏览量

用户视图 是普通的Backbone 视图。从图形上看,它仅提供 log-in/log-out、帐户资料等 link。它也是用户配置文件数据的存储库,它存储它的模型。我们关心的是它 1. 具有用户的唯一 ID,并且 2. 将使用该 UID 实例化控制器 object。

// We set up the user model to initialize the 
// controller when the data has been fetched
var UserModel = Backbone.Model.extend({
  initialize: function(options) {
    this.controller= options.controller; // A reference to the controller is passed
                                         // in before we instantiate the model
    _.bindAll(this, "initializeController"); // Ensures proper context
    this.listenTo(this, "add", this.initializeController);
  },

  initializeController: function () {
    this.controller.fetch({ id: this.get('UID') });
  },
}); 

// We fetch the user data, which, when it comes in, initialized the
// user's ID
var usrModel = new UserModel({ controller: appController });
usrModel.fetch();

每个 content 行都有自己的视图。这允许在用户与该内容项交互时进行简单的事件管理。每个视图都会将自己的 ID 连同描述交互的事件一起发送到控制器。相反,如果用户在单独的并发 session 中选择或取消选择视图,控制器可以发送视图事件提醒它。

// We wire the star event
initialize: function() {
  _.bindAll(this, "checkItem", "uncheckItem", "onClickStar");
  // Listen to items that have changed in a concurrent session
  this.listenTo(App.vent, "new:item:selected", this.checkItem);
  this.listenTo(App.vent, "new:item:unselected", this.uncheckItem);
},

events: {
  "click .star": "onClickStar"
},

onClickStar: function () {
  // we check if the star has been checked or unchecked
  // Here you can roll your own, depending on your implementation (e.g. checkbox)
  if (checked)
   App.vent.trigger("item:selected", this.model.get('ID'));
  else
   App.vent.trigger("item:unselected", this.model.get('ID'));
},

checkItem: function (id) {
  if (this.model.get('ID') == id)
    // find the check item and check it (without using click)
},

uncheckItem: function (id) {
  if (this.model.get('ID') == id)
    // find the check item and uncheck it (without using click)
}

关键是在触发器上发送 ID,因此侦听触发器的控制器知道 add/remove

控制器

控制器本身就是操作的大脑。这是我们订阅内容项更改,并在后端传播这些更改,我们在后端侦听更改并将它们发布到视图。这里也是我们设置与内容事件交互的 socket.io 绑定的地方。

// Let's define the controller collection object
// The controller has to defined AND instantitated BEFORE 
// the user model object, so that it can pass a reference to
// itself to the user model
var Controller = Backbone.Collection.extend({
  initialize: function () {
    _.bindAll(this, "socketIni");
    this.socketIni();
  },

  // We override fetch to pass in the id we need for the fetch route
  fetch: function(options) {
    this.url = "/user/" + options.id + "/content";
    return Backbone.Collection.prototype.fetch.call(this, options);
  },

  socketIni: function() {
    var socket = io();
    this.listenTo(App.vent, "item:selected", function (id) {
      // When a check is added, socket emits the id and event
      socket.emit('item added', id);
    });
    this.listenTo(App.vent, "item:unselected", function (id) {
      // When a check is removed, socket emits the id and event
      socket.emit('item removed', id);
    });

    // Set up socket to let the content views when something changes in the db
    socket.on('new item selected', function(id) {
      App.vent.trigger('new:item:selected', id);
    });
    socket.on('new item unselected', function(id) {
      App.vent.trigger('new:item:unselected', id);
    });
  },
});

// We instantiate the controller that we passed in to usrModel
// REMEMBER: This must be done BEFORE you construct userModel.
vet appController = new Controller();

上面的代码可能并不完美,但它演示了如何在多个并发 session 中保持大量视图新鲜的基本思想。