Last week, we learned about MySQL and the database back-end
. We
also learned about ER diagrams. We also learned about HTML and HTML
forms. (We also learned a little CSS.) On Tuesday we learned about
JavaScript and jQuery.
Today, we'll dynamically update the page by interacting with the server using Ajax.
Next week, we'll learn PHP, the first kind of middle-ware
that
we'll be learning, and we'll finally have the entire process complete.
By the end of today, you will be able to:
People did really well on hwk1 on SQL. You can pick up solutions today.
select name,ma.city as 'MA city',ca.city as 'CA city'
from contact
inner join address as ma using (id)
inner join address as ca using (id)
where ma.state = 'MA' AND ca.state = 'CA';
The insight to this is that you need a join (cross product) of two address tables in order to check for both addresses at once.
Another solution is to use subqueries to get lists of CA and MA people:
select name,city,state
from contact inner join address using (id)
where id in (select id from address where state='MA')
and id in (select id from address where state='CA');
I'll copy/paste those into a MySQL shell so you can see them in action.
$.post(url, formdata, callback, datatype);
$("#myform").serialize()
function cb(response) {
alert("done");
formatData(response);
};
{"title": "Dr. Zhivago",
"tt": 59113,
"year": 1965,
"stars": ["Omar Sharif",
"Julie Cristie",
"Geraldine Chaplin"],
"isGreat": true,
"director": {"name": "David Lean",
"nm": 180,
"birth": "1908-03-25"},
"IMDB rating": 8.0}
JSON.parse()
and JSON.stringify()
are widely supported by JavaScript
implementations.
Here's one of the final examples from the reading, but slightly re-factored: ex1.html. We'll look at the HTML, first. Notice the empty elements.
<p id="response"></p>
<p>
<form method="get" action="/~cs304/php/bleats/insert-bleat.php">
<p>bleat text:
<p><textarea name="bleat" rows=3 columns=60></textarea>
<p><input type="submit" value="add bleat conventional">
<p><input id="ajax_button" type="button" value="add bleat ajax">
</form>
<button id="refresh_button" type="button">refresh list</button>
<div id="bleats"></div>
The code divides into four reasonably sized chunks.
This function puts the timestamp at the top and fades it out. This is unchanged from the reading.
function reportSuccess () {
var now = new Date();
console.log('success at '+now);
$("#response").text('saved at '
+now.toLocaleString()).show();
setTimeout(function () {
$("#response").fadeOut('slow');
},
3000);
}
This function formats an array of bleats. I modified it to set the global and also to report success, using the previous function.
var global_bleats;
function showBleats(bleats) {
global_bleats = bleats;
var i, len = bleats.length;
var listElt = $("<ul>");
for( i = 0; i < len; i++ ) {
var bleat = bleats[i];
$("<li>")
.text(bleat.bid+": ("+bleat.likes+") "+bleat.bleat)
.appendTo(listElt);
}
$("#bleats").empty().append(listElt);
reportSuccess();
}
This function handles form submissions Ajax-style, using the
modified showBleats
function.
$("#ajax_button").click(function () {
$.post("/~cs304/php/bleats/insert-bleat-ajax.php",
$("form").serialize(),
showBleats,
"json"); // end of .post args
}); // end of .click call
This function loads a list of bleats when page loads. It's unchanged from the reading.
function loadBleats() {
$.post("/~cs304/php/bleats/list-bleats-ajax.php",
showBleats,"json");
}
loadBleats();
Why aren't there parens after showBleats
but there are
after serialize()
?
The parens cause the function to be invoked now and passes the
return value to the .post()
method. Instead, we want to pass
the function itself, as a first-class object.
Imagine your friend wants some ice. You have an icemaker. You can either give her some ice, or you can give her the icemaker, and she can run it whenever she wants some ice:
var ice = iceMaker();
$.post(-, -, iceMaker(), 0 );
$.post(-, -, iceMaker, 0 );
What questions do you have so far?
Suppose we want to add a feature where the user can refresh the list (reload the bleats) by clicking a button. This could be useful if they are composing a message but they want to see what new bleats have come in.
Write some code to implement this feature. You can use the ex1.html as your starting point, which has the HTML for the button. Caution: this exercise is easier than you might think, so if you find yourself writing a lot of code, stop and think again.
Here's my solution:
Since the loadBleats()
function already does what we need,
all we have to do is attach it to the button.
$("#refresh_button").click(loadBleats);
Another thing we learned from the reading is the idea of templating. This allows us to
.html()
or .text()
method to do the insertion. (We can
also modify attributes, css, or anything else.)
Here's the example from the reading. Notice how we don't need to know the structure of the HTML; we only need to know the names of the things we are cloning and finding.
function displayFriends() {
var i;
// this element is "off-stage" for now
var people = $('<div>').addClass('people');
for( i = 0; i < friends.length; i++ ) {
var item = friends[i];
var clone = $('#person-template > .person').clone();
clone.find('.name').text(item.name);
clone.find('.house').text(item.house);
clone.find('.pet').text(item.pet);
clone.find('.address').text(item.address);
people.append(clone);
}
// finally add everything to the document all at once
$('#person-container').append(people);
}
Let's go back to the quiz questions. What other questions do you have?
Let's use these ideas to improve the markup in our bleat display. We want the new format to look like this:
Your bleat here. It might be so very long that it wraps around, which allows us to show off the floating info and the margin on the text element.
The CSS magic isn't important here, but the HTML is:
You could write a combination of jQuery and literal strings to create that code, interspersed with data from the bleat object, but it would be:
Let's let HTML do what it's good at, and just use jQuery to clone and modify this.
Write a function named formatBleats
that serves the same
purpose as showBleats
except it uses templating.
You can use the ex2.html as your
starting point, which has the template and CSS code. This
exercise does have a lot of coding in it; I suggest using
the displayFriends()
and showBleats
functions as
starting points. You can draft your function in the following textarea,
and then copy/paste it into the ex2.html
file.
You can use the global_bleats
variable for your testing.
Here's my solution:
function formatBleats(bleats) {
var i, len = bleats.length;
var listElt = $("<ul>");
for( i = 0; i < len; i++ ) {
var bleat = bleats[i];
var clone = $("#bleat_template .bleat").clone();
clone.find(".number").text(bleat.bid);
clone.find(".likes").text(bleat.likes);
clone.find(".text").text(bleat.bleat);
clone.attr('data-bid',bleat.bid); // remember this!!
clone.appendTo(listElt);
}
$("#bleats").empty().append(listElt);
reportSuccess();
}
ex2-solution.html has it fully in action. It's invoked in three different places.
In the reading, we also learned how to add a like event handler on an ancestor of the bleats, allowing the click events to bubble up to it. A quick recap from the reading.
![]()
- The user clicks on a bleat (an LI). That's the event target.
- The click bubbles up
- The event handler at the DIV handles it
Here's the working solution: ajax5.html.
Here's the code to like a bleat, given its ID. Note that this is
updated to use the new formatBleats()
function. Remarkably
brief, given all the work it's doing!
function likeBleat(bid) {
$.post("/~cs304/php/bleats/like-bleat.php",
{"bid": bid},
formatBleats,
"json"); // end of .post args
}
Here's the event handler that does all the hard work:
$("#bleats").click(
function (event) {
var bid = $(event.target).attr('data-bid');
console.log("clicked on BID "+bid);
likeBleat(bid);
});
Deceptively concise:
event
as its arg (the jQuery event object)
event.target
out of the object. That's the LI that was clicked.
data-bid
attribute of the LI.
likeBleat()
to do the rest of the work.
As concise as that code is, it would be easy to turn it into one long line of jQuery, like this:
$("#bleats").click(function (event) { likeBleat($(event.target).attr('data-bid')); });
But look at the frightening blizzard of punctuation at the end!
Let's go back to the quiz questions. What other questions do you have?
Let's see whether liking
still works in our solution to the
second exercise: ex2-solution.html
It doesn't! Why not?
Because the target is now probably the P
or something else
inside the LI. That is, the target is some descendant of the LI.
How could we modify our event handler to deal with this problem?
Work our way up the tree from the event.target
until we
get to the LI, then read the data-bid
off that element.
It might help to know that the jQuery .closest() method goes up the tree, returning the first ancestor that satisfies the selector.
Starting from ex3.html, and the following code for the likes event handler, fix the code and test it.
Note that this does not require a lot of coding!
Here's my solution:
The key is to realize what we're looking for. We could just look
for the LI
or an element of class .bleat
. I
combined them, for clarity. However, that makes the code a bit more
brittle, because if either aspect of the structure changes, this
method finds the empty set, and liking fails again.
$("#bleats").click(
function (event) {
var bid = $(event.target)
.closest("LI.bleat")
.attr('data-bid');
console.log("clicked on BID "+bid);
likeBleat(bid);
});
Here it is in action: ex3-solution.html.
We'll take a minute to look at homework 3 on Ajax
We've come a long way! We learned