ember emberjs javascript labs

Copying text to the clipboard in Ember.js with ZeroClipboard

If you’re used to working with Backbone.js, you may be accustomed to writing your own render method for your views. Typically, it looks something like:

var MyView = Backbone.View.extend({
  render: function () {
    this.$el.html(this.template(this.model.toJSON()));
  }
});

In contrast, in Ember.js, rendering is done for you. In fact, most of the time you don’t create or manage views yourself; Ember will figure it out, based on your routes and your templates. That sounds pretty great, but what happens when you want to leverage a jQuery plugin, or some other imperative, DOM-related JavaScript? If you don’t control the render method, where do put your code that calls d3 or SlickGrid?

Ember.View#didInsertElement

Ok– so if you’re clever enough or lucky enough, you’ll spend most of your time in Ember writing html templates and imperative controller actions. But if you do need to call a jQuery plugin in your Ember app, you’ll probably need to override Ember.View#didInsertElement, which means creating an explicit View class:

App.MyView = Ember.View.extend({
  didInsertElement: function () {
  }
});

To see this in action, let’s implement an interesting but troublesome feature. You want to let your users copy something to the clipboard, but you don’t know how to do it. Turns out that the clipboard is an operating system feature, and browsers haven’t historically provided a JavaScript API for writing to the OS clipboard. But plenty of sites have this feature, so how does everybody do it?

As it happens, good old Adobe Flash can modify the clipboard, so a popular way of copying content to the clipboard from a webpage is to put an invisible Flash movie over an html link or button, then ask the Flash object to modify the clipboard for you when it gets clicked. If that sounds unpleasant, take heart: You can use ZeroClipboard, which does the unfun parts for you.

If we want to use ZeroClipboard in Ember, that’s not too hard. We just add ZeroClipboard to our HTML, host its magic .swf file somewhere on our server, and add some code to our didInsertElement method:

JS Bin

This will configure ZeroClipboard, and create a new ZeroClipboard object that inserts the full URL of the current page into the clipboard when the user clicks our $('.copy-button') element.

Give it a try.

but… UX?

So, that worked — but we probably want to give some user feedback in response to the click. Right now, we don’t tell the user that anything has happened, which is important when you’re doing something invisible like modifying the clipboard. Let’s replace the link with the text ‘copied!’, when the user clicks.

JS Bin

That’s pretty good, but it only lets the user copy the url once. Let’s show the ‘copied’ message for a second or two, then go back to the original link, so the user can copy the url again.

JS Bin

That’s a bit better, but wait — the link reappears in 1.5 seconds, but it still doesn’t work after the first time. That’s because didInsertElement is called only the first time the view is appended to the DOM, not when it rerenders, so we’ve clobbered our original ZeroClipboard, but we haven’t replaced it with a new one. So let’s extract the ZeroClipboard setup into a method that we’ll call from didInsertElement, and we’ll also call that after we’ve set justCopied property on our controller. That should create a new ZeroClipboard object each time we set justCopied to true.

JS Bin

That actually didn’t work any better, but it’s for a good reason. Ember doesn’t immediately re-render templates when bound properties change. Instead, it batches them together using something called the run loop. We want bindClipButton to be called after the DOM has been updated, so lets use Ember.run.next to schedule it to run sometime after Ember has flushed all its template changes. For more details on Ember.run, I highly recommend the Ember.js docs.

JS Bin

That’s it!

You can try out the final version here