Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Asynchronously delete skills #7

Merged
merged 9 commits into from
Nov 2, 2014
Merged
21 changes: 15 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,20 +97,29 @@ This is the first release with JavaScript and jQuery included. As a reference, h

### v[1.2.0] Asynchronously edit and delete jobs

[View pull request](../../pull/6).
[Download release](https://github.com/codeunion/linkedout-example/archive/v1.2.0.zip).

- [X] User can edit jobs on the résumé show page
- [X] User can delete jobs on the résumé show page
- [X] Editing jobs occurs asynchronously
- [X] Deleting jobs occurs asynchronously
- [X] Edited jobs are updated on the page without refresh
- [X] Deleted jobs are removed from the page without refresh

### v[1.4.0] Asynchronously edit and delete skills
### v[1.3.0] Asynchronously delete skills

- [X] User can delete skills on the résumé show page
- [X] Deleting skills occurs asynchronously
- [X] Deleted skills are removed from the page without refresh

This release also makes improvements to the underlying architecture of the application, including the following changes:

- [X] Use [RESTful routing](http://en.wikipedia.org/wiki/Representational_state_transfer#Applied_to_web_services) conventions
- [X] Logging in `skills.js`
- [X] Format of HTML and JavaScript more consistent

- [ ] User can edit skills on the résumé show page
- [ ] User can delete skills on the résumé show page
- [ ] Editing and deleting skills occurs asynchronously
- [ ] Edited skills are updated on the page without refresh
- [ ] Deleted skills are removed from the page without refresh
In addition, the `/resumes/edit` page has been changed to `/users/edit` and now contains only a form to edit the user profile. This change removes redundancy in editing other resources.

### v[1.5.0] Display education information

Expand Down
37 changes: 19 additions & 18 deletions linkedout.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,11 @@ def default_user
erb :'resumes/show'
end

get "/resumes/edit" do
@jobs = default_user.jobs
@skills = default_user.skills

erb :'resumes/edit'
get "/users/edit" do
erb :'users/edit'
end

put "/users/edit" do
put "/users" do
user_attrs = params[:user]

default_user.update(user_attrs)
Expand All @@ -58,11 +55,10 @@ def default_user
end
end

put "/jobs/edit" do
put "/jobs/:job_id" do
job_id = params[:job_id]
job_attrs = params[:job]

job_id = job_attrs.delete("id")

job = Job.get(job_id)
job.update(job_attrs)

Expand All @@ -73,11 +69,10 @@ def default_user
end
end

delete "/jobs" do
delete "/jobs/:job_id" do
job_id = params[:job_id]
job_attrs = params[:job]

job_id = job_attrs.delete("id")

job = Job.get(job_id)
job.destroy

Expand All @@ -96,19 +91,25 @@ def default_user
skill.save

if request.xhr? # this will return true when handling an AJAX request
partial :'partials/skill', :locals => { :skill => skill }
html = "<li>"
html += partial :'partials/skill', :locals => { :skill => skill }
html += "</li>"
html
else
redirect "/"
end
end

put "/skills/edit" do
delete "/skills/:skill_id" do
skill_id = params[:skill_id]
skill_attrs = params[:skill]

skill_id = skill_attrs.delete("id")

skill = Skill.get(skill_id)
skill.update(skill_attrs)
skill.destroy

redirect "/"
if request.xhr?
skill_id
else
redirect "/"
end
end
32 changes: 22 additions & 10 deletions public/js/jobs.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@ var createJobOnSubmit = function() {
evt.preventDefault();

var $newJobForm = $(this);

// Use the destination path defined in the form's 'action'
// attribute, i.e. `/jobs`
var actionPath = $newJobForm.attr('action');
var newJobFormData = $(this).serialize();

log("Sending POST request to /jobs");
log("Sending POST request to " + actionPath);

$.post("/jobs", newJobFormData, function(jobHTML) {
log("Received response from POST request to /jobs");
$.post(actionPath, newJobFormData, function(jobHTML) {
log("Received response from POST request to " + actionPath);

log("Adding new job element to list");
$newJobForm.parent('li').before(jobHTML);
Expand Down Expand Up @@ -48,21 +52,25 @@ var updateJobOnSubmit = function() {
evt.preventDefault();

var $editJobForm = $(this);

// Use the destination path defined in the form's 'action'
// attribute, i.e. `/jobs/:job_id`
var actionPath = $editJobForm.attr('action');
var editJobFormData = $editJobForm.serialize();

// The job item to update is the the element of class
// 'job' in the same containing element (in this case, <li>)
var $jobElem = $editJobForm.siblings('.job');

log("Sending PUT request to /jobs/edit");
log("Sending PUT request to " + actionPath);

// Send async PUT request to /jobs/edit
// Send async PUT request to /jobs/:job_id
$.ajax({
url: '/jobs/edit',
url: actionPath,
type: 'PUT',
data: editJobFormData
}).done(function(responseData) {
log("Received response from PUT request to /jobs/edit");
log("Received response from PUT request to " + actionPath);
// This function will execute when the response comes
// back from the server
//
Expand All @@ -87,23 +95,27 @@ var deleteJobOnSubmit = function() {
evt.preventDefault();

var $deleteJobForm = $(this);

// Use the destination path defined in the form's 'action'
// attribute, i.e. `/jobs/:job_id`
var actionPath = $deleteJobForm.attr('action');
var deleteJobFormData = $deleteJobForm.serialize();

// Grab the containing <li> element so that we can remove
// it when the delete request completes
var $jobContainerElem = $(this).closest('li');

log("Sending DELETE request to /jobs");
log("Sending DELETE request to " + actionPath);

// Sending a DELETE request requires using the jQuery
// .ajax() function and configuring the url, type,
// data, and complete options
$.ajax({
url: '/jobs',
url: actionPath,
type: 'DELETE',
data: deleteJobFormData
}).done(function() {
log("Received response from DELETE request to /jobs");
log("Received response from DELETE request to " + actionPath);

log("Removing deleted job element");
$jobContainerElem.remove();
Expand Down
84 changes: 64 additions & 20 deletions public/js/skills.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
var createNewSkillsOnSubmit = function() {
// What's this with the dollar sign in the variable?
// It doesn't change how the variable works, it is just an
// indicator to say "this variable contains a jQuery object"
var $newSkillForm = getNewSkillForm();
// Bind to the "submit" event of the new skill form using
// event delegation
$('.skills').on('submit', 'form[name="new_skill"]', function(evt) {
log("New skill form submitted");

// Bind to the "submit" event of the new skill form
$newSkillForm.submit(function (evt) {
// Prevent the submit from sending an HTTP POST request
// Instead, we'll handle the request with AJAX
evt.preventDefault();

// What's this with the dollar sign in the variable?
// It doesn't change how the variable works, it is just an
// indicator to say "this variable contains a jQuery object"
//
// In this context, `this` points to the form which has just
// been submitted, which is what we want.
var $newSkillForm = $(this);

// Use the destination path defined in the form's 'action'
// attribute, i.e. `/skills`
var actionPath = $newSkillForm.attr('action');

// Grab the form data and serialize it as a string in
// standard URL-encoded notation
var skillFormData = $(this).serialize();
var newSkillFormData = $newSkillForm.serialize();

log("Sending POST request to " + actionPath);
// Send a POST request asynchronously to the "/skills"
// route on the server, passing the serialized form data
// in the request body.
Expand All @@ -22,33 +33,66 @@ var createNewSkillsOnSubmit = function() {
// this will send a request to the server to save this
// skill to the database
//
// The insertNewSkillIntoDOM function (argument 3) will
// The anonymous function passed as the third argument will
// be executed when the browser receives a response from
// the server. It is passed the response body, which in
// this case is a snippet of HTML representing the newly-
// created skill.
$.post("/skills", skillFormData, insertNewSkillIntoDOM);
$.post(actionPath, newSkillFormData, function(newSkillHTML) {
log("Received response from POST request to " + actionPath);

log("Adding new skill element to list");
// Add the new skill to the list, just before the form's
// parent 'li' element
$newSkillForm.parent('li').before(newSkillHTML);

log("Resetting new skill form")
// Reset the form so that new skills can be added
$newSkillForm.get(0).reset();
});
});
};

var getNewSkillForm = function() {
// Select the new skill form using its name attribute
return $('form[name="new_skill"]');
};
var deleteSkillOnSubmit = function() {
$('.skills').on('submit', 'form[name="delete_skill"]', function(evt) {
log("Edit skill form submitted");

evt.preventDefault();

var $deleteSkillForm = $(this);

// Use the destination path defined in the form's 'action'
// attribute, i.e. `/skills/:skill_id`
var actionPath = $deleteSkillForm.attr('action');
var deleteSkillFormData = $deleteSkillForm.serialize();

// Grab the containing <li> element so that we can remove
// it when the delete request completes
var $skillContainerElem = $(this).closest('li');

var insertNewSkillIntoDOM = function(newSkillHTML) {
var $newSkillForm = getNewSkillForm();
log("Sending DELETE request to " + actionPath);

// Add the new skill to the list, just before the form's
// parent 'li' element
$newSkillForm.parent('li').before(newSkillHTML);
// Sending a DELETE request requires using the jQuery
// .ajax() function and configuring the url, type,
// data, and complete options
$.ajax({
url: actionPath,
type: 'DELETE',
data: deleteSkillFormData
}).done(function() {
log("Received response from DELETE request to " + actionPath);

// Reset the form so that new skills can be added
$newSkillForm.get(0).reset();
log("Removing deleted skill element");
$skillContainerElem.remove();
});
});
};


// Wait to execute all code until the document is ready
// (i.e. all of the DOM nodes have been loaded)
$(document).ready(function() {
createNewSkillsOnSubmit();

deleteSkillOnSubmit();
});
3 changes: 1 addition & 2 deletions views/partials/job.erb
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<div class="job">
<form name="delete_job" action="/jobs" method="post" accept-charset="utf-8" class="float_right">
<form name="delete_job" action="/jobs/<%= job.id %>" method="post" accept-charset="utf-8" class="float_right">
<input type="hidden" name="_method" value="delete">
<input type="hidden" name="job[id]" value="<%= job.id %>">
<input type="submit" value="Delete">
</form>
<a href="/resumes/edit" title="Edit job" class="js_edit_job float_right">Edit</a>
Expand Down
6 changes: 2 additions & 4 deletions views/partials/job_edit.erb
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
<% if hidden %>
<form class="hidden" name="edit_job" action="/jobs/edit" method="post" accept-charset="utf-8">
<form class="hidden" name="edit_job" action="/jobs/<%= job.id %>" method="post" accept-charset="utf-8">
<% else %>
<form name="edit_job" action="/jobs/edit" method="post" accept-charset="utf-8">
<form name="edit_job" action="/jobs/<%= job.id %>" method="post" accept-charset="utf-8">
<% end %>
<input type="hidden" name="_method" value="put">

<input type="hidden" name="job[id]" value="<%= job.id %>">

<input type="text" name="job[job_title]" value="<%= job.job_title %>"> at
<input type="text" name="job[company_name]" value="<%= job.company_name %>">
<br>
Expand Down
6 changes: 5 additions & 1 deletion views/partials/skill.erb
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
<li><%= skill.name %></li>
<form name="delete_skill" action="/skills/<%= skill.id %>" method="post" accept-charset="utf-8" class="float_right">
<input type="hidden" name="_method" value="delete">
<input type="submit" value="Delete">
</form>
<%= skill.name %>
12 changes: 0 additions & 12 deletions views/partials/skill_edit.erb

This file was deleted.

10 changes: 4 additions & 6 deletions views/partials/skill_new.erb
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
<li>
<form name="new_skill" action="/skills" method="post" accept-charset="utf-8" class="mini">
<input type="text" name="skill[name]" placeholder="add a skill">
<form name="new_skill" action="/skills" method="post" accept-charset="utf-8" class="mini">
<input type="text" name="skill[name]" placeholder="add a skill">

<input type="submit" value="Save">
</form>
</li>
<input type="submit" value="Save">
</form>
28 changes: 0 additions & 28 deletions views/resumes/edit.erb

This file was deleted.

Loading