1

I am using the Meteor reactive framework to allow a user to edit a text box in a web gui, update the database on any changes on the text box, and update the textbox with updates from the database.

This creates a dependency loop, and when I type fast, the latency between the updates destroys text written by the user.

My thought of how to alleviate this is to temporarily pause updates from the database to any object that the user has focused on.

I have tried numerous ways to do this. Here is my template:

<template name="valueEditor">
  <div class="list-item {{editingClass}}">
    <input type="text" value="{{value}}" placeholder="value">
  </div>
</template>

Here are the helpers:

Template.valueEditor.helpers({
  value : function(){
    var state = ! Session.equals(EDITING_KEY, this._id);
    console.log("reactive state = " + state)
    var result = Objects.find({_id:this._id},{reactive:state}).fetch()[0].value;
    console.log("Database is emitting '" + result + "'back to the UI input!!!")
    return result;
  });

Here are the events:

Template.valueEditor.events({

  'keydown input[type=text]': function(event) {
    console.log("You've pressed = " + String.fromCharCode(event.which));
    if (event.which === 27 || event.which === 13) {
      event.preventDefault();
      event.target.blur();
    }
  },

  'keyup input[type=text]': _.throttle(function(event) {
    console.log("saving '" + event.target.value + "' to database.");
    Objects.update(this._id, {$set: {value: event.target.value}});
  }, 300)

)};

Here is the output (when I type fast):

"You've pressed = T"
"You've pressed = E" 
"You've pressed = S"
"saving 'tes' to database."
"You've pressed = T"
"You've pressed = I"
"reactive state = false"
"Database is emitting 'tes'back to the UI input!!!"
"You've pressed = N"
"saving 'tesn' to database."
"You've pressed = G"
"You've pressed =  "
"reactive state = false"
"Database is emitting 'tesn'back to the UI input!!!"
"saving 'tesn' to database."

How can I make it so the database doesn't overwrite the text that I intended to type in the input box???

Matthew Beck
  • 451
  • 5
  • 19

3 Answers3

1

I found out I could get the element to stop updating from the database by sending back to the html element what's already in the html element when a user is editing it. Here's the code:

Template.objectTemplate.events({
  'focus input[type=text]': function(event) {
    Session.set(EDITING_VALUE, event.target.value);
    Session.set(EDITING_KEY, this._id);
  },
  'blur input[type=text]': function(event){
    if (Session.equals(EDITING_KEY, this._id))
    {
      Session.set(EDITING_KEY, null);
      Session.set(EDITING_VALUE, "");
    }
  },
  'keydown input[type=text]': function(event) {
    if (event.which === 27 || event.which === 13) {
      event.preventDefault();
      event.target.blur();
    }
  },
  'keyup input[type=text]': _.throttle(function(event) {
    Objects.update(this._id, {$set: {value: event.target.value}});
  }, 300),
});

Template.objectTemplate.helpers({
  value : function(){
    var x;
    if (Session.equals(EDITING_KEY, this._id))
      x = Session.get(EDITING_VALUE);
    else
      x = this.value;
    return x
  }
Matthew Beck
  • 451
  • 5
  • 19
0

Inside your second keypress event, do a timeout and only update the database every x seconds or more.

Adnan Y
  • 2,982
  • 1
  • 26
  • 29
  • I want the database to be able to update mid-typing. Is there a way to temporarily turn off the reactivity of that UI element? – Matthew Beck Apr 23 '15 at 12:34
  • Plus, if I do it the way that you mentioned, someone could type slow then fast, and still see the bug. I want the bug to be completely gone. – Matthew Beck Apr 23 '15 at 12:36
0

A solution seems to be in either Tracker.nonreactive(func) (docs) or some flavor of Collection.find({}, {reactive: false});.

There's a discussion of the same question here at the Meteor.com forums and a quick one here.

When I understand better (maybe by looking into the Tracker Manual?), I'll update this answer...

Community
  • 1
  • 1
Mallory-Erik
  • 1,750
  • 1
  • 19
  • 24