Series: Drag-n-Drop Image Uploads

Upload a File as Part of a Form

Published on Jun 20, 2015

We want to have an image associated with a post. We need it to be saved at the same time as the post.

We'll be using the ember-cli-form-data addon to help us accomplish this.

Also included is a short bonus section for Rails users on how to setup your server side framework to accept the payload that Ember is giving you.


Links

Code

ember install ember-cli-form-data
//app/post/adapter.js
import ApplicationAdapter from '../application/adapter';
import FormDataAdapterMixin from 'ember-cli-form-data/mixins/form-data-adapter';

export default ApplicationAdapter.extend(FormDataAdapterMixin, {
  // Adapter code
});
//app/post/edit-fields/template.hbs
<p>{{input valueBinding='file' type='file' id='file-field'}} <img src="{{model.imageUrl}}" id="preview-image" class="small-image" /></p>
// save action in app/post/edit/controller.js
save: function(){
  var file = document.getElementById('file-field').files[0];
  this.set('model.thumbnailImage', file);
  this.get('model').save().then(()=>{
    this.transitionToRoute('post.show', this.get('model'))
  })
},
//app/post/model.js
export default DS.Model.extend({
  //...
  thumbnailImage: DS.attr('file'),
  imageUrl: Ember.computed.alias('thumbnailImage.thumbnail_image.url')
//app/post/show/template.hbs
<img src="{{model.imageUrl}}" class="small-image" />
//save action in app/posts/new/controller.js

save: function(){
  var post = this.store.createRecord('post', this.get('newPost'));
  var file = document.getElementById('file-field').files[0];
  post.set('thumbnailImage', file);

  post.save().then((response)=>{
    this.transitionTo('post.show', response)
  })
}

Transcript

I have a form for creating and editing posts. I’d like to add a field for adding an image to the post. This is trivial in server-side frameworks, using tools like CarrierWave. Here, we see that using CarrierWave and Active Admin in Rails, it’s just one line to upload our image. There are some Ember tutorials and add-ons, but they mostly seem to focus on uploading the files individually as their own record, rather than having them as part of another record.

Luckily, there’s one add-on that allows you to add a file to the form. Today, we’re going to use Ember-Cli form data in order to create a fully working form with a file upload. At the end, I’ll include a quick bonus section for Rails users, showing how to manipulate CarrierWave to accept the data that Ember is sending. I don’t usually include server-side stuff, but I spent enough time figuring this out that I thought it was worthwhile to share. Then in this week’s pro-episode, I’ll show you how to preview the image before uploading.

First we’ll install the Ember-Cli form data add-on. This gives us the FormDataAdapterMixin. When we include that mixin in an adapter, it gives us the DS.attr type file for the model associated with that adapter.

So, we’ll generate our custom post adapter. Then in that generated file, we’ll import and include the mixin. Then we’ll go to our post model and add the thumbnailImage property, with the type file.

Let’s go to the Ember inspector and look what we’re getting. We’ll click on one of these objects and find the thumbnailImage property which is itself an object. We’ll save it to the console, and then the console will be able to drill down and find the url. This is what we can use to display it on the page.

So we’ll go to the template of our showpage and then use the path we traced out before to get the url into the source attribute. We’ll add the custom class small-image so it will fit well within our form.

When we go to the showpage, we can see that we are correctly displaying the image. Now we need to in the edit page, have the image upload field. In the edit page, we’ll see that all the fields are in the edit fields component. So we’ll go there and put in a file-field. Then in our save action, we’ll grab the first file associated with that field, the only file associated with that field, and then set it to the thumbnailImage property of our model. Then we’ll save the model as before.

We can test that. We will upload a random screenshot, and then see if it changes it, and it does. Our file field is successful.

For completeness, we’ll want to add this logic to the post new page so that we can upload files when we’re creating a new post as well. The template is already in edit field, so we don’t need to add that.

So that’s it for adding a file to a form. We’ve got both a new and edit forms sending a file up to the server and saving it. If you’re not using Rails on the backend, this is the end of the episode. The techniques shown here will allow you to upload files of small to medium size, and I hope you’ll join me in the pro-episode to talk about how to preview the image before you upload it.

If you are a Rails user, then I’m going to give you a quick bonus section on how to edit your CarrierWave setup to accept the the payload you just sent it. I'm assuming you already have CarrierWave setup to work with server-side forms. In my case, I have it set up with Fog and MiniMagick.

Then, in my post model, I have thumbnailImage mounted as an image uploader. Then I’ve done a bit of fairly standard customization in my uploader. We'll first have to change out Fog, the most commonly used method of AWS, with CarrierWave-AWS. Fog has a rare bug that happens to be triggered by what we’re about to do. This involves changing the gem and then changing the storage key in our image uploader class and then changing the initializer. More details about this are shown in the CarrierWave-AWS readme.

Now we’ll get into the nitty-gritty of manually saving the file using CarrierWave. We can’t rely on active record to do this for us anymore. So, we’ll take our create and update methods from our general API controller and then we’ll paste them into the PostsController. These are currently carbon copies of the originals, but if we change to them, they’ll overwrite the originals. And these may look slightly unfamiliar, but they’re done like this to reduce extra work, so when we don’t need to customize it like we are doing right now, then we can just basically do this to do everything in the PostsController.

Alright, now to our customizations. We’ll insert a save_image method into each of them. In the create method, it has to go between the resource creation, right here, and the render. Then in the update method, it can go anywhere before the render.

We’ll define the save_image method. First, it creates a new ImageUploader. Then it uploads the file from the params, saves that to the thumbnail_Image on the post, and then saves the post.

So that’s the basics of uploading a file as part of a form. Until next time, keep being awesome.

Drag-n-Drop Image Uploads

Subscribe to our mailing list