Uploading Attachments with Ajax

For the past two days, my Rails Assessment partner and I have been working on revamping our project, ArtMart, in order to complete the Rails and JavaScript assessment. One of the requirements is to create a new resource and render the response without a page refresh using our existing Rails API and Ajax.

At first, this seemed like a very simple task. Especially since most of the labs leading up to the assessment provide practice using Ajax. However, once I started to implement this functionality, I quickly ran into a problem neither of us had ever encountered before.

The Problem

How to upload form data, including a file, and submit it via Ajax while preventing the page refresh, and then render the created resource from the response.

In a previous lab, I learned how to take all the form data and serialize it using jQuery’s  .serialize() function. Hence, my initial notion that adding the functionality would not be too challenging. Well, I was wrong.

The code I had looked something like this:

function attachFormListener() {
  $( "form" ).on( "submit", function(e) {
    e.preventDefault();
    let url = $(this).attr('action');
    let data = $(this).serialize();

    $.ajax({
      url: url,
      type: 'POST',
      data: data,
      success: function(response) {
        let artwork = new Artwork(response.artwork);
        let html = artwork.renderHTML();
        $('.artwork-new').html(html);
      }
    });
  });
}

This worked for all regular form input fields, but not for the file input field. The Ajax was was working and posting to the right action, but params was missing a key piece of information: the actual image data.

without-image-key

Because of this, the transaction was being rolled back and the resource just wouldn’t get created.

Looking for answers on how to handle file submission via Ajax using Rails, it seemed to be a problem that would require some workarounds. I tried a few of them but was unsuccessful. By this point, I don’t think my brain was processing right anymore.

Okay, So What Worked?

Using the FormData API.

The FormData interface provides a way to easily construct a set of key/value pairs representing form fields and their values, which can then be easily sent using the XMLHttpRequest.send() method. It uses the same format a form would use if the encoding type were set to "multipart/form-data".

Source: Mozilla Developer Network

This was actually one of the first things I had tried. Until I learned from a classmate (thank you, Gordon) that I was simply using it wrong! The code was actually very close to what I tried,  but I was missing two key pieces of the puzzle.

For everything to work, I had to set processData and contentType both to false inside of the Ajax call.

So this is what the working code looks like now:

function attachFormListener() {
  $( "form" ).on( "submit", function(e) {
    e.preventDefault();
    let url = $(this).attr('action');

    $.ajax({
      url: url,
      type: 'POST',
      data: new FormData(this), // compile 'this form' into key/value pairs
      processData: false,
      contentType: false,
      success: function(response) {
        let artwork = new Artwork(response.artwork);
        let html = artwork.renderHTML();
        $('.artwork-new').html(html);
      }
    });
  });
}

 

After submitting the form this way, now params looks like this:

with-image-key

So with that, Paperclip can do its job and the resource can be created as normal and finally sent back to the client as JSON.

Conclusion

What I thought would be a simple feature to add, turned out to be a lengthy process leading to what felt like hours wasted. I remember at one point I almost gave up, and thought of satisfying the requirement in another way. However, I just couldn’t leave it alone, and I’m glad that I didn’t.

Although the solution ended up being very simple, further adding to that feeling of wasting so much time, I choose to look at this as just another learning opportunity on my journey toward becoming a web developer.

 

Resources:

Getting Acquainted With JavaScript

As I work through the JavaScript labs on Learn.co I have been confused with how to write proper JavaScript. What is the proper place for brackets? What about conditionals? Most importantly, what is the deal with this annoying semicolon? This is all more confusing since we are going through a curriculum that’s split between the last two versions of ECMAScript, ES5 (used on the older track) and ES6 (which the new track is based on).

In this  blog post, I aim to provide a brief guide for some of the more common things we’ll be dealing with when writing JavaScript. Keep in mind that everyone usually settles into their own style of programming, and this guide is only meant to assist with some of the lower level decisions such as when to use const vs. let.

The rules:

Use 2 spaces for indentation

Most style guides and code examples I come across follow this rule. This is something easy to take care of by going to the configuration settings of your chosen text editor.

Use single quotes for strings

The exception to this rule should be if you need to escape apostrophes or other quotes. Although not strictly enforced by the language, pick one preference and stick by it.

console.log('Hello world... Now in JavaScript!');
console.log("He's using double quotes to escape ' ");

Add space after keywords

// good
if (condition) { ... }

// bad
if(condition) { ... }

Brackets open on the same line

function sayHello(name) {
  console.log('Hello, ' + name);
}

Else, else if follow after closing bracket for the previous condition

// good
if (true) {
  // do something here
} else {
  // do something else
}

// bad
if (true) {
  // do something here
}
else {
  // do something else
}

Use parentheses for conditions

This is one where I wasted approximately 20 minutes in one of the early labs, all because I had been spoiled by Ruby’s friendly syntax.

