Using X-Editable with Ember JS

November 12, 2014 • Damien White

In a couple of apps I’ve worked on, we’ve used the jQuery plugin X-Editable for entering values instead of bulky textboxes. X-Editable works nicely with Bootstrap and pops-up an editor in order to fill in a value. If you aren’t familiar with X-Editable, I encourage you to check out their demos. Figure 1 shows a simple screenshot of what a typical pop-up looks like.

Figure 01: X-Editable Popup Form
Figure 01: X-Editable Popup Form

Figure 2 shows that X-Editable can also do inline forms if you choose instead of a popup.

Figure 02: X-Editable Inline Form
Figure 02: X-Editable Inline Form

X-Editable works great, but what if you want to use it with Ember? That’s where I ran into a problem. You see, X-Editable changes the value that you have for you. We don’t want that. We want it to flow through Ember so everything is properly bound. Let’s start with how we might accomplish this goal.

X-Editable and Ember Binding

The first step is to setup a link for X-Editable. In this tutorial we’ll use a simple textbox, but the concept should work with other types of editors (e.g. date picker). Below is the HTML code (using Handlebars) for a simple X-Editable link.

<a href="#" class="editable" data-type="text">{{name}}</a>

This alone doesn’t give us enough information in order to update a model. I added in a data-id attribute that I could use in order to set the proper model property. Within it, I simply put in the name of the property that I’m going to want to set (in this case, name). With that added in, we have our final link.

<a href="#" class="editable" data-type="text" data-id="name">{{name}}</a>

Now let’s focus on the JavaScript code. We have to wire up any of the .editable elements using a bit of jQuery, but not so fast. We’re in an Ember application. Where should we do this? I created an Ember.View to handle this for my template. From there, I hooked into the didInsertElement method on the VIew.

App.IndexView = Ember.View.extend({
  didInsertElement: function() {
    $('.editable').editable();
  }
});

That call to editable is how we’d normally wire-up our X-Editable links. In our case though, we’re going to want to hook into X-Editable’s success method in order to update our model. Adding in that code gives us the following.

App.IndexView = Ember.View.extend({
  didInsertElement: function() {
    self = this
    $('.editable').editable({
      success: function(response, newValue) {
        propName = $(this).data('id');
        self.set('controller.model.' + propName, newValue);
      }
    });
  }
});

With the success method in place, we now are updating our model with Ember. This works perfectly.

A Little Problem

For those of you running Ember 1.7 and below, there is still one little problem. Our value isn’t always kept up-to-date when dealing with a value inside a Handlebars expression. This doesn’t seem to happen with Ember 1.8 and above (because it uses new metal views), but I found an issue using 1.7 and below because the views use script metamorph tags to keep values up-to-date. If you aren’t sure what I mean, let’s look closer at Ember 1.7 vs 1.8.

Big Changes From Ember 1.7 to 1.8

When using Ember 1.7, when you create a simple Handlebars tag, like so:

<h3>Amount = {{formatCurrency amount}}</h3>

It ends up yielding the following HTML code.

<h3>Amount =
  <script id="metamorph-2-start" type="text/x-placeholder"></script>
  123,456
  <script id="metamorph-2-end" type="text/x-placeholder"></script>
</h3>

Notice that our value has been wrapped in <script id="metamorph-2-start" type="text/x-placeholder"></script> and <script id="metamorph-2-end" type="text/x-placeholder"></script>. This is how Ember used to keep track of values in your application.

With Ember 1.8 and above, the results are MUCH cleaner.

<h3>Amount = 123,456</h3>

These are called “Ember Metal Views” (you may have heard them called “HTMLBars” as well, as that is what they were called during development).

X-Editable and the Ember 1.7 Problem

This problem affects all versions of Ember prior to 1.8. Continuing with our “Amount” example, let’s make that value editable using X-Editable as we saw previously. We’re also going to use a Handlebar helper that formats the value to US currency, adding commas to the value. It is called formatCurrency, but its implementation doesn’t matter for our example. Below is the Handlebars code.

<p>The amount is <a href="#" class="editable" data-type="text" data-id="amount">{{formatCurrency amount}}</a></p>

Now remember, in Ember 1.7, our call to formatCurrency amount will be wrapped in script metamorph tags. Meaning that there is now HTML content INSIDE our X-Editable tag. This looks like so:

<p>The amount is <a href="#" class="editable editable-click" data-type="text" data-id="amount">
  <script id="metamorph-1-start" type="text/x-placeholder"></script>
  123,456
  <script id="metamorph-1-end" type="text/x-placeholder"></script>
</a></p>

When we change the value of amount in this scenario, we get an unexpected surprise (Figure 03).

Figure 03: Script Metamorph Error
Figure 03: Script Metamorph Error

What is the error Uncaught Error: Cannot perform operations on a Metamorph that is not in the DOM. telling us? No guesses? This error is coming from our helper method, but why?

What’s happened is that X-Editable changes the value in the HTML for us, replacing the content between the a tags, thus stripping out the script metamorph tags that Ember 1.7 (and below) need to update the value. Thankfully there is a way to tell X-Editable to stop this behavior. It took me a good long time to figure out how to accomplish this, but looking at the docs gave me the answer. The display callback can be overridden to return false, thus meaning “no displaying methods will be called, element’s text will never change.” Perfect! Let’s add in that code to our IndexView giving us the final code that supports both 1.8 and below.

App.IndexView = Ember.View.extend({
  didInsertElement: function() {
    self = this
    $('.editable').editable({
      display: function() {
        return false; // Ember already takes care of updating the UI for us, so don't allow X-Editable to do it as well.
      },

      success: function(response, newValue) {
        propName = $(this).data('id');
        self.set('controller.model.' + propName, newValue);
      }
    });
  }
});

That’s a Wrap

I hope you found this post helpful. We learned a bit about the inner workings of Ember, and the difference that 1.8+ brought to our views. We also learned how to use X-Editable successfully with Ember. If you want to play around with some source code, here is a CodePen that you can mess around with. Try changing the Ember version to 1.7.0 and you’ll see that the code works as expected.

See the Pen emOqWe by Damien White (@visoft) on CodePen.

Posted in ember and tagged with bootstrap, ember, x-editable

Damien White

I am a software architect with over 16 years of experience. I simply love coding! I have a driving passion for computers and software development, and a thirst for knowledge that just cannot be quenched. I'm happy to share what I know in my quest to learn as much as possible. I focus most of my time on web development using Ruby on Rails, Ember.js, and ASP.NET MVC.

comments powered by Disqus