现在如何使用Blaze在Meteor模板中的动态字段上使用X-editable?

时间:2014-04-04 16:04:05

标签: meteor x-editable meteor-blaze

我有x-editable在Meteor 0.7.2中工作,但自升级到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

        });

    }

我已经尝试了“新”渲染方法,将其嵌入到另一个模板中,如here所述,它似乎也无效。

现在使用x-editable的最佳方法是什么,渲染只会触发一次并且无法确保数据存在。

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

此问题与关于x-editable in a meteor template的旧主题相关。

任何帮助都会在这里得到超级赞赏。我很茫然。感谢

6 个答案:

答案 0 :(得分: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的过时信息

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

perusing the docs和来源之后,需要完成的主要事项是:

这是代码(为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与此有关。这是因为这个值是set after the update,因为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更好地支持数据的反应性。

答案 1 :(得分: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/处看到它(两个窗口打开)。您需要登录,但请尝试更改&#34; me&#34;上的任何信息。页。

  1. 像往常一样在render()中设置x-editable
  2. 具有自定义显示功能,&#34;清空&#34;价值观等。
  3. 在两个窗口中打开。更改win1中的值,单击win2中的值..POPUP应显示正确的值。
  4. 支持自定义帮助程序中的日期和数组等自定义类型
  5. 刚刚实施了这个......仍在做一些测试,但欢迎反馈。这取代了我之前的帮助程序。

答案 2 :(得分:1)

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

<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');

答案 3 :(得分: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)

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

答案 4 :(得分:1)

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

模板:

<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);
            }
        });
    });
};

我可以做些改进,我很高兴听到它们,但我花了很多时间来处理糟糕的咖啡因翻译(一直告诉我一直使用返回),所以我想添加另一个例子。

答案 5 :(得分:0)

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

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

<强> document.html

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

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

<强> 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;
        }
    });
}