Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Java Spring Basics Using the MVC Architecture Wrappin' It Up

Test Test
Test Test
21,581 Points

Here are the answers for Implementing the Favorites Page and the Search Page

Hi! If you want to check how Favorite Page and Search Page can be implemented here's one way to do it:

Favorites Page:

1) Method in GifRepository:

You can use for-each loop or use a stream (for the latter you need Java version 1.8):

//for-each loop
   public List<Gif> findByFavoriteMark() {
        List<Gif> gifs = new ArrayList<>();
        for(Gif gif : ALL_GIFS) {
            if(gif.isFavorite()) {
                gifs.add(gif);
            }
        }
        return gifs;
    }

//stream
    public List<Gif> findByFavoriteMark() {
        return ALL_GIFS
                .stream()
                .filter(Gif::isFavorite)
                .collect(Collectors.toList());
    }

2) Method in GifController (make sure you name List as below (so it can correspond with favorites.html, which was included in the project)). After that you can run your app.

  @RequestMapping("/favorites")
    public String listFavorites(ModelMap modelMap) {
        List<Gif> gifs = gifRepository.findByFavoriteMark();
        modelMap.put("gifs", gifs);
        return "favorites";
    }

II. Search Page

1) Method in GifRepository:

//for-each loop
    public List<Gif> findBySearch(String search) {
        List<Gif> gifs = new ArrayList<>();
        for(Gif gif : ALL_GIFS) {
            if(gif.getName().toLowerCase().contains(search)) {
                gifs.add(gif);
            }
        }
        return gifs;
    }

//or stream
    public List<Gif> findBySearch(String search) {
        return ALL_GIFS
                .stream()
                .filter(e-> e.getName().toLowerCase().contains(search))
                .collect(Collectors.toList());
    }

2) Method in GifController

 @RequestMapping(value="/", params="q")
    public String listSearch(@RequestParam ("q") String q, ModelMap modelMap) {
        List<Gif> gifs= gifRepository.findBySearch(q);
        modelMap.put("gifs", gifs);
        return "search";
    }

3) Make sure to create html file (you can write it yourself or just copy "favorites.html" and rename it to "search.html":

Then if you type, for exampe, "and" in the search bar, you will see 3 gifs (android-explosion, ben-and-mike, infinite-andrew) PS: Hope it helps:)

Lukasz Wozniak
Lukasz Wozniak
7,383 Points

You don't have to make specific template for search result page. Use "home.html"

You should also make search parameter in findBySearch method to lowercase.

8 Answers

Kirill Babkin
Kirill Babkin
19,940 Points

@muhammadammaraamir

In the form tag you can come up with any URI that you later will catch in your controller.

<form th:action="@{/search}" method="get">
   <div class="input-field">
       <input name="q" type="search" placeholder="Search all gifs..." required="required" autocomplete="off"/>
       <i class="material-icons">search</i>
   </div>
</form>

and in your controller you can request mapping to /search and i returned favorite.html just because you don't need to do anything in there to display list of gifs and as @Lukasz Wozniak stated you can return a home page -> "home.html" the idea is the same.

@RequestMapping("/search")
    public String listSearch(@RequestParam String q, ModelMap modelMap){
        List<Gif> allGifs = gifRepository.findIfContains(q);
        modelMap.put("gifs", allGifs);
        return "favorites";
    }

and in you GifRepository you can create new method to find a set of gifs.

public List<Gif> findIfContains(String query){
        List<Gif> gifs = new ArrayList<>();
        for (Gif gif : ALL_GIFS){
            if(gif.getName().toLowerCase().contains(query.trim().toLowerCase())){
                gifs.add(gif);
            }
        }
        return gifs;
    }

and of course you can use stream if you wish so.

Thomas Salai
Thomas Salai
4,964 Points

@Konstantin Kochetov Thanks for posting your solutions.

@Lukasz Wozniak, would you like to share us also your solutions ?

cheers

Julia Grill
Julia Grill
21,690 Points

Hi,

What Lukasz meant was that, when casting the gif name to all lower cases, one should also call search.toLowerCase(), in order to enable proper searching; to his second point, he made the right point, that one doesn't have to create a separate html file in order to make the search function work, instead, you can simply return "home" in list search, hope that clears things up!

Cheers

