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 with Hibernate User Messages in Spring Making Flash Messages Survive a Redirect

Sagar Thakkar
Sagar Thakkar
8,814 Points

No BackGround colour came on flash msg,coded as shown in video

layout.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">

<head th:fragment="head(page_title)" lang="en">
    <meta charset="UTF-8"/>
    <meta description="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" th:href="@{/vendor/codrops/css/cs-select.css}" />
    <link rel="stylesheet" th:href="@{/vendor/codrops/css/cs-skin-border.css}" />
    <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
    <link rel="stylesheet" th:href="@{/app.css}" />

    <title th:text="'giflib | ' + ${page_title}">giflib</title>
</head>

<body>

<div th:fragment="nav" 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 th:classappend="${#httpServletRequest.servletPath.equals('/upload') ? 'active' : ''}"><a th:href="@{/upload}">Upload</a></li>
                <li th:classappend="${#httpServletRequest.servletPath.equals('/') ? 'active' : ''}"><a th:href="@{/}">Explore</a></li>
                <li th:classappend="${#httpServletRequest.servletPath.equals('/categories') ? 'active' : ''}"><a th:href="@{/categories}">Categories</a></li>
                <li th:classappend="${#httpServletRequest.servletPath.equals('/favorites') ? 'active' : ''}"><a th:href="@{/favorites}">Favorites</a></li>
            </ul>
            <ul id="mobile-nav" class="side-nav">
                <li th:classappend="${#httpServletRequest.servletPath.equals('/upload') ? 'active' : ''}"><a th:href="@{/upload}">Upload</a></li>
                <li th:classappend="${#httpServletRequest.servletPath.equals('/') ? 'active' : ''}"><a th:href="@{/}">Explore</a></li>
                <li th:classappend="${#httpServletRequest.servletPath.equals('/categories') ? 'active' : ''}"><a th:href="@{/categories}">Categories</a></li>
                <li th:classappend="${#httpServletRequest.servletPath.equals('/favorites') ? 'active' : ''}"><a th:href="@{/favorites}">Favorites</a></li>
            </ul>
        </div>
    </nav>
</div>

<div th:fragment="searchbar" class="search-bar container">
    <div class="row">
        <div class="col s12">
            <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>
        </div>
    </div>
</div>

<div th:fragment="flash" th:if="${flash != null}" class="container">
    <i class="right material-icons" data-close="">close</i>
    <div th:classappend="${#strings.toLowerCase(flash.status)}" th:text="${flash.message}" class="flash"></div>
</div>

<div th:fragment="scripts">
    <script th:src="@{/vendor/jquery/jquery-1.11.3.js}"></script>
    <script th:src="@{/vendor/materialize/js/materialize.js}"></script>
    <script th:src="@{/vendor/codrops/js/classie.js}"></script>
    <script th:src="@{/vendor/codrops/js/selectFx.js}"></script>
    <script th:src="@{/app.js}"></script>
</div>

</body>
</html>

form.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" >
<head th:replace="layout :: head('new category')"></head>
<body>
<div th:replace="layout :: nav"></div>
<div th:replace="layout :: flash"></div>
<div class="container">
    <form th:action="@{/categories}" method="post" th:object="${category}">
        <div class="row">
            <div class="col s12">
                <h2>New Category</h2>
            </div>
        </div>
        <div class="divider"></div>
        <div class="row">
            <div class="col s12 l8" th:classappend="${#fields.hasErrors('name')}? 'error' : ''">
                <input type="text" th:field="*{name}" placeholder="Category Name"/>
                <div class="error-message" th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></div>
            </div>
        </div>
        <div class="row">
            <div class="col s12 l8" th:classappend="${#fields.hasErrors('colorCode')}? 'error' : ''">
                <select th:field="*{colorCode}" class="cs-select cs-skin-border">
                    <option value="" disabled="disabled">Category Color</option>
                    <option th:each = "color : ${color}" th:value = "${color.hexCode}" th:text="${color.name}" th:style="|color:${color.hexCode}|">Aqua</option>
                </select>
                <div class="error-message" th:if="${#fields.hasErrors('colorCode')}" th:errors="*{colorCode}"></div>
            </div>
        </div>
        <div class="row">
            <div class="col s12 l8">
                <button type="submit" class="button">Add</button>
                <a th:href="@{/categories}" class="button">Cancel</a>
            </div>
        </div>
    </form>
    <div class="row delete">
        <div class="col s12 l8">
            <form>
                <button type="submit" class="button">Delete</button>
            </form>
        </div>
    </div>
