在 Meteor 中复制文本?

Text Duplicating in Meteor?

我正在尝试制作一个允许多人同时编辑文本正文的 Meteor 应用程序(想想 Google Docs/Drive)。

我想为了做到这一点,我需要一个模板,它只显示数据库中当前的文本,然后每当修改文本时,它需要更新数据库中的文本。

我能够在下面这个最小复制中重现我的完整应用程序中的相同问题(设计供单个用户而不是多个用户使用,因此从使用 Mongo 集合切换到会话。 )

<body>
  {{> hello}}
</body>

<template name="hello">
  <pre contentEditable="true">{{text}}</pre>
</template>

(如果我使用 div 而不是 pre,则会发生完全相同的问题。)

if (Meteor.isClient) {
  Session.setDefault('text', "Edit me!");

  Template.hello.helpers({
    text: function () {
      return Session.get('text');
    }
  });

  Template.hello.events({
    "input pre": function (event) {
      Session.set('text', $(event.target).text());
    }
  });
}

尝试在应用程序中输入一些内容,您应该很快就会发现错误:每次击键它都会获取所有现有文本并将其附加到您输入的内容中(因此每次击键都会重复所有文本)。这是真正奇怪的部分:这种行为并不总是立即开始......事实上,我还没有找到任何特别可靠的重现它的方法。一旦它复制了一次文本,它就会可靠地一次又一次地重复每次击键,直到您刷新页面。刷新页面后,有时该错误会在您下次击键时再次出现,有时可能需要约 20 次击键才会出现。

我已经在 Safari 8(OS X 和 iOS)上测试过,Chrome(OS X 和 Windows),和 Firefox(仅 OS X)并且该问题出现在每个浏览器中。

如果您还不能重现它,请尝试突出显示所有文本,将其删除,然后键入。也尝试开始一个新行。我发现这些操作似乎更有可能引发文本重复,但即使是那些操作也不会始终引发问题。

我的问题是:

  1. 为什么会出现这个错误?
  2. 我怎样才能阻止它发生?

如果你想直接看到问题而不必 运行 流星服务器(虽然我已经给了你所有的代码......)我把它扔了 here.

我认为这是因为您正在捕获所有事件,而不是将其缩小到特定事件类型。

我刚刚在 MeteorPad 上试了几分钟,它从未重复文本:

/main.html

<body>
  {{> hello}}
</body>

<template name="hello">
  <pre contentEditable="true">{{text}}</pre>
</template>

/client/app.js

if (Meteor.isClient) {
  Session.setDefault('text', "Edit me!");

  Template.hello.helpers({
    text: function () {
      return Session.get('text');
    }
  });

  Template.hello.events({
    "keyup pre": function (event) {
      $(event.target).text("I was called");
      Session.set('text', $(event.target).text());
    }
  });
}

这感觉和看起来都像是 hack...但它比原始代码稍微好一点,所以我会把它作为答案分享:

在每个 input 之后,将新内容插入后备存储后,使用 $(event.target).contents().remove(); 清除字段。

示例:

Template.hello.events({
  "input pre": function (event) {
    Session.set('text', $(event.target).text());
    $(event.target).contents().remove();
  }
});

注意事项:

  1. 每次击键后文本都会暂时消失(有时它是如此简短,您甚至都不会注意到它。)
  2. 每次键入时,光标都会跳回到文本的开头。 (但光标在原始代码中也有跳跃的趋势。)

这两个警告都非常糟糕...基本上所有发生的事情都是我交换了出现的重复文本,让文本暂时消失。

一个真正的解决方案是,如果有某种我喜欢的方式,挂接到空格键或类似的东西......比如,我需要一些代码在空格键响应更新后立即运行。或者,如果我可以修改空格键的反应更新方式。也许我会查看 Spacebars 上的文档 and/or 源代码,并根据我发现的内容提交错误报告或功能请求...

这是一个已知问题,详细讨论了 here. I am using this solution(来自 Swavek)并且效果很好。

简而言之,当两个人同时操作相同的 DOM 个元素时,问题就出现了:

  1. 流星,虽然有反应
  2. 负责管理contenteditable里面的浏览器代码div。

解决方案是告诉 Meteor 不要操作 contenteditable div 的内部,而是刷新整个 div。你这样做:

<body>
  {{> hello}}
</body>

<template name="hello">
  {{{getContenteditableDiv}}}  <!-- Beware: triple brackets! -->
</template>

Template.hello.helpers({
  getContenteditableDiv: function() {
    return '<pre contentEditable="true">' + Session.get('text') + '</pre>';
  }
});