- Overview
- Transcript
6.4 PHP and jQuery: Part 2
In the previous lesson, we focused exclusively on the PHP aspect of our little project. Today, we get to switch over to the JavaScript, and enhance the web site. We'll pay special attention to code structure, the AJAX method, and communicating with the server.
1.Introduction1 lesson, 00:38
1.1Welcome00:38
2.The Basics7 lessons, 1:33:04
2.1Hello jQuery10:52
2.2Not So Fast, jQuery06:54
2.3The Basics of Querying the DOM15:21
2.4Events 10119:58
2.5Events 20115:21
2.6Bind...Live...Delegate...Huh?10:49
2.7Creating and Appending Content13:49
3.Effects9 lessons, 2:39:59
3.1Slides and Structure23:46
3.2The this Keyword09:35
3.3Modifying Effect Speeds05:47
3.4Creating Custom Effect Methods07:16
3.5Full Control With animate21:34
3.6Homework Solutions12:15
3.7The Obligatory Slider (First Attempt)32:50
3.8Prototypal Inheritance and Refactoring the Slider34:03
3.9Your Questions Answered12:53
4.Utilities4 lessons, 59:31
4.1$.each and Templating12:49
4.2Say Hello to Handlebars15:09
4.3The Twitter API22:11
4.4Filtering with jQuery.grep09:22
5.Custom Events1 lesson, 25:17
5.1Custom Events and the Observer Pattern25:17
6.AJAX5 lessons, 1:33:13
6.1Loading Pages Asynchronously11:10
6.2Interacting with the Server-Side11:24
6.3PHP and jQuery: Part 122:37
6.4PHP and jQuery: Part 228:30
6.5Deferreds19:32
7.Plugin Development1 lesson, 45:57
7.1Head First Into Plugin Development45:57
8.Exit1 lesson, 01:06
8.1Goodbye01:06
6.4 PHP and jQuery: Part 2
[SOUND] Now we're going to focus on the JavaScript integration. We're going to make all of this ajaxified. So when we click on one of these letters, rather than performing a full postback and then linking to a different page like as we've done right here. We're going to do all of this on a single page and we're going to make these database queries asynchronously. So I will click Back and the first step is to go into our scripts file. JavaScript > scripts and we're going to create a object literal. And we'll call this Actors. Now our Actors object is going to have an init method, that gets everything rolling. You should be familiar with this by now. And then we'll trigger it, Actors.init(). So one of the first things that I wanna do is find all of our events because if you think about it, we wanna listen for when the user makes a selection right here. When one of those items in the dropdown is changed, so that is an event we're going to listen to. I'm going to add a method called, specifically for dealing with this, this.bindEvents(), like so. Now we'll create it, and bindEvents, we're going to say this. And we need a way to access if we come back to index.template. We need a way to access this select with an ID of q. Now what we could do is simply something like this, get the element with an ID of q, and listen for when it is changed. That's a new event that you're going to learn about today. But again we wanna be careful about having all of this specific dom access in the ID's and the class names so let's pass into the init method instead that way we can easily adjust it. A property name of letterSelection, and then make that equal to a query for the element with an ID of q. So now when we call the init method, this will accept a config object, and let's make that available. So we'll say this.config equals config. So now we can access this object by actors.config. So now we can say that this.config.letterSelection, which now references this right here. And we're going listen for when it's changed. When the user selects a new item from that dropdown. And when they do, we're going to call a method called fetchActors. So let's create that right now. And right here we're simply going to log fetching just to make sure that we're not making any mistakes. But before we do this, let's come back to our footer file because we haven't imported any of the scripts that we need. So we want to make sure that we're working with jQuery. We're going to be working with handlebars, and then we also need to import our scripts.js file. So we'll do that one, js/scripts.js, and then I will also import jQuery and handlebars as we've been doing in the last couple applications. Okay, so now let's come back to scripts.js. When the user makes a selection, we're gonna console.log fetching to make sure it works. I will select the letter d. And now, you can see as soon as I changed it, we logged fetching. So that method, we know, is going to be called every time the user makes a selection. So now we can use some Ajax. We're going to make an Ajax request. Now this Ajax method needs some parameters. The first one is going to be, what are we sending through? Well, we're going to send through the serialized form that we learned about in the last lesson. If you need a refresher, go back and watch that, where we learned that we can serialize a form, and that sets it up with key-value pairs, so that it can be posted wherever it needs to go. So let's create a new property called form. And we're simply going to be generic here, but if you want, you could give that a better name. You probably should. And, in fact, I'm going to. Just to make sure that we're doing this with best practices in mind. So we'll give this an ID or a class. An ID makes sense. And now I'm going to replace that like so. Now we have a reference to our form. And now we can say, you would think, this.config.form. And this is where I know a lot of you are getting tripped up. Because you think well, this, in this context, refers to the actors object. But now, if we console.log this within fetchActors one more time, and now, this refers to the select, and what you have to remember is that this fetchActors method is being triggered by jQuery, right here. So we're telling jQuery when an object is selected. Then, I want you to trigger the fetchActors method. And what jQuery's doing behind the scenes, it's setting up what this will be equal to when the callback or when this method is triggered. So now within here, for your convenience, and it's really helpful, this is going to refer to the select where the change occurred. So, if you want to get back to the Actors object, within here, let's create a variable called self. And I'm going to make this equal to the Actors object. And now, if we uncomment this, we can reference self.config.form. And now we're referencing the Actors object instead. Then we will serialize it. Next we'll set up the URL. Where are we going? So we're gonna go to index.php. What is the type? That's gonna be a post. And then we're sending through the data. And also for now, we're gonna use this success callback. And this is a little dated at this point, but it'll be fine for the time being. And we're going to log the results. And what we'll also do is log finished, just in case results is empty, we can still see some kind of visual feedback. So let's go through this again. When the user makes a selection, they choose letter d, then fetchActors is going to be called. FetchActors is going to create an Ajax request to index.php. And it's going to send through the serialized form, so it's going to send through essentially the letter that the user selected. So now, index.php could receive an asynchronous request. It will run through this page. It's going to say is isset POST q? Well yes, it will be set because we are sending that through, and if it helps you, right here, I'm going to log this to the console. Reload, make a selection. And in this case we're getting the full results because we're running the Ajax request. For now I'm gonna return just so we get that one result. And now you'll see that's what serialize is doing. And that's going to be sent through to index.php. At which point that file can access it through the host super global. So let's get rid of all of this. Index.php is going to run. Isset POST q? Yes, and then we create this Actors, but we need to be able to echo something back. So for now, let's just echo RECEIVED and then we'll return. So let's try that. Reload the page. Make a selection, D. It says finished. That's from our callback. And then we get RECEIVED. Cool. So at this point, let's see what would happen if we did echo Actors. Does that work? Let's try it. Make selection d, and we're just going to get array because that how you would, you can't echo an object. So another way would be to encode this. Json_encode. Let's try that. Now we've encoded it with PHP, I'll click d. And now look what we get in response. Okay, so now we're getting a lot closer to what we want. But I wanna show you something else. Instead of logging it, let's log typeof results. Are we getting a string back, or are we actually getting an object? Let's choose d. And we get string. Hm. Well, we encoded it with PHP. We know that. So when get it back, it's a string. Why don't we tell jQuery that the data type that we expect in response is going to be JSON? Now let's do it one more time. Select d, and because we did that, now jQuery knows to parse it as JSON. So at this point, one more time, just log the object. Click d. There are no e's so let's do d. And we get a list of all these objects. So now if we want to get, let's say, the first name, we can say results, the first item and then we'll get the first_name. Click d, and there we go, we've made an asynchronous request to a file that will query a database and return the results, and we did that without any page refresh. Really neat. So, now that we know that we have the results, we ned to set up some templates. I'm gonna come back to index.template.php, and here is our actor list. But below it, I wanna set up a JavaScript template as well. We'll give this an id of actor_list_template. And within here, we more or less want to duplicate what we have here. Now there are ways to combine this, but I think it's easier for now if we keep it very distinct. So we will create a list item and set up that data-actor_id custom attribute and that will be equal to actor_id, we'll call it. Next, within here, we're going to have the anchor tag. And rather than using concatenation to do the first name with the last name, let's use a handlebars helper and we'll call it fullName. That's what we wanna do and we'll pass in this and this ultimately as we're filtering through that object that's returned. This will be equal to that one item, where we can access the properties that we need. Now the href again is going to be actor.php. Actor_id equals actor_id. And the final step to here is because we know we're getting all of those objects in response, we need to be able to tell handlebars to filter through those. So we'll say for each this, and then we'll end our each statement. So if this is really confusing to you at this point, go back to the handlebars episode where we go over all of this. So now that we've created a template, let's go back to scripts and we will reference it here below. We will call it actorListTemplate, and we're going to fetch that from the DOM, like so. And that should be business as usual, we've gone over this a lot in the last several lessons. So now we can come back to the Ajax, and before we continue I wanna have a different method that will set up all of our templates. So we'll call this setupTemplates and this method's only job is to take all of the templates that we have and compile them. So we'll say this.config.actor_list_template and we're going to overwrite that, and we'll make that equal to handlebars, and we're going to compile that. I'll hide the side bar so we have a little more room. There we go. So now we're overwriting this value right here. And we're just compiling it. We're going to handlebars.compile that. So now that this is the function that we can call. We can find the context and we're good to go. Now if we come back to index.template, if you'll remember, we had this fullName helper. So let's go ahead and register that while we're within this method. Handlebars.registerHelper. And what are we gonna call it? FullName. That will execute a function. Now this function needs to receive the object that we're working with. So we will call it the actor, cuz remember, if we come back, we will pass that object to the fullName function, or the fullName callback. So now that object is represented by actor, and we will simply return actor's first name, a space, and the actor's last name. Good. So we can execute that right up here. Set up the templates. We bind the event. Each method is responsible for its own little piece of the puzzle. Next we come back to our success callback function. And we want to take the results and append it to this unordered list with a class of actors_list. So let's come back and add a reference to that. We'll call it actorList. Get the ul with the class of actors_list. Good. So now we come to our success callback function, and we can say self.config.actorsList and we are going to append to that unordered list. And what are we appending? Well we want to bind the results to the handlebars template. Self.config.actorListTemplate. That is a function and we're going to pass in the context or the data, which is results. Okay. So let's see where we are at this point. Let's go back and try it out. I'm gonna select a letter, d. Up, and we're getting an error, cannot call method match of null. And I actually received a couple tweets about this. When you receive this, it's because handlebars can't find your template. So you either referenced it wrong or maybe you emptied out the container where it no longer exists. If I come to index.template.php we can easily see that the problem is we have our template contained within this php if expression. So it's saying if there are actors, only on that condition should we create this. But we want that template to always be available. So I'm going to remove all of that and add our template below here, like so. So now we know that that will always be available. So let's come back to scripts.js and if you want, let's see what happens if we console that log, that right there. Select a letter, we hit d, and take a look. We have replaced all the information like so. But now we're not seeing it on the page and it's because of that same thing. We know the results are right, we know that we are appending it to the actors list, and if that's not happening, it means something's wrong with the actors list. And again we come back, it's because the actors list is kind of optimized for this php right here. So if actors isn't available, it doesn't get created. So now we know that we do want it to display, so I can remove this php entirely and now, once again≤ we can take our script and put it back within the UL. All right, so now let's try it. I'll hit the letter d and now we get all of those links, really neat. And that's all being done with JavaScript and Ajax. So now we have all of these links, if I click on one of them, we haven't set up any events, so it just directly goes there. But already we've implemented a lot right here, and what's great about this is, if JavaScript is disabled, it's still going to work. You don't want to get in those situations where your application, especially if it's a simple website, exclusively relies on JavaScript to execute properly. We want it to work in both situations. JavaScript is just being used as a helper. So we go to b, and we go to a. But now, you see this new issue we're appending all of these contents on. So again, if I select b, we get the b's. But if I hit d, we get the d's, but now they're added on. So what we want to do is empty that out each time, and we can do that fairly easily. We'll come back to scripts.js and right here at the top we'll say self.config.actorsList.empty. Or because we can chain in jQuery, we can simply remove that, empty it out, and then append the results. One more time, click b we get b, click c we get c, click d we get d, very snappy. So the next step is, I see here we still have this button, but we're not using it because we're using JavaScript instead. So at the top why don't we, you probably have an idea on it, but for now, I'm going to be a little lazy and we'll remove it like so. And now when the page loads we don't need that button and so we just hide it. Choose a letter but now it's important to note again if I load the page and we do a letter where there are no results, there's obviously no actors where the last name begins with e, we don't get anything. We should provide some level of response for the user. So why don't we come back, and right here where we fetched the actors, we can say if there were results, so we can see if we can get access to that first item in the results. If we were able to, then we have results. Otherwise, nothing was returned. So if that's the case, then we will do that. Otherwise, self.config.actorsList, and we will append, for now we'll just append a list item that says nothing returned. That way they at least get a little feedback. Click e, nothing returned. D, they get the results. C, but go to maybe something like z, oh there are z's. Let's do x. There we go. Nothing returned. But again, we get that same problem where now we're appending to it because we're only emptying if there were results, so at the top we'll do it. Self.config.actorsList, and we'll empty it. Then we can remove that, and now it's going to be emptied every time. One more time, b. Or we could do x, and there we go that's working a lot better. Next we wanna listen for when the user clicks on one of these links because we don't want to go to actor.php, we want to again load that asynchronously. So I'm gonna come back to our scripts.js and bind another event, and we're going to listen for when the actors list we're going to listen for when one of the anchors is clicked, or one of the list items. So we'll listen for a click, we'll use event delegation to listen for the list item, and when it is clicked, we're going to call a new method called displayAuthorInfo. Let's create that at the bottom. And now because we know we are going to be displaying information about the actor, we need to set up another template. So I'll do this right here at the bottom. And we'll get some more breathing room. And this template, we'll call it actor_info_template. And it will be wrapped within a div with a class of actor_info. And within here we're going to have a paragraph tag that will contain the information about the actor. So we'll do it like so. And then I also want a close button, so we'll do span. We'll give it a class of close and then use an x to represent that. Because we're going to work with this, I need to store a reference to it, so we'll do that at the bottom, like so. And we'll make this JavaScript friendly. So we're gonna use camelCase, that's the most common when working in JavaScript. So the first thing is as before, we'll create a variable called self and make that equal to the Actors object. And then when this method is called, that means the user clicked on a link, and they want to view information about the user. So they clicked on the link. Let's make sure that we prevent the default, and then perform our Ajax request. But this time, once again, the url will be index.php. We know that. And the type is going to be POST. So what I'm realizing here is for this little application, we're using the same settings for all of our Ajax requests. So if that is the case for your whole application, it's gonna be the exact same settings. Rather than repeating ourselves, let's go back to the top and let's do some Ajax setup. AjaxSetup and the url is going to be index.php. All Ajax requests by default are going to go here and we're going to set the type for all of them to POST. So now we've instructed jQuery for all Ajax requests, unless overwritten, to use these settings. And that means now we can get rid of these up here, and then if we come down right here, we can get rid of those as well. So now we only need to pass the data, data, and we're going to pass through we'll call it actor_id is the property name. And then we need to pass through the ID of the user. And if you'll remember, when we make a selection, and we inspect element, we can access the ID in the database, or in the table by referencing the list items data actor_id attribute. So we can grab that by saying this, call the data method, and we'll pass in an actor_id like so, good. So now we're sending a Ajax request to index.php and we're sending through the ID of the actor. Now if we come back to index.php we know that there's some things to be aware of now. Right now we've optimized it for JavaScript because we echoed out those results. But remember, if we're working with PHP, we're returning and then we never load anything else. So it seems like what we need to do is have a way to detect whether it was an Ajax request that loaded this page or not, and we can do that pretty easily. I'm gonna open up functions.php, and at the top, we're going to create a helper function called isXHR. Now, jquery executes these Ajax calls, it actually sets a header called HTTP_X_REQUESTED_WITH. So we can return from this function, isset SERVER HTTP_REQUESTED_WITH. So now, this will either return true or false. If we make an Ajax request. Now, we have a function we can use to determine was that an Ajax request that triggered this page, or, was it just a regular page load? Which means now, we can say at the top, we'll do this. If isXHR and isset POST q, in that case, we can grab this information right here, paste it in, and we can even shorten this just a bit. We can copy that, paste it in here and we can echo that out and return. Just to make sure nothing else on the page runs. Now we know that we want to connect on either condition. So we will place that at the top. So now whenever the page loads, we're going to connect to the database. And then we say was it a Ajax request essentially and there is a q key in the post superglobal? In that case, echo that out for the JavaScript. Otherwise we're going to run right here and this will just be for regular PHP stuff, in which case we can get rid of that entirely. There we go. So now we need to do one more and we're going to listen for if once again isXHR and we have the actor ID key in the super global available. So we can see if isset POST actor-id. In that case we know that an Ajax request triggered it, and we do have an actor's ID available. And then we simply do info, get_actor_info, and we're going to send through the actor's ID, and in this case we don't need the actor's first name or last name because we're still on that page. So we're going to return the film_info column from the database and return. All right, let's try that. Reload the page, open up the console, choose a letter, click on it, that's going to make the Ajax request. But if we come back to scripts.js we don't have any kind of success callback. Now we could use that success callback method, but that's not as flexible, there are better ways and we're going to take a look at those in the next lesson, but for now just know at a very high level that these Ajax methods return a jQuery version of the XHR object. I know that's a little confusing, but what that means is it implements what we call the promises interface, and it allows us to trigger many callbacks. So for example, I could say, var jqhxr equals this. And then we could say things like, jqhxr.done. Then we could run a function. Later, we could do it again, and have multiple methods when a specific Ajax request occurs. Now, in this case, because we're not focusing on deferreds just yet, what we'll do is we'll try this. Because it returns that promises interface that we can work with, we can use this little number right here. Done. When it's done, then execute a function. Now alternatively we could also use helpers like success, which makes it easier to understand from the callback methods, like this. But that's going to be deprecated in jQuery 1.8, so try to get in the habit of not doing that. And again we are going to cover all of that in the deferreds lesson. So in this case when it's done we will get the results and we'll say self.config. And we'll get the actorInfo, and that's that div with a class of actorInfo, and we're going to set it to HTML, and once again we're going to bind the template. Now in this case we have set up that new template, but we haven't compiled it just yet. Well, we have a method for doing that, though, so this makes it easy. Set up templates, let's copy that, paste it in, and this time we're going to change it to actorInfoTemplate, and replace it like so. Now we'll come down to the bottom, we'll add another one, actorInfoTemplate, and replace it like so. So now all we're doing is we're just rinsing and repeat. We're grabbing the actor info template, we're storing its HTML, then we come back and we update it and compile it, so now actorInfoTemplate is a function that we can pass the context or the data to accordingly. So let's come back down once again. Now we have access to self.config.actorInfo. We're gonna set its HTML. And let's go ahead and bind the data. Self.config actorInfo, we'll call that function and we're going to pass in the results. So we'll pass in info results like so. Okay, let's try this out. I'm gonna reload the page and bring this all the way down. I'm gonna make a selection, z. I'm gonna click on the name, our event is going to intercept that, it's going to load it, and now its contained like so. But what we really wanna do is have this slide down. So that means we need to do some CSS, but because this is taking up a lot of time, I'm going to go into style.css and just paste in some nice looking things. And also what I'm going to do is fetch normalize.css, which is a popular stylesheet, which normalizes some cross-browser issues. So now let's come back to header and we're going to reference those. Link, normalize.CSS, and change that one to style. Okay, and let's reload the page. So now we have search actors by last name. We choose a letter. It returns those. We can click on something and we're not going to see results, and that's because if we come to style.css, what I'm doing here is hiding the actor info by default, and that way we can slide it down with our JavaScript. So we take our actor information, we set up the HTML, and then we'll slide it down, over 300 milliseconds. Choose letter b, choose a name, and then it slides down. Pretty cool. But now, what if I choose a different name, while it's up? That does get updated, but that's a little jagged. Why don't, when they click on it, we slide up, as well. And we'll do that right at the top. Self.config. We'll take the actor info. And slide it up over 300 milliseconds, like so. Let's try that again. Choose a letter, B. That loads it, it slides down. I choose another one. That slides up, loads it, and then slides down. And one last one, just to make sure everything's working correctly. Very cool. And we're more or less done. The final thing is just to make sure that close button is working, so we'll come back to the events and we'll bind one last one this.config, get the actor info, listen for when it's clicked. But what we're listening for with event delegation when the span with the class of close is clicked, then we'll call the the closeOverlay method and we'll do that at the very bottom. Now this method's only job is to close the actor info. So say actor.config. We will get the actorInfo div and fade it out over 300 milliseconds. That's its only job. We'll try it again. Chose a letter. Choose a name. That's going to load it. You can even have a loading icon if you want. Click x, and that fades out. Or it probably makes more sense to slide up since we're sliding down initially. B, choose a name. It slides down. Once it loads we're done. So we click x. Choose another one. Load it and that's all being done with Ajax. And what's great about this though, is if JavaScript is disabled, it's still going to work. B, click go, choose these. It takes us to the new file or it displays the information and it's still great for them. But with JavaScript we're just making it more convenient. Last one, it slides down, we have our data, we're done and it slides up. So this has been a massive tutorial, it's a simple concept, but if you think about everything we've learned, especially if you're into PHP you may have learned about PDO, better ways to perform your database queries so that SQL injection is protected. We took a look at how to create clean JavaScript. Notice we have this one one Actors object that's available to the global space, and then all of its methods are contained within it. We're binding all of our events in one place. That way if you need to debug something related to an event you don't have to search through 500 lines. You just take a look at your events section. And the same thing for setting up our templates. We have little, small chunks. This chunk is for fetching actors. So, if there's something wrong with that, you know how to debug it, because you're building little chunks rather than one massive procedural bit of code. All right, that's gonna do it for this lesson. There's gonna be lots of questions, I bet. So, you can tweet me @jeffrey_way. Or go to tutsplus.com/forums. I'll see you later. Bye-bye.