Accessing Specific Items12:54 with Craig Dennis
Most web applications follow the same design pattern, there is a list of items and a more detailed view of each item. Let’s ensure you can access a specific item from your list by using what is known as a slug.
- Add a new page that responds to
/ideas/:slug/. The controller should get the model by the slug passed in the url and pass it as the model for the template created in step 2.
- Add a new template for the idea detail page. Make it inherit from our base template.
- The content of the new idea detail page should list everyone who voted. You might need a new keyword.
- Add a form that allows voting for this specific idea. Route it to the existing vote route.
In just about every web application you end up working on 0:00 you're going to encounter a couple of recurring patterns. 0:04 Most notably you're going to show a list of things just like we did with ideas, and 0:07 then you're going to show each of those items on a separate, more detailed page. 0:11 Now, what this means is you'll need a way to uniquely identify each of those items. 0:16 Now, you've probably seen some ugly URLs in your time, 0:20 cruising around the interwebs. 0:23 While we don't have one, most web apps are using a database, and 0:25 they usually provide a way to uniquely identify an item, 0:29 by what is known as an ID, which is short for identifier. 0:32 Well, those are usually numeric, and some sites just pop them in their URL. 0:36 But what does 231 mean to you? 0:41 A better solution and best practice is to use what is known as a slug whose origin 0:44 isn't from that gross creature that hates salt, but comes from the newspaper world. 0:49 There's more info in the teacher's notes. 0:54 Now since URLs can only contain certain characters, 0:56 you need to remove the unallowed ones, yet still want things to be legible. 1:00 Most search engines give heavy weight to turns in the URL, 1:04 meaning they will be more likely to rise to the top. 1:07 Also, for users who might want to share or bookmark this, 1:11 what makes more sense to you, item number 231 or yoda-pez-dispensers? 1:14 Which are you more likely to search for? 1:19 In order for 1:21 us to be able to vote on a specific idea, we're going to need a way to identify it. 1:22 Now, since we don't have a database, and 1:27 this item doesn't have a unique identifier, let's take the slug approach. 1:28 Creating slugs is a part of the Spark framework, but 1:33 we can easily plugin an open source library that will take care of it for us. 1:36 Okay, so let's add our dependency. 1:41 I searched for Java slug, and I found slugify. 1:43 The main independency information is here, so let's grab this group ID 1:48 and let's flip back over to our code, and we'll go to Gradle file. 1:54 Here we're gonna add, we're gonna say, compile, add our dependency, 2:02 calling it slugify, and it's slugify, and it was 2.1.4. 2:07 Okay, then we'll refresh our Gradle dependencies. 2:13 And now that we have that, let's head over to our model so that we can use it. 2:18 So our course ID, our model, is here. 2:22 Go ahead and close the Gradle window, create some more space. 2:27 Okay, so what we want to do, is in the constructor here, 2:30 let's take what they pushed into the title and just make a new slug. 2:35 So we'll say Slugify slugify equals new Slugify. 2:38 Now this Slugify throws an unhandled exception. 2:45 It throws an I/O exception, we're probably not gonna run into that. 2:51 And let's not worry about being too defensive about this. 2:54 We'll just write something out to the screen with this printStackTrace. 2:58 Not really the best practice. 3:03 We probably want to do something deeper than that, 3:04 but we're just working through this right now. 3:07 So we'll do slugify.slugify. 3:11 Here we're gonna pass on the title there, and we're gonna store that slug. 3:13 And we'll store that in a new field, right, this new field here. 3:17 We'll make a new create field slug. 3:22 We'll make it a string called slug. 3:26 And. 3:32 SInce we have a slug here, we'll add a new getter for it, right? 3:37 So we'll generate a new getter Since the title is not updatable, 3:41 right, we can't set the title, only getters are here. 3:47 We don't need to worry about the slug and the title getting out of sync. 3:51 If we added one, we'd have to make sure that that had it, but 3:55 we don't have that right now. 3:57 So let's not worry about that, all right? 3:58 And we wanna be able to find one of these course ideas, 3:59 by this new slug that we just added, right? 4:03 So let's pop over to the interface, our CourseIdeaDAO here, 4:05 and we're going to add the ability to find one by slug. 4:11 So we'll say findBySlug. 4:16 And then what will happen is the user will pass in a slug. 4:18 And if we go to our simple don't do this at home model 4:21 we can have it implement that new method, which now it's complaining about. 4:25 All right, it's complaining that it doesn't implement, so 4:30 select implement methods findBySlug. 4:33 Okay, this is an excellent place to show off the power of streams. 4:36 If you haven't seen these before, this might blow your mind, but 4:41 I just want to show you here how powerful this is. 4:44 Now we know that we're using Java 8, cuz of all the lambdas and 4:46 everything that we've seen so far. 4:49 So one of the really powerful things in Java 8 is this idea of streams. 4:51 And what happens is, you kinda chain. 4:55 So for each one of the ideas, 4:57 what we'll do is we'll pick out everything that doesn't match. 5:00 We'll only keep ones that do match this slug. 5:03 So we'll say filter (idea 5:06 -> idea.getSlug().equals(slug)) 5:09 Cool. 5:19 And whichever the first one is that we find, there's what we want. 5:21 So there's actually a method called findFirst. 5:27 So it's gonna go through that filter. 5:30 The first one that matches is what's gonna come here. 5:31 Now if it doesn't find one, what happens is, 5:34 what's returned is what's known as an optional. 5:40 And an optional is a way of protecting against nulls. 5:42 So they offer a great form of protection. 5:47 Now what we want to return is we want to return a CourseIdea. 5:50 So if we do orElseThrow, this is what we're gonna do if we didn't find one, 5:53 we want an exception to happen, okay? 5:59 So we're gonna say NotFoundException. 6:00 This is a new class that we're gonna make here in a second. 6:03 And this is a method reference, if you remember from the workshop. 6:06 So this will throw this NotFoundException. 6:11 And we'll say here, we'll say Create Class NotFoundException. 6:13 And we want it in the model package. 6:19 And we'll gonna just go ahead and say that this extends a run time exception, 6:21 right, because we don't knowuntil runtime that this is a bug. 6:26 Okay, awesome, so we will try to find the slug. 6:30 And if it doesn't happen, we're gonna throw this NotFoundException. 6:33 It's pretty succinct, right? 6:38 So we're going to stream, we're going to look at each idea. 6:39 And if the idea, if its slug equals the slug that we're looking for, 6:43 we're going to get it back, or else we're going to throw a new exception. 6:47 So we could've written that in the imperative way, 6:51 but I wanted to show off streams a little bit here. 6:54 Okay, so where were we, sorry I got distracted, nerd-sniped. 6:56 All right, so let's add a way to store our voters. 7:00 Since you're only allowed to vote once per idea, let's use a set of user names. 7:04 That'll take care of the problem by ensuring uniqueness, right? 7:09 So, let's flip back to our CourseIdea model here. 7:12 And we're going to add a new sets of strings, so it's a set of user names, 7:18 of strings that are, we'll call voters, sorry. 7:25 And it is definitely Java set, and 7:33 we need to initialize that when it comes in. 7:38 So, we'll say voters = new HashSet. 7:44 All right, so. 7:51 Let's add a convenience method that will allow us to add a voter. 8:00 So we'll say public, and when we add it, we want to let them know whether or 8:05 not it was added successfully. 8:09 So we'll say voterUserName, and 8:11 we'll just return voters that add, which is voterUserName. 8:16 Cool, that makes sense? 8:24 Sets, when you call addOnSet, it will return a true or false. 8:27 So, that's what we want. 8:30 And let's do a count of how many times it's been voted for. 8:32 So let's say, public int getVoteCount. 8:35 That's probably some data that we'll want to show, right? 8:44 How popular is this? 8:48 Okay, now let's add a handler that will track the vote. 8:50 So we want to put the dynamic slug right into the URL. 8:55 So how do we define that path? 8:59 Let's go to Main here. 9:00 So we know that we want it to be a post, right, because they're posting a new vote. 9:03 So it's ideas. 9:09 And how do we add the slug? 9:10 Well, the good news is, it's super straight forward. 9:11 All you need to do is put a colon 9:14 followed by the name of the parameter that you'd like to refer to later. 9:17 So we're gonna say slug/vote. 9:21 And then, of course, we're gonna take a request and response. 9:27 We're gonna open up that route. 9:31 Okay, and in here, 9:35 what we wanna do is, let's go find a CourseIdea, should it exist. 9:36 We'll do dao. 9:41 our new method, findBySlug(). 9:42 And we're going to pull that parameter off of the request, and 9:44 that is just like you might think on a thing called parans. 9:47 So that's pulling whatever was in the URL 9:50 at this place in between these two slashes, slug and slug here. 9:55 Nice, right? 10:00 So Now let's add the user as a voter. 10:02 So we're gonna do idea.addVoter, and 10:08 we're gonna do req.attribute. 10:11 Remember we're using the attributes username. 10:15 Okay, we've now added the voter. 10:25 And let's pop them back to the idea list so 10:29 that they can see the vote count pop up. 10:31 Okay, that looks like a good design there, so 10:40 they'll vote and we'll add a voter to the CourseIdea, should it exist. 10:43 Okay, so now what we need to do is we need to modify that template so 10:48 that we have a place to send those requests. 10:51 So let's go to our ideas template. 10:54 Okay, so let's, inside each one of these list items, 10:57 why don't we make each one of these a form, okay? 11:00 So I'm gonna come in and I'm gonna add a new form, and 11:03 that's gonna go to our new page. 11:07 So we're gonna say ideas. 11:08 And remember, we added that getter for get slug. 11:09 And it's gonna make a vote. 11:14 And that was a method of post. 11:17 Okay, let's move this title up inside the form. 11:23 So each list item is gonna have a form. 11:26 That form is gonna show the title, and then let's show the vote count. 11:30 Remember, we created that method called get vote count, but 11:35 it's called vote count here in this section, and 11:39 we need to give everybody a button that says Vote. 11:46 Okay, so let's reboot that server and let's go add a few. 11:51 So I'm going to view all course ideas. 11:56 Let's add Spark Testing. 11:58 And let's add the Ninja Framework. 12:01 All right, so let's vote. 12:06 Okay, so I'm gonna vote for this Spark Test thing. 12:10 Boom. Awesome. 12:12 It's up to 1. Now what if I do it again? 12:13 Nothing. 12:16 Hm. 12:18 That's right. 12:18 We made it a set so there can only be one. 12:19 We really should be showing them an error message. 12:21 Hm. 12:24 Let's make sure that this was working for different users as well. 12:25 So I'm gonna erase my cookie, I'm gonna refresh this page, and 12:27 it should have us go back to login. 12:31 Let's log in as the thought leader, chalkers. 12:32 Let's go and view all course ideas, and let's have him vote here. 12:36 Great, there's two votes. 12:38 You know what would be cool? 12:40 What if we add a detail page that was just for this one idea, right, like you click 12:41 this and it went to a detail page, and it listed all the voters who voted on it? 12:46 That's actually a great way for 12:50 you to recall a lot of what you've learned through this course. 12:51 Why don't you take that on? 12:53 In the next video, I'll show you how I did it, but 12:55 why don't you give it a spin first? 12:57 I've put some directions in the teacher's notes. 12:59 Have fun, you got this. 13:01
You need to sign up for Treehouse in order to download course files.Sign up