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!
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