Heads up! To view this whole video, sign in with your Courses account or enroll in your free 7-day trial. Sign In Enroll
Well done!
You have completed Intro to Java Web Development with Spark!
Preview
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.
Directions
- 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.
Related Discussions
Have questions about this video? Start a discussion with the community and Treehouse staff.
Sign upRelated Discussions
Have questions about this video? Start a discussion with the community and Treehouse staff.
Sign up
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 RunTimeException,
6:21
right, because we don't know until
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 dot add,
which is voterUserName.
8:16
Cool, that makes sense?
8:24
Sets, when you call add on Sets,
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 params.
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 getVoteCount, but
11:35
it's called voteCount
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 upYou need to sign up for Treehouse in order to set up Workspace
Sign up