4

我在 Meteor 0.7.2 中有x-editable工作,但自从升级到 0.8.0 后,它不再正确渲染。我倾向于以一堆空标签结束。这是令人沮丧的,因为数据就在那里,只是在渲染函数被触发时还没有。

<template name="clientPage">
    <header>{{> clientPageTitleUpdate}}</header>
</template>

<template name="clientPageTitleUpdate">
    <h1><span class="title-update editable" data-type="text" data-pk="{{_id}}" data-name="title" data-value="{{title}}">{{title}}</span></h1>
</template>


    Template.clientPageTitleUpdate.rendered = function() {

        console.log(this.$(".title-update").text());

        // set up inline as defaule for x-editable
        $.fn.editable.defaults.mode = 'inline';

        $(".title-update.editable:not(.editable-click)").editable('destroy').editable({

            url:    "empty",
            toggle: "dblclick",

            success: function (response, newValue) {
                // update value in db
                var currentClientId = $(this).data("pk");
                var clientProperties = { title: newValue };

                Clients.update(currentClientId, {$set: clientProperties}, function(error) {
                    if (error) {
                        Errors.throw(error.message)
                    }
                });
            }// success

        });

    }

我已经尝试了将 this 嵌入到另一个模板中的“新”渲染方法,如此处所述它似乎也不起作用。

现在使用 x-editable 的最佳方法是什么,它只触发一次并且不能确保数据在那里。

