How to use X-editable in dynamic fields in a Meteor template now with Blaze?

I had x-editable working in Meteor 0.7.2, but since upgrading to 0.8.0 it no longer displays correctly. I usually get a bunch of empty tags. This is disappointing because there is data, simply not at the point where the render function is running.

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

I tried the β€œnew” visualized method of embedding this in another template, as described here , and it doesn't seem to work either.

What is the best way to use x-editable at the moment, which only displays lights once and does not guarantee that the data is there.

I use Iron Router, and my templates are not embedded in the {{#each}} block, which seems to be the main solution for the new rendering model.

This question is related to this older x-editable theme in meteor template .

Any help that would be appreciated here was greatly appreciated. I'm at a loss. Thanks

+4
source share
6 answers

EDIT: It is much easier to implement now in Meteor 0.8.3 :

Template:

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

the code:

 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 

To make this the most effective, make sure that the data context of the edited template is just an editable field, as in the example above with {{> docTitle someHelper}} .


Deprecated information for Meteor 0.8.0 to 0.8.2

I also had to do this, but was not sure about using the global assistant in my application. So I tried to accomplish this by simply changing the behavior of the editable.

The main thing that needed to be done, after viewing the documents and the source, were:

Here's the code (apologies for 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) 

This is ugly because it destroys and re-creates the popover after setting a new value, so that the form field is updated with the correct value.

After some additional reverse engineering, I found a cleaner way to do this, which does not require the destruction of the editable. Gadi was right because container.data().editableContainer.formOptions.value has something to do with it. This is because this value is set after the update , because x-editable believes that it can cache it now. Well, it cannot, therefore we replace it with the original function, therefore the value continues to be updated from the text field.

 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 

Notes:

I will try to make it more concise in the future, expecting better support from Meteor, depending on the data in response.

+8
source

Updated for Meteor 0.8.3 +

This covered all cases for me (see code below). This uses fairly fine-grained reactivity and will update the x-editable instance only when the specified value changes.

Template:

 <!-- 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 Client (for 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); } }); }); 

You can see it in action at http://doingthiswithmeteor.com/ (when you open two windows). You need to log in, but try changing any of your information on the "me" page.

  • Setting x-editable in rendered () as usual
  • Ability for custom display functions, "empty" values, etc.
  • Open in two windows. Change the value in win1, click on the value in win2 .. POPUP should display the correct value.
  • Support for custom types, such as dates and arrays, from custom helpers

It just implemented it ... some testing is still being done, but feedback is welcome. This replaces my previous workaround with a helper.

+5
source

If Andrew answers you and you have many such fields, you can find a convenient function for creating the necessary templates. Here is an example

 <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> 

and in 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'); 
+1
source

Another implementation that works with the iron router and Collection2 validation management:

Control

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

And the JS code:

  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) 

I could not find a way to automatically set the data context. I am sure it should not be very difficult. Any help is appreciated!

+1
source

Work from Andrew's answer, I was able to get this to work for me. This is not in coffeescript, also I think that Blaze.getCurrentData () can now be Blaze.getData () according to the Meteor docs.

Template:

 <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> 

the code:

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

There are probably improvements I can make, and I'm glad to hear them, but I spent a lot of time translating the coffeescript poorly (constantly saying that I always use return), so I wanted to add another example,

+1
source

This is my simplified approach based on gadicc post (tested with Meteor 0.9.3).

Let's say there is a MyDocuments collection that is displayed using the documentList template. Each document in the collection has a title field, which we want to edit using 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; } }); } 
0
source

Source: https://habr.com/ru/post/1499812/


All Articles