</div>
<div th:replace="layout :: scripts"></div>
</body>
</html>

CategoryController.java

package com.teamtreehouse.giflib.web.controller;

import com.teamtreehouse.giflib.model.Category;
import com.teamtreehouse.giflib.service.CategoryService;
import com.teamtreehouse.giflib.web.Color;
import com.teamtreehouse.giflib.web.FlashMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import java.util.List;

@Controller
public class CategoryController {
    @Autowired
    private CategoryService categoryService;

    // Index of all categories
    @SuppressWarnings("unchecked")
    @RequestMapping("/categories")
    public String listCategories(Model model) {
        // TODO: Get all categories
        List<Category> categories = categoryService.findAll();

        model.addAttribute("categories",categories);
        return "category/index";
    }

    // Single category page
    @RequestMapping("/categories/{categoryId}")
    public String category(@PathVariable Long categoryId, Model model) {
        // TODO: Get the category given by categoryId
        Category category = null;

        model.addAttribute("category", category);
        return "category/details";
    }

    // Form for adding a new category
    @RequestMapping("categories/add")
    public String formNewCategory(Model model) {
        // TODO: Add model attributes needed for new form
        if (!model.containsAttribute("category")) {
            model.addAttribute("category", new Category());
        }
        model.addAttribute("color", Color.values());

        return "category/form";
    }

    // Form for editing an existing category
    @RequestMapping("categories/{categoryId}/edit")
    public String formEditCategory(@PathVariable Long categoryId, Model model) {
        // TODO: Add model attributes needed for edit form

        return "category/form";
    }

    // Update an existing category
    @RequestMapping(value = "/categories/{categoryId}", method = RequestMethod.POST)
    public String updateCategory() {
        // TODO: Update category if valid data was received

        // TODO: Redirect browser to /categories
        return null;
    }

    // Add a category
    @RequestMapping(value = "/categories", method = RequestMethod.POST)
    public String addCategory(@Validated Category category, BindingResult result, RedirectAttributes redirectAttributes) {
        // TODO: Add category if valid data was received
        if(result.hasErrors()){
            // Include validation errors upon redirect
            redirectAttributes.addFlashAttribute("org.springframework.validation.BindingResult.category", result);

            // Add category if invalid was received
            redirectAttributes.addFlashAttribute("category", category);

            // Redirect back to the form
            return "redirect:/categories/add";
        }
        categoryService.save(category);

        redirectAttributes.addFlashAttribute("flash",new FlashMessage("Category successfully added!",FlashMessage.Status.SUCESS));

        // TODO: Redirect browser to /categories
        return "redirect:/categories";
    }

    // Delete an existing category
    @RequestMapping(value = "/categories/{categoryId}/delete", method = RequestMethod.POST)
    public String deleteCategory(@PathVariable Long categoryId) {
        // TODO: Delete category if it contains no GIFs

        // TODO: Redirect browser to /categories
        return null;
    }
}

1 Answer

Boban Talevski
Boban Talevski
24,793 Points

Your problem lies with a typo in the enum value SUCCESS.

You have it here in your controller method as FlashMessage.Status.SUCESS and the app compiles and works without a problem, since you probably have it written that way in your FlashMessage class as well. But the thing is, the thymeleaf fragment in layout.html is using that particular enum value (to lower case) to add a css class to the flash message:

<div th:fragment="flash" th:if="${flash != null}" class="container">
    <i class="right material-icons" data-close="">close</i>
<!-- The line below -->
    <div th:classappend="${#strings.toLowerCase(flash.status)}" th:text="${flash.message}" class="flash"></div>
</div>

And you guessed it, that particular css class adds the background color to the flash message :) The snippet below is from the app.css file

.flash.success {
    background-color: #7f9e40;
    color: #f3f6ee;
}

.flash.failure {
    background-color: #ac4343;
    color: #f2e2e2;
}

But since in your case, the class name will evaluate to sucess instead of success, and there's no sucess css class defined, the appropriate background color won't be applied.