我正在使用 Iron Router 并且我的模板没有嵌入到 {{#each}} 块中,这似乎是新渲染模型的基本解决方案。

这个问题与这个关于流星模板中的 x-editable 的旧主题有关。

任何帮助都将在这里非常感激。我很茫然。谢谢

4

6 回答 6

8

编辑:现在在 Meteor 0.8.3 中更容易实现

模板:

<template name="docTitle">
    <span class="editable" title="Rename this document" data-autotext="never">{{this}}</span>
</template>

代码:

Template.docTitle.rendered = ->
  tmplInst = this

  this.autorun ->
    # Trigger this whenever data source changes
    Blaze.getCurrentData()

    # Destroy old editable if it exists
    tmplInst.$(".editable").editable("destroy").editable
      display: ->
      success: (response, newValue) -> # do stuff

为了最有效,请确保可编辑模板的数据上下文仅是正在编辑的字段,如上例中的{{> docTitle someHelper}}.


Meteor 0.8.0 到 0.8.2 的过时信息如下

我也必须这样做,但不确定在我的应用程序中使用全局帮助器。所以我试图通过改变可编辑的行为来完成它。

在仔细阅读文档和源代码之后,需要做的主要事情是:

这是代码(为 Coffeescript 道歉):

Template.foo.rendered = ->
  container = @$('div.editable')
  settings =
    # When opening the popover, get the value from text
    value: -> $.trim container.text()
    # Don't set innerText ourselves, let Meteor update to preserve reactivity
    display: ->
    success: (response, newValue) =>
      FooCollection.update @data._id,
        $set: { field: newValue }
      # Reconstruct the editable so it shows the correct form value next time
      container.editable('destroy').editable(settings)
  container.editable(settings)

这很难看,因为它会在设置新值后破坏并重新创建弹出框,以便表单字段从正确的值更新。

经过更多的逆向工程,我找到了一种更清洁的方法来做到这一点,它不涉及破坏可编辑文件。加迪是对的,container.data().editableContainer.formOptions.value这与它有关。这是因为这个值是在更新后设置的,因为 x-editable 认为它现在可以缓存它。好吧,它不能,所以我们用原始函数替换它,以便继续从文本字段更新值。

Template.tsAdminBatchEditDesc.rendered = ->
  container = @$('div.editable')
  grabValue = -> $.trim container.text() # Always get reactively updated value
  container.editable
    value: grabValue
    display: -> # Never set text; have Meteor update to preserve reactivity
    success: (response, newValue) =>
      Batches.update @data._id,
        $set: { desc: newValue }
      # Thinks it knows the value, but it actually doesn't - grab a fresh value each time
      Meteor.defer -> container.data('editableContainer').formOptions.value = grabValue

笔记:

在未来等待 Meteor 更好地支持响应式依赖数据时,我将尝试使其更简洁。

于 2014-04-17T22:01:19.627 回答
5

为 Meteor 0.8.3+ 更新

这涵盖了我的所有情况(见下面的代码)。这使用了非常细粒度的反应性,并且仅在指定值更改时才会更新 x-editable 实例。

模板:

<!-- once off for your entire project -->
<template name="xedit">
    {{> UI.contentBlock}}
</template>

<!-- per instance -->
<template name="userInfo">
  {{#xedit value=profile.name}}<a>{{profile.name}}</a>{{/xedit}}
</template>

客户端 Javascript(用于 Meteor 0.8.3+):

// once off for your entire project
Template.xedit.rendered = function() {
  var container = this.$('*').eq(0);
  this.autorun(function() {
    var value = Blaze.getCurrentData().value;
    var elData = container.data();
    if (elData && elData.editable) {
      elData.editable.setValue(value, true);
      // no idea why this is necessary; xeditable bug?
      if (elData.editableContainer)
        elData.editableContainer.formOptions.value = elData.editable.value;
    }
  });
}

// per instance; change selector as necessary
Template.userInfo.rendered = function() {
  // Note you don't need all the :not(.editable) stuff in Blaze
  this.$('a').editable({
    success: function(response, newValue) {
      // according to your needs
      var docId = $(this).closest('[data-user-id]').attr('data-user-id');
      var query = { $set: {} }; query['$set']['profile.username'] = newValue;
      Meteor.users.update(docId, query);
    }
  });
});

您可以在http://doingthiswithmeteor.com/上看到它的运行情况(打开两个窗口)。您需要登录,但请尝试更改“我”页面上的任何信息。

  1. 像往常一样在 render() 中设置 x-editable
  2. 能够自定义显示功能、“空”值等。
  3. 在两个窗口中打开。更改 win1 中的值,单击 win2 中的值。弹出窗口应显示正确的值。
  4. 支持自定义类型,如来自自定义助手的日期和数组

刚刚实现了这个......仍在做一些测试,但欢迎反馈。这取代了我以前的帮助解决方法。

于 2014-04-15T22:04:28.513 回答
1

另一个使用 Iron-router 并管理 Collection2 验证的实现:

控制

div(id="text" class="editable" data-type="text" data-pk="#{_id}" data-name="address" data-value="#{address}" data-context="Buildings") #{address}

和JS代码:

  setTimeout( ->   #needed to work with iron-router
    $(".editable").editable
      placement: "auto top"
      display: ->
      success: (response, newValue) ->
        newVal = {}
        oldVal = $.trim $(this).data("value")
        name = $(this).data("name")
        newVal[name] = newValue
        eval($(this).data("context")).update $(this).data("pk"), $set: newVal
        , (error) ->
          Notifications.error error.message
          Meteor.defer -> $(".editable[data-name=" + name + "]").data('editableContainer').formOptions.value = oldVal

        console.log "set new value to " + newValue
        Session.set "text", newValue
  ,500)

我找不到自动设置数据上下文的方法。我相信这应该不是很困难。欢迎任何帮助!

于 2014-07-15T08:55:51.760 回答
1

如果 Andrew 的答案对您有用,并且您有很多这样的字段,您可能会发现使用函数来创建所需的模板很方便。这是一个例子

<template name="main">
  <div style="height:200px"></div>
  <div class="container">
    <div class="jumbotron">
      {{#each editables}}
        {{> editable1}}
        {{> editable2}}
      {{/each}}
    </div>
  </div>
</template>

<template name="editable1">
  <p id="{{id}}" data-type="textarea" data-placeholder="Enter text" data-emptytext="Click to enter text" data-rows="4">{{content}}</p>
</template>
<template name="editable2">
  <p id="{{id}}" data-type="textarea" data-placeholder="Enter text" data-emptytext="Click to enter text" data-rows="4">{{content}}</p>
</template>

在js中:

Template.main.editables = function(){
  return Objects.find({});
};

function xeditFactory(collection, template, field){
  template.content = function(){ return this[field]; };
  template.id      = function(){ return 'xedit_'+this._id+'_'+field; };
  template.rendered = function(){
    var container = this.$('#xedit_'+this.data._id+'_'+field);
    console.log(container);
    var grabValue = function() {
      return $.trim(container.text());
    };
    return container.editable({
      value: grabValue,
      display: function() {},
      success: (function(_this) {
        return function(response, newValue) {
          var set = {};
          set[field]=newValue;
          collection.update(_this.data._id, {$set:set});
          return Meteor.defer(function() {
            return container.data('editableContainer').formOptions.value = grabValue;
          });
        };
      })(this)
    });
  };
}

xeditFactory(Objects, Template.editable1, 'field1');
xeditFactory(Objects, Template.editable2, 'field2');
于 2014-06-20T21:32:08.707 回答
1

根据安德鲁的回答,我能够让它为我工作。它不在coffeescript中,而且我认为 Blaze.getCurrentData() 现在可能是 Blaze.getData() 根据 Meteor 文档。

模板:

<template name="objective">
    <p id="objective" class="editable" data-type="textarea" data-placeholder="Enter text" data-emptytext="Click to enter text" data-rows="4">{{objective.value}}</p>
</template>

代码:

Template.objective.rendered = function(){
    var self = this;
    this.autorun(function(){
        data = Blaze.getData();
        self.$("#objective.editable").editable("destroy").editable({
            placement: "bottom",
            display: function(){},
            success: function(response, newValue){
                var insert = {
                    "contract_id":data._id,
                    "value": newValue
                };
                Meteor.call('update_objective', insert);
            }
        });
    });
};

我可能可以做出改进,我很高兴听到它们,但是我花了很多时间处理糟糕的咖啡脚本翻译(一直告诉我要使用 return),所以我想添加另一个示例。

于 2016-03-31T03:09:51.340 回答
0

这是我的简化方法,基于 gadicc 的帖子(使用 Meteor 0.9.3 测试)。

假设有一个MyDocuments集合,它通过documentList模板呈现。集合中的每个文档都有title字段,我们要使用 xedtiable 对其进行编辑。

文档.html

<template name="documentList">
    {{#each documents}}
        {{>document}}
    {{/each}}
</template>

<template name="document">
   <p>Title: {{>xeditable titleOptions}}</p>
</document>

文档.js

Template.document.titleOptions = function () {
    return {
        // We need to specify collection, id and field to autoupdate MongoDb
        collection: MyDocuments,
        id: this._id,
        field: 'title',
        value: this.title             
    }
}

xeditable.html

<template name="xeditable">
    <span class="xeditable">{{value}}</span>
</template>

xeditable.js

Template.xeditable.rendered = function () {
    var container = this.$('*').eq(0);
    if (!container.hasClass('processed')) {
        container.addClass('processed');
        var options = _.extend(this.data, {
            // Default success function, saves document do database
            success: function (response, value) {
                var options = $(this).data().editable.options;
                if (options.collection && options.id && options.field) {
                    var update = {};
                    update[options.field] = value;
                    options.collection.update(options.id, {
                        $set: update
                    });
                }
            }
        });
        container.editable(options);
    }

    this.autorun(function () {
        var value = Blaze.getData().value;
        var elData = container.data();
        if (elData && elData.editable) {
            elData.editable.setValue(value, true);
            // no idea why this is necessary; xeditable bug?
            if (elData.editableContainer)
                elData.editableContainer.formOptions.value = elData.editable.value;
        }
    });
}
于 2014-09-26T11:22:16.877 回答