Meteor JS: How do I insert a document into a collection, but only on the client-side?

Viewed 1587

I'm new to Meteor and building a simple app to learn the framework. The app I'm building let's you place words on an image of a kitten.

The desired behavior is this:

A user clicks anywhere on the kitten, and a contenteditable element appears letting the user enter text. Clicking outside of the element saves the element, and it remains in place.

The problem I'm running into:

If I have two browser windows opened with the app, and I click on one kitten in one window, an empty field appears in both windows. Ideally, the empty field would appear only on the window I clicked on. Once a word is saved, then in should be visible in both windows.

My question:

Is there a way to insert a document into a collection on the client-side only, and then use upsert later to add the document to servert-side collection?

Here's what I tried:

I created a stub method that exists only on the client-side for inserting the document. The problem with this, is that when I click on the image, an empty field appears for a split-second, and then disappears again.

Here's the code:

image-tags.js

if (Meteor.isClient) {
  var isEditing;

  Template.image.image_source = function () {
    return "http://placekitten.com/g/800/600";
  };

  Template.tag.rendered = function(){
    var tag = this.find('.tag');
    if (isEditing && !tag.innerText) {
      tag.focus();
    }
  }

  Template.image.events({
    'click img' : function (e) {
      if (isEditing) {
        isEditing = false;
      } else {
        isEditing = true;
        var mouseX = e.offsetX;
        var mouseY = e.offsetY;

        // Tags.insert({x:mouseX, y:mouseY});

        // Insert tag on the client-side only.
        // Upsert later when the field is not empty.
        Meteor.call('insertTag', {x:mouseX, y:mouseY});
      }
    },

    'click .tag' : function (e) {
      isEditing = true;
    },

    'blur .tag' : function (e) {
      var currentTagId = this._id;
      var text = e.target.innerText;

      if(text) {
        Tags.upsert(currentTagId, {$set: {name: text}});
      } else {
        Tags.remove(currentTagId);
      }
    }
  });

  Template.image.helpers({
    tags: function() {
      return Tags.find();
    }
  });

  // Define methods for the collections
  Meteor.methods({
    insertTag: function(attr) {
      Tags.insert({x:attr.x, y:attr.y});
    }
  });
}

// Collections
Tags = new Meteor.Collection('tags');

image-tags.html

<head>
  <title>Image Tagger</title>
</head>

<body>
  {{> image}}
</body>

<template name="image">
  <figure>
    <img src="{{image_source}}" />
    <figcaption class="tags">
        {{#each tags}}
          {{> tag}}
        {{/each}}
      </figcaption>
  </figure>
</template>


<template name="tag">
  <div class="tag" contenteditable style="left: {{x}}px; top: {{y}}px;">
    {{name}}
  </div>
</template>
2 Answers
Related