Wednesday, June 25, 2014

Spring MVC - Thymeleaf - Bootstrap - I

Source Code | See Application
You can see the full description of the aplication and the other two entries related here

Initial configuration

Web.xml

In this file we define that the Spring DispatcherServlet will be in charge of all the requests that have the ".htm" suffix
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.0"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <servlet>
        <servlet-name>twitterFlightExample</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>twitterFlightExample</servlet-name>
        <url-pattern>*.htm</url-pattern>
    </servlet-mapping>
</web-app>

Spring Web Application Context - twitterFlightExample-servlet.xml

For default Spring search the webapplication file with the pattern: [servlet-name]-servlet.xml in this case the file will be: twitterFlightExample-servlet.xml
<beans>
    <import resource="classpath*:META-INF/spring/applicationContext*.xml"/>

    <context:component-scan base-package="org.anotes.example.twitterflight.*"/>

    <bean id="templateResolver" class="org.thymeleaf.templateresolver.ServletContextTemplateResolver">
        <property name="prefix" value="/pages/"/>
        <property name="suffix" value=".html"/>
        <property name="templateMode" value="HTML5"/>
        <property name="cacheable" value="false"/>
    </bean>

    <bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine">
        <property name="templateResolver" ref="templateResolver"/>
    </bean>
    <bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
        <property name="templateEngine" ref="templateEngine"/>
        <property name="order" value="1"/>
    </bean>
</beans>

Notes:

In the templateResolver we define:
  • The path where all of thymeleaf template files:/pages/  will be found 
  • The extension of this templates: .html
  • The template mode: HTML5
  • The cacheable property must be false for developement

Controller

We will use the following controller:
@Controller
public class MainController {
    @Autowired
    ProductService productService;
    @Autowired
    MainInfo mainInfo;

    @RequestMapping("/main")
    public ModelAndView main(HttpServletRequest request) {
        Map requestParams = request.getParameterMap();
        updateMainInfoBasedOn(requestParams);
        ProductFlt productFlt = mainInfo.getFilter();
        SummaryInfo summaryInfo = mainInfo.getSummaryInfo();
        List<Product> products = productService.findProducts(productFlt, mainInfo.getPageInfo());
        Map model = new HashMap<>();
        model.put("products", products);
        model.put("pageInfo", mainInfo.getPageInfo());
        model.put("filterList", productFlt.getFilterEntryList());
        model.put("brandList", summaryInfo.getBrandSummaryList());
        return new ModelAndView("main", model);
    }

    @RequestMapping("/getProductInfo")
    public ModelAndView getProductInfo(@RequestParam("product") Long productGkey) {
        logger.debug("Getting product info for:{}", productGkey);
        List<ProductPriceHistory> productPriceHistoryList = productService.getProductPricesHistoryFor(productGkey);
        Map model = new HashMap();
        model.put("productPriceHistoryList", productPriceHistoryList);
        SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm");
        model.put("displayDateFormatter", dateFormat);
        return new ModelAndView("productDetail", model);
    }

Notes:

  • ProductService: will be the facade to all of the domain functionality that we need.
  • MainInfo: will be the session object that mantain all the information needed; for instance: the current filter applied, the current page and so on.
  • Methods:
    • main(HttpServletRequest request) It renders the main page; and this will be called also when the filters or the page changes.
    • getProductInfo(@RequestParam("product") Long productGkey) It returns the html fragment that contains the price information of the product. It is called from javascript ajax call, when the user click on the "Expand" link.

View:

<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:th="http://www.thymeleaf.org"
  class="csstransforms csstransforms3d csstransitions">
  <head>
    ....
        <link rel="stylesheet" href="/bower_components/twitterflightexample/twitterflighexample.css"/>
        <!-- Latest compiled and minified CSS -->
        <link rel="stylesheet" href="/bower_components/bootstrap/css/bootstrap.min.css"/>
        <link rel="stylesheet" href="/bower_components/bootstrap/css/bootstrap-responsive.min.css"/>
        <link rel="stylesheet" href="/bower_components/nprogress/nprogress.css"/>
  </head>
<body>
.....
    <div class="panel-collapse collapse in" th:each="brand : ${brandList}">
        <ul class="list-group">
        <li class="list-group-item">
            <a href="#"
               th:href="@{main.htm?(filter=brand,value=${brand.id})}"
               th:text="${brand.description}">Accesorias y Belleza</a>
            <span class="badge" th:text="${brand.nbr}">42</span>
        </li>
        </ul>
    </div>
.....
            <ol id="breadcrumb-zone" class="breadcrumb">
                <li th:each="filterEntry : ${filterList}"
                    th:class="${filterEntryStat.count}==${filterEntryStat.size}?'active'">
                    <a href="#" th:href="@{main.htm?(filter=breadcrumbIdx,value=${filterEntryStat.count}-1)}"
                       th:text="${filterEntry.value}"
                       th:if="${filterEntryStat.count}!=${filterEntryStat.size}">
                        Home</a>
                <span th:text="${filterEntry.value}"
                      th:if="${filterEntryStat.count}==${filterEntryStat.size}">
                    Name2</span>
                </li>
            </ol>
.....
            <a class="pull-left cursorpointer"
           th:onclick="'javascript:showMoreInfo(this,\'' + ${product.gkey} + '\');'">
            <span th:id="spn+${product.gkey}">Expand</span>
           </a>
.....

