From 280297b237b74b74944b3f5f04a1e8c946ce5f21 Mon Sep 17 00:00:00 2001 From: Tanner Welsh Date: Wed, 29 Oct 2014 20:03:27 -0400 Subject: [PATCH 1/5] Move CSS files into own css directory Organization! Ohhhhh yeah. --- public/{ => css}/main.css | 0 public/{ => css}/normalize.css | 0 views/layout.erb | 4 ++-- 3 files changed, 2 insertions(+), 2 deletions(-) rename public/{ => css}/main.css (100%) rename public/{ => css}/normalize.css (100%) diff --git a/public/main.css b/public/css/main.css similarity index 100% rename from public/main.css rename to public/css/main.css diff --git a/public/normalize.css b/public/css/normalize.css similarity index 100% rename from public/normalize.css rename to public/css/normalize.css diff --git a/views/layout.erb b/views/layout.erb index f677a8f..e5a8150 100644 --- a/views/layout.erb +++ b/views/layout.erb @@ -4,10 +4,10 @@ - + - +
From 3a8689eb77d7412011a6e01278817cad997e494f Mon Sep 17 00:00:00 2001 From: Tanner Welsh Date: Thu, 30 Oct 2014 00:45:59 -0400 Subject: [PATCH 2/5] New skills are created asynchronously; add jQuery MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds dependency on the jQuery library (loaded via the CDN). Adding a skill using the form on the résumé show page now uses AJAX to allow users to add skills without requiring a full page refresh. Sending an AJAX request to create a new resource generally involves writing JavaScript code that implements the following steps: - Bind to the "submit" event of a form - Prevent the form from doing what it would normally do (usually: send an HTTP POST request) - Send an XMLHTTPRequest with the "POST" method, passing along the form data in the request body (in this case, we're using jQuery's `$.post` method to abstract over the logic of sending a request) - Add an event listener to handle the response when it comes back (this is the asynchronous part: we give the browser a function and say "hey, whenever you hear back from the server, run this code.") --- linkedout.rb | 8 ++++---- public/js/main.js | 49 +++++++++++++++++++++++++++++++++++++++++++++++ views/layout.erb | 5 +++++ 3 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 public/js/main.js diff --git a/linkedout.rb b/linkedout.rb index 249fadc..c42ee5b 100644 --- a/linkedout.rb +++ b/linkedout.rb @@ -62,13 +62,13 @@ def default_user end post "/skills" do - skill_attrs = params[:skill] - skill_attrs.merge!({ :user => default_user }) + skill_name = params[:skill_name] - skill = Skill.new(skill_attrs) + skill = Skill.new({ :name => skill_name, :user => default_user }) skill.save - redirect "/" + # respond with an HTML partial for just this skill + partial :'partials/skill', :locals => { :skill => skill } end put "/skills/edit" do diff --git a/public/js/main.js b/public/js/main.js new file mode 100644 index 0000000..74fe9e8 --- /dev/null +++ b/public/js/main.js @@ -0,0 +1,49 @@ +// Wait to execute all code until the document is ready +// (i.e. all of the DOM nodes have been loaded) +$(document).ready(function() { + + // Select the new skill form using its name attribute + var $newSkillForm = $('form[name="new_skill"]'); + + // 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(); + + // Grab the new skill name from the form, since this is + // the data we want to send to the server + var newSkillName = $newSkillForm.find('input[name="skill[name]"]') + .val(); + + // Then put it into a JavaScript object so that we can send + // it to the server (where it will be parsed into a Ruby hash) + var newSkillData = { "skill_name": newSkillName }; + + // Send a POST request asynchronously to the "/skills" + // route on the server, passing the serialized form data + // in the request body. + // + // When a user submits the form to create a new skill, + // this will send a request to the server to save this + // skill to the database + // + // The anonymous function (third argument passed to $.post) + // is 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", newSkillData, function(newSkillHTML) { + + // Add the new skill to the list, just before the form's + // parent 'li' element + $newSkillForm.parent('li') + .before(newSkillHTML); + + // Reset the form so that new skills can be added + $newSkillForm.get(0).reset(); + + }); + }); +}); diff --git a/views/layout.erb b/views/layout.erb index e5a8150..45dff07 100644 --- a/views/layout.erb +++ b/views/layout.erb @@ -17,5 +17,10 @@ <%= yield %> + + + + + From ad8cbd6fbe596bcc52a219378e1295aa2710976b Mon Sep 17 00:00:00 2001 From: Tanner Welsh Date: Thu, 30 Oct 2014 00:50:20 -0400 Subject: [PATCH 3/5] Send all data from new skill, not just name By sending the all of the form data (using jQuery's .serialize() function), the server can create a new skill from the form data whether it is sent via AJAX or a regular HTTP POST request. This is useful in case the user has disabled JavaScript. To determine what kind of response to send back, we call `.xhr?` on the `request` object, which returns true if the server is responding to an AJAX request. That way, we can send back whatever the JavaScript wants (in this case it is a snippet of HTML). --- linkedout.rb | 12 ++++++++---- public/js/main.js | 11 +++-------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/linkedout.rb b/linkedout.rb index c42ee5b..47daf78 100644 --- a/linkedout.rb +++ b/linkedout.rb @@ -62,13 +62,17 @@ def default_user end post "/skills" do - skill_name = params[:skill_name] + skill_attrs = params[:skill] + skill_attrs.merge!({ :user => default_user }) - skill = Skill.new({ :name => skill_name, :user => default_user }) + skill = Skill.new(skill_attrs) skill.save - # respond with an HTML partial for just this skill - partial :'partials/skill', :locals => { :skill => skill } + if request.xhr? # this will return true when handling an AJAX request + partial :'partials/skill', :locals => { :skill => skill } + else + redirect "/" + end end put "/skills/edit" do diff --git a/public/js/main.js b/public/js/main.js index 74fe9e8..11dad66 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -12,14 +12,9 @@ $(document).ready(function() { // Instead, we'll handle the request with AJAX evt.preventDefault(); - // Grab the new skill name from the form, since this is - // the data we want to send to the server - var newSkillName = $newSkillForm.find('input[name="skill[name]"]') - .val(); - - // Then put it into a JavaScript object so that we can send - // it to the server (where it will be parsed into a Ruby hash) - var newSkillData = { "skill_name": newSkillName }; + // Grab the form data and serialize it as a string in + // standard URL-encoded notation + var newSkillData = $(this).serialize(); // Send a POST request asynchronously to the "/skills" // route on the server, passing the serialized form data From b3fe17b61748979b9ca70a5fca25e7acbb1ee4be Mon Sep 17 00:00:00 2001 From: Tanner Welsh Date: Thu, 30 Oct 2014 00:56:22 -0400 Subject: [PATCH 4/5] Refactor main.js to use named functions Better to break up the work into separate functions with explicit purposes. +1 code style points. --- public/js/main.js | 46 +++++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/public/js/main.js b/public/js/main.js index 11dad66..6d5da37 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -1,20 +1,15 @@ -// Wait to execute all code until the document is ready -// (i.e. all of the DOM nodes have been loaded) -$(document).ready(function() { - - // Select the new skill form using its name attribute - var $newSkillForm = $('form[name="new_skill"]'); +var createNewSkillsOnSubmit = function() { + var $newSkillForm = getNewSkillForm(); // 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(); // Grab the form data and serialize it as a string in // standard URL-encoded notation - var newSkillData = $(this).serialize(); + var skillFormData = $(this).serialize(); // Send a POST request asynchronously to the "/skills" // route on the server, passing the serialized form data @@ -24,21 +19,34 @@ $(document).ready(function() { // this will send a request to the server to save this // skill to the database // - // The anonymous function (third argument passed to $.post) - // is executed when the browser receives a response from + // The insertNewSkillIntoDOM function (argument 3) 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", newSkillData, function(newSkillHTML) { + $.post("/skills", skillFormData, insertNewSkillIntoDOM); + }); +}; - // Add the new skill to the list, just before the form's - // parent 'li' element - $newSkillForm.parent('li') - .before(newSkillHTML); +var getNewSkillForm = function() { + // Select the new skill form using its name attribute + return $('form[name="new_skill"]'); +}; - // Reset the form so that new skills can be added - $newSkillForm.get(0).reset(); +var insertNewSkillIntoDOM = function(newSkillHTML) { + var $newSkillForm = getNewSkillForm(); - }); - }); + // Add the new skill to the list, just before the form's + // parent 'li' element + $newSkillForm.parent('li').before(newSkillHTML); + + // Reset the form so that new skills can be added + $newSkillForm.get(0).reset(); +}; + + +// Wait to execute all code until the document is ready +// (i.e. all of the DOM nodes have been loaded) +$(document).ready(function() { + createNewSkillsOnSubmit(); }); From 584754bc52b7aa35e4cfe2d078b23d2e45e60dc7 Mon Sep 17 00:00:00 2001 From: Tanner Welsh Date: Thu, 30 Oct 2014 00:59:03 -0400 Subject: [PATCH 5/5] =?UTF-8?q?User=20can=20create=20new=20jobs=20from=20r?= =?UTF-8?q?=C3=A9sum=C3=A9=20show=20page=20asynchronously?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same deal as with skills, now jobs can be added asynchronously too. --- linkedout.rb | 6 +++++- public/js/main.js | 26 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/linkedout.rb b/linkedout.rb index 47daf78..b00e531 100644 --- a/linkedout.rb +++ b/linkedout.rb @@ -47,7 +47,11 @@ def default_user job = Job.new(job_attrs) job.save - redirect "/" + if request.xhr? + partial :'partials/job', :locals => { :job => job } + else + redirect "/" + end end put "/jobs/edit" do diff --git a/public/js/main.js b/public/js/main.js index 6d5da37..7586103 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -45,8 +45,34 @@ var insertNewSkillIntoDOM = function(newSkillHTML) { }; +var createNewJobsOnSubmit = function() { + var $newJobForm = getNewJobForm(); + + $newJobForm.submit(function (evt) { + evt.preventDefault(); + + var jobFormData = $(this).serialize(); + + $.post("/jobs", jobFormData, insertNewJobIntoDOM); + }); +}; + +var getNewJobForm = function() { + return $('form[name="new_job"]'); +}; + +var insertNewJobIntoDOM = function(newJobHTML) { + var $newJobForm = getNewJobForm(); + + $newJobForm.parent('li').before(newJobHTML); + + $newJobForm.get(0).reset(); +}; + + // Wait to execute all code until the document is ready // (i.e. all of the DOM nodes have been loaded) $(document).ready(function() { createNewSkillsOnSubmit(); + createNewJobsOnSubmit(); });