Bruno Aldo Lunardi
Bruno Aldo Lunardi
15,795 Points

i tried a similar solution as provided above by Kiril but i'm getting an error, page not found

    @RequestMapping("/search")
    public String findBySearch(@PathVariable String q, ModelMap modelMap) {
        List<Gif> gifs = repository.findByName(q);
        modelMap.put("gif",gifs);
        return "favorites";
    }

i'm getting this: http://localhost:8080/@%7B/search%7D?q=compiler-robot

Bear in mind that i changed the <form> to <form action="@{search}" located in my home.html

I think you should change "gif" to "gifs" in your modelMap and add "th:" before "action" in your form tag.

Boban Talevski
Boban Talevski
24,793 Points

Is there a specific reason we need to add "th:" before action in the search form tag? The way I see this, is that we should use "th:" if there's anything dynamic that we need to put there.

But I don't think we need to, since we'll be using a fixed URI for the search request, like simply "/search" for example. I mean I didn't even think of adding "th:" before action and everything works as expected. Or am I missing something?

Lukasz Wozniak
Lukasz Wozniak
7,383 Points

Yes, that's exactly what i meant :)

Thomas Salai
Thomas Salai
4,964 Points

Thanks you very much to you all for your explanations.

What are you supposed to write in the action attribute of the form? How will our a URI with the format /search?q=compiler be generated?

Nelson Fleig
Nelson Fleig
23,674 Points

Good work every one. Would have been nice to learn how to do "includes" a-la-PHP to avoid copying and repasting the navigation and footer on every html page. I'm so spoiled by these awesome Team Treehouse videos xD

Did anyone figure out how to make the search appear on the current page? Could you please post your code?

I coded a new search controller and added the logic mentioned above of searching the query by iterating the GifRepository and storing them in a new list and sending the list in the model map. I created a new html page (search.html) . I am getting an error of

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title><!DOCTYPE html>
        <html lang="en" xmlns:th="http://www.thymeleaf.org">
        <head>
            <meta charset="UTF-8"/>
            <meta name="viewport" content="width=device-width, initial-scale=1"/>

            <link rel="icon" th:href="@{/favicon.png}" />

            <link rel="stylesheet" th:href="@{/vendor/materialize/css/materialize.css}" />
            <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
            <link rel="stylesheet" th:href="@{/app.css}" />

            <title>giflib | Favorites</title>
        </head>
<body>
<div class="navbar-fixed">
    <nav>
        <div class="container">
            <a th:href="@{/}" class="brand-logo">gif<span>.</span>lib</a>
            <a href="#" data-activates="mobile-nav" class="button-collapse right"><i class="material-icons">menu</i></a>
            <ul class="right hide-on-med-and-down">
                <li><a th:href="@{/}">Explore</a></li>
                <li><a th:href="@{/categories}">Categories</a></li>
                <li class="active"><a th:href="@{/favorites}">Favorites</a></li>
            </ul>
            <ul id="mobile-nav" class="side-nav">
                <li><a th:href="@{/}">Explore</a></li>
                <li><a th:href="@{/categories}">Categories</a></li>
                <li class="active"><a th:href="@{/favorites}">Favorites</a></li>
            </ul>
        </div>
    </nav>
</div>
<div class="gifs container">
    <div class="row">
        <div th:each="gif : ${matchedGifs}" class="col s12 l4">
            <a th:href="@{'/gif/' + ${gif.name}}">
                <img th:src="@{'/gifs/' + ${gif.name} + '.gif'}" />
                <a href="#" th:class="(${gif.favorite} ? 'un' : '') + 'mark favorite'"></a>
            </a>
        </div>
    </div>
</div>

<script th:src="@{/vendor/jquery/jquery-1.11.3.js}"></script>
<script th:src="@{/vendor/materialize/js/materialize.js}"></script>
<script th:src="@{/app.js}"></script>
</body>
</html></title>
</head>
<body>

</body>
</html>

Error: This application has no explicit mapping for /error, so you are seeing this as a fallback.

Boban Talevski
Boban Talevski
24,793 Points

I think it's best to create a new post/question for this and provide some more code.

For example, are you sure you are reaching this search.html page at all? What's the action on your form tag? What's your implementation of the method in GifControler for handling a search request? What's your implementation of the search method in GifRepository?