Make sure to always wrap your conditions inside parentheses. I believe the only exception to this rule is when using the ternary operator, which in Javascript looks the same as in Ruby:

condition ? expr1 : expr2
       // if true : if false

Use === instead of ==

While the double equals operator has its uses, it’s better to be on the safe side and check for strict equality using triple equals to avoid some unintended behavior.

The == operator allows for type conversion before checks, while === checks for strict type equality.

'1' == 1; // true because '1' is converted to a number before comparison
1 == 1; // true

'1' === 1 // false because the types are different

Like in the above examples, use !== instead of != to check for non-equality.

Write each variable declaration in its own expression

// good
let a = 'hi';
let b = 'hello';

// not necessarily bad, but it's harder to follow
let a = 'hi', b = 'hello';

While the second example works, separating each variable assignment makes the code easier to read.

Const, let, or var?

ES6 introduced const and let as ways to declare variables. Prior to them, there was only the use of var. I’m still not 100% sure that my usage of these new identifiers is correct all the time, but I try to force myself to use them in any lab that supports ES6 as a way to practice their use.

In a nutshell, use const when the variable you’re declaring isn’t going to be reassigned. For example, the endpoint of an API. Otherwise, opt for let.

Both const and let are block scoped, meaning that they are only available within the block they’ve been declared in.

Avoid using var. According to Eric Elliot:

`var` is now the weakest signal available when you define a variable in JavaScript. The variable may or may not be reassigned, and the variable may or may not be used for an entire function, or just for the purpose of a block or loop.

His point is that proper use of const and let is more expressive of what each variable is intended to do. Their use lets other developers know whether a variable is going to change or not later on in the code.

Arrow functions

Probably the biggest feature introduced in the ES6 specification are arrow functions. In fact, according to this 2015 article from 2ality, arrow functions topped the list as developers’ favorite JavaScript ES6 feature. And guess, what? The more I use them, the more I agree.

Arrow functions provide cleaner syntax and are especially handy as callback functions. Compare the following two examples:

// extracting names out of a json object

function extractNames(tracks) {
  return tracks.map( function(track) {
    return track.name;
});

// arrow function passed as callback to .map()
function extractNames(tracks) {
  return tracks.map( track => track.name );
}

 

$.ajax({
  url: url,
  dataType: 'jsonp',
  success: function(response) {
    callback(response);
  }
});

// arrow function passing response to the callback
$.get(url, response => callback(response) );

I know, I know, the second one is cheating a bit because of the switch to $.get() but the syntax would still be cleaner if you were set on using $.ajax() along with arrow functions.

Some key things to keep in mind when using arrow functions are:

  1. Parentheses are optional when there’s only one parameter
  2. A function with no parameters requires parentheses:
    () => { some code here }
    
  3. Arrow functions can have either a “concise body” or the usual “block body”. In a concise body, the return is implicit. When using a regular block body a return must be specified.
    var a = [
      "Hydrogen",
      "Helium",
      "Lithium",
      "Beryl­lium"
    ];
    
    var a2 = a.map(function(s){ return s.length });
    
    var a3 = a.map( s => s.length ); // return is implicit in this case
    
    source: <em><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions" target="_blank">MDN</a></em>
    

Do use semicolons!

Okay, it seems these days this is more of a personal preference rather than a strict rule. Even a lot of the solutions to the labs omit the use of semicolons. The reason why it seems that semicolons are optional is because JavaScript uses something called Automatic Semicolon Insertion (ASI), which is a feature of the ECMAScript specification.

From the specification:

Certain ECMAScript statements must be terminated with semicolons. Such semicolons may always appear explicitly in the source text

But it goes on to state:

For convenience, however, such semicolons may be omitted from the source text in certain situations.

In order to avoid errors caused by a missing semicolon, I would highly encourage its use.

Linters

Tools like JSHint, JSLint and ESLint allow for safer coding with fewer errors. These are tools that check your code real-time and point out any possible mistakes or things that could cause issues.

They are available as plugins for most of the popular text editors. Just do a search for them using the plugin/package manager for your preferred editor.

Here’s an example of JSHint in action inside Atom:

screen-shot-2016-10-23-at-8-07-23-pm

As you can see, it’s a very handy tool to have so you can focus on the actual errors with your code, as opposed to errors caused by its syntax.

Conclusion

Just like Ruby, and I’m sure any other programming language, seems like there  is a lot to learn when it comes to JavaScript. I encourage you to at least skim through the resources listed below if you want more in-depth coverage of the topics discussed in this post. In addition, get acquainted with the Mozilla Developer Network. It is an amazing resource for JavaScript documentation.

Hopefully, this short guide provided an introductory primer to those who are brand new to the language.

 

Resources

http://www.bradoncode.com/blog/2015/08/26/javascript-semi-colon-insertion/

Semicolons in JavaScript: A preference

JavaScript Standard Style

var, let or const?

exploringjs.com – Arrow functions