            <li>Price:
                <strong th:text="${product.price}? ${#numbers.formatDecimal(product.price, 0, 'COMMA', 2, 'POINT')}">123456</strong>
            </li>
.....                
    <script src="/bower_components/jquery/jquery.min.js"></script>
    <script src="/bower_components/bootstrap/js/bootstrap.min.js"></script>
    <script src="/bower_components/bootstrap/js/bootstrap-paginator.min.js"></script>
    <script src="/bower_components/nprogress/nprogress.js"></script>
    <script th:inline="javascript">
/*<![CDATA[*/
var options = {
    bootstrapMajorVersion: 3,
    currentPage: [[${pageInfo.page}]],
    totalPages: [[${pageInfo.totalPages}]],
    numberOfPages: 10,
    onPageClicked: function (e, originalEvent, type, page) {
        window.location.href = "/main.htm?page=" + (page - 1);
    }
};
/*]]>*/
</script>
</body>

Notes:

  • xmlns:th="http://www.thymeleaf.org" This is needed in order to use thymeleaf
  • bootstrap-paginator: is used to implement the pagination
  • nprogress: is used to show the progress bar at the top during the execution of ajax

Thymeleaf

  • Loop: th:each="brand : ${brandList}"
  • Href replace: th:href="@{main.htm?(filter=brand,value=${brand.id})}"
  • Simple text replace: th:text="${brand.description}"
  • Class replace: th:class="${filterEntryStat.count}==${filterEntryStat.size}?'active'"
  • Setting onclick event: th:onclick="'javascript:showMoreInfo(this,\'' + ${product.gkey} + '\');'"
  • Painting zones conditionally th:if="${filterEntryStat.count}!=${filterEntryStat.size}"
  • Strings concatenation th:src="'/img/' +${product.imageUrl}"
  • Format Numbers th:text="${product.price}? ${#numbers.formatDecimal(product.price, 0, 'COMMA', 2, 'POINT')}"
  • Calling methods of objects of the model
    • Adding "displayDateFormatter" to the model:
      public ModelAndView getProductInfo....
          Map model = new HashMap();
          ...
          SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm");
          model.put("displayDateFormatter", dateFormat);
          return new ModelAndView("productDetail", model);
      }
      
    • Calling a method "format" of the object "displayDateFormatter"
      <td th:text="${displayDateFormatter.format(prod.created)}">2.41</td>
  • Inline javascript
    <script th:inline="javascript">
        currentPage: [[${pageInfo.page}]],
        totalPages: [[${pageInfo.totalPages}]],
    </script>
    

6 comments:

Anbarasan14 said...

Fabulous post. Irrespective of the age groups your post is liked by all. Thanks for sharing.
IELTS Coaching in Anna Nagar
IELTS Coaching in Chennai Anna Nagar
IELTS Coaching in Adyar
IELTS Coaching in Porur
IELTS Coaching in Mumbai
Best English Speaking Classes in Mumbai
Spoken English Classes in Chennai
IELTS Coaching Centre in Chennai

sandhiya said...

That's a beautiful post. I can't wait to utilize the resources you've shared with us. Do share more such informative posts.
Data Analytics Training in Chennai
R Training in Chennai
Machine Learning course in Chennai
Data Science Training in Chennai
RPA Training in Chennai
UiPath Training in Chennai
DevOps Training in Chennai
R Training in OMR
R Training in Porur

iuwekjhbr said...

Please refer below if you are looking for best Online job support and proxy interview from India

DevOps Online Job Support From India | PHP Online Job Support From India | Selenium Online Job Support From India | Hadoop Online Job Support From India | Java Online Job Support From India | Angular Online Job Support From India | Python Online Job Support From India | Android Online Job Support From India

Thank you for excellent article.

Unknown said...

Please refer below if you are looking for best Online job support and proxy interview from India

AWS Online Job Support From India | Workday Online Job Support From India | ReactJS Online Job Support From India | Manual Testing Online Job Support From India | Dotnet Online Job Support From India | Peoplesoft Online Job Support From India | Teradata Online Job Support From India

Thank you for excellent article.

Laura Bush said...

I read this article, it is really informative one. Your way of writing and making things clear is very impressive. Thanking you for such an informative article.Job Support From India

Unknown said...

IEEE Final Year Project centers make amazing deep learning final year projects ideas for final year students Final Year Projects for CSE to training and develop their deep learning experience and talents.

IEEE Final Year projects Project Centers in India are consistently sought after. Final Year Students Projects take a shot at them to improve their aptitudes, while specialists like the enjoyment in interfering with innovation.

corporate training in chennai corporate training in chennai

corporate training companies in india corporate training companies in india

corporate training companies in chennai corporate training companies in chennai

I have read your blog its very attractive and impressive. I like it your blog. Digital Marketing Company in Chennai Project Centers in Chennai

Post a Comment