Skip to content

Instantly share code, notes, and snippets.

@eliotsykes
Last active January 28, 2020 04:32
Show Gist options
  • Save eliotsykes/75ae13dbe4b9f6718f4f to your computer and use it in GitHub Desktop.
Save eliotsykes/75ae13dbe4b9f6718f4f to your computer and use it in GitHub Desktop.
JSON API with jQuery AJAX and Rails
// Wrap in anonymous function to avoid adding variables to global scope needlessly.
(function($) { // $ is jQuery
function addTodoToDOM(data) { // 'data' is object built from response JSON
// id will be needed when adding delete functionality dynamically.
// var todoId = data.todo.id;
// Add HTML for new todo to document
var tableRow = '<tr><td>' + data.todo.description + '</td></tr>';
var todosTable = $("#todos");
todosTable.append(tableRow);
todosTable.show();
};
function clearForm() {
// Clear input field
var descriptionInput = $("#todo_description");
descriptionInput.val("");
};
function handleError(error) {
// Relies on error response from API being JSON object like:
// { errors: [ "Error message", "Another error message" ] }
var errorsObj = $.parseJSON(error.responseText)
var errorMessages = errorsObj.errors;
alert("There was an error: " + errorMessages);
};
// Not needed for HTTP Basic Auth
function getAuthToken() {
// meta tag in <head> holds auth token
// <meta name="auth-token" content="TOKEN GOES HERE">
var authToken = $("meta[name=auth-token]").attr("content");
return authToken;
};
// LEFT AS REMINDER, NOT needed if using HTTP Basic authentication!
//
// The browser will take care of gathering credentials for HTTP Basic Auth
// as long as authenticate_or_request_with_http_basic is being used for authentication.
//
// authenticate_or_request_with_http_basic will send a 401 Unauthorized with a
// WWW-Authenticate response header which trigger the browser to prompt for username
// and password using native dialog. This username+password will then be cached
// by the browser and submitted correctly as an Authorization request header tagged
// on to all AJAX requests to the server.
//
// function getHttpBasicAuth() {
// var encodedCredentials = window.btoa(unescape(encodeURIComponent(getUsername() + ':' + getPassword())));
// return 'Basic ' + encodedCredentials;
// }
function createTodo(event) {
// Prevent click from triggering form submission default behaviour
event.preventDefault();
var descriptionInput = $("#todo_description");
var description = descriptionInput.val();
var todo = { "todo" : { "description": description } };
var ajaxOptions = {
type: "POST", // HTTP verb, one of: POST, DELETE, PATCH, PUT, GET
headers: { "Authorization": getAuthToken() }, // Not needed for HTTP Basic Auth
url: "/api/v1/todos",
dataType: "json",
contentType: "application/json; charset=utf-8",
data: JSON.stringify(todo)
};
// Initiate the AJAX request
// Docs: http://api.jquery.com/jQuery.ajax/
$.ajax(ajaxOptions).done([addTodoToDom, clearForm]).fail(handleError);
};
function deleteTodo(event) {
event.preventDefault();
// Get the delete button that was clicked (event.target) and
// wrap it in a jQuery object.
var clickedElement = $(event.target);
// The delete button needs a data-todo-id attribute, e.g.
// <button data-todo-id="7" data-delete-todo-button="true">Delete</button>
var todoId = clickedElement.data("todo-id");
var ajaxOptions = {
type: "DELETE",
headers: { "Authorization": getAuthToken() }, // Not needed for HTTP Basic Auth
url: "/api/v1/todos/" + todoId,
dataType: "json",
contentType: "application/json; charset=utf-8"
};
function removeTodoFromDOM() {
// Assumes that the delete button is a child element of the
// todo's table row.
var todoRow = clickedElement.closest("tr");
todoRow.remove();
};
$.ajax(ajaxOptions).done(removeTodoFromDOM).fail(handleError);
};
function setupTodoHandlers() {
$(document).on("click", "[data-create-todo-button]", createTodo);
// <%= button_to "Delete", delete_todo_path(todo),
// method: :delete, data: { :"delete-todo-button" => true, :"todo-id" => todo.id } %>
$(document).on("click", "[data-delete-todo-button]", deleteTodo);
};
setupTodoHandlers();
})(jQuery); // IIFE (Immediately Invoked Function Expression)
// Pass in jQuery to prevent other code from changing what $ means to this code above.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment