Meteor JS: Как мне вставить документ в коллекцию, но только на стороне клиента?

Я новичок в Meteor и создаю простое приложение для изучения фреймворка. Приложение, которое я создаю, позволяет размещать слова на изображении котенка.

Желаемое поведение:

Пользователь щелкает в любом месте котенка, и появляется элемент contenteditable, позволяющий пользователю вводить текст. Щелчок за пределами элемента сохраняет элемент и остается на месте.

Проблема, с которой я столкнулся:

Если у меня открыто два окна браузера с приложением, и я нажимаю на одного котенка в одном окне, в обоих окнах появляется пустое поле. В идеале пустое поле должно появляться только в том окне, на которое я нажал. Как только слово сохранено, оно должно отображаться в обоих окнах.

Мой вопрос:

Есть ли способ insert добавить документ в коллекцию только на стороне клиента, а затем использовать upsert позже, чтобы добавить документ в коллекцию на стороне сервера?

Вот что я пробовал:

Я создал метод-заглушку, который существует только на стороне клиента для вставки документа. Проблема в том, что когда я нажимаю на изображение, на долю секунды появляется пустое поле, а затем снова исчезает.

Вот код:

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>

person Chanpory    schedule 29.12.2013    source источник


Ответы (2)


Вы должны сохранить временный тег (и, возможно, вашу переменную isEditing) в Session:

Session.set("isEditing", true);
Session.set("newTag", {x:mouseX, y:mouseY});

Вы также можете создать локальную коллекцию, передав null вместо имени коллекции при ее инициализации. Однако Session должен работать для того, что вы делаете. Посмотрите таблицу лидеров в качестве примера.

Редактировать:

<figcaption class="tags">
  {{#each tags}}
    {{> tag}}
  {{/each}}
  {{#with newTag}}
    {{> tag}}
  {{/with}}
</figcaption>

Template.image.newTag = function() {
  return Session.get("newTag");
}
person sbking    schedule 29.12.2013
comment
Спасибо, я только что проверил таблицу лидеров, но не совсем понял, что вы имеете в виду, передавая null вместо названия коллекции. Вот последняя сборка моего приложения: image-tagger.meteor.com. Вы можете наблюдать, как в обоих окнах браузера приложения появляется пустое поле, когда вы впервые нажимаете на котенка. Если я сохраню информацию временного тега в Session, как мне сделать так, чтобы пустое поле по-прежнему отображалось при нажатии на изображение только в одном браузере? - person Chanpory; 29.12.2013
comment
Локальная коллекция определяется как var MyLocalCollection = new Meteor.Collection(null);. См. мое редактирование для возможного способа добавления временного тега Session. - person sbking; 29.12.2013
comment
Ах, я вижу, этот пример действительно помогает. Попробую. Благодарю вас! - person Chanpory; 29.12.2013
comment
При более внимательном рассмотрении кажется, что вы предлагаете включить пустой тег в DOM до того, как пользователь нажмет на изображение, а не после. Следовательно, событие клика должно отображать/скрывать тег. И когда тег будет сохранен, элемент тега будет заменен реальным? - person Chanpory; 29.12.2013
comment
Если вы используете блок {{#with newTag}}, тег не будет добавлен в DOM до тех пор, пока помощник newTag не вернет что-то отличное от null/undefined. Итак, вы делаете Session.set("newTag", {x:mouseX, y:mouseY}) в обработчике кликов, а затем делаете Session.set("newTag", null), когда хотите, чтобы временный HTML-тег исчез. - person sbking; 29.12.2013

Если вы создаете коллекцию только на стороне клиента, у вас могут возникнуть проблемы, если вы отключены: ваши новые документы не будут храниться на сервере.

На мой взгляд, лучший способ - установить свойство «опубликовано», «редактируется» или «статус» (со значением «опубликовано/изменено/...») в вашем документе. Затем ваши методы публикации должны вернуться:

  1. Все документы текущего пользователя
  2. Все документы "опубликованы" или не в статусе "редактируются"

Когда пользователь создает документ, он сохраняется на сервере, но со статусом редактирования. затем, когда вы сохраните, вы можете решить опубликовать его, и тогда все остальные пользователи получат документ в своей подписке.

Надеюсь, что альтернативное решение поможет вам

person Rebolon    schedule 08.01.2014