Using Java Extensions to format data in your views

Java Extensions are a very nice helper inside your templates, which will help you to keep your template code as well as your model code clean from issues such as data formatting. Reformatting values such as dates is a standard problem at the view layer for most web developers. For example, the problem of having a date with millisecond exactness, though only the year should be printed. This is where these extensions start. Many web developers also do this by using JavaScript, but this often results in code duplication on frontend and backend.

This recipe shows a pretty common example, where a date needs to be formatted to show some relative date measured from the current time. This is very common in the Twitter timeline, where every Tweet in the web interface has no correct date, but merely a "n hours ago" or "n days ago" flag.

Getting ready

Just create a tiny application. You will need to create a new application and add a database to the application configuration, so entities can be specified.

How to do it...

You need a route to show your tweets in conf/routes:

GET      /{username}/timeline           Application.showTweet

After that we can model a tweet model class:

package models;


import java.util.Date;
import javax.persistence.Entity;
import play.data.validation.Max;
import play.db.jpa.Model;

@Entity
public class Tweet extends Model {

        @Max(140) public String content;
        public Date postedAt;
        public User user;
}

As well as a simple user entity:

@Entity
public class User extends Model {

        @Column(unique=true) 
        public String login;
}

The controller is quite short. It uses an alternative query for the 20 newest tweets, which is more JPA like:

public static void showTweets(String username) {
        User user = User.find("byLogin", username).first();
        notFoundIfNull(user);
        List<Tweet> tweets = Tweet.find("user = ? order by postedAt DESC", user).fetch(20);
        render(tweets, user);
}

The rendering code will look like this:

#{extends 'main.html' /}
#{set 'title'}${user.login} tweets#{/set}

#{list tweets, as:'tweet'}
<div><h3>${tweet.content}</h3> by ${tweet.user.login} at <i>${tweet.postedAt.since()}</i></h3></div>
#{/list}

Now this code works. However, the since() Java Extension, which is built in with Play only works when you hand over a date in the past as it calculates the difference from now. What if you want to add a feature of a future tweet which is blurred, but will show a time when it is shown? You need to hack up your own extensions to do this. Create a new class called CustomExtensions in the extensions package inside your application directory (so the file is ./app/extensions/CustomExtension.java)

public class CustomExtensions extends JavaExtensions {

        private static final long MIN   = 60;
        private static final long HOUR  = MIN * 60;
        private static final long DAY   = HOUR * 24;
        private static final long MONTH = DAY * 30;
        private static final long YEAR  = DAY * 365;

        public static String pretty(Date date) {
                Date now = new Date();
                if (date.after(now)) {
	                long delta = (date.getTime() - now.getTime()) / 1000;

                if (delta < 60) {
                    return Messages.get("in.seconds", delta,
 pluralize(delta));
                }

                if (delta < HOUR) {
                    long minutes = delta / MIN;
                    return Messages.get("in.minutes", minutes, pluralize(minutes));
                }

                if (delta < DAY) {
                    long hours = delta / HOUR;
                    return Messages.get("in.hours", hours,
 pluralize(hours));
                }

                if (delta < MONTH) {
                    long days = delta / DAY;
                    return Messages.get("in.days", days, pluralize(days));
                }

                if (delta < YEAR) {
                    long months = delta / MONTH;
                    return Messages.get("in.months", months,
 pluralize(months));
                }

                long years = delta / YEAR;
                return Messages.get("in.years", years, pluralize(years));

                } else {
                        return JavaExtensions.since(date);
                }

        }
}

Update your ./app/conf/messages file for successful internationalization by appending to it:

in.seconds = in %s second%s
in.minutes = in %s minute%s
in.hours   = in %s hour%s
in.days    = in %s day%s
in.months  = in %s month%s
in.years   = in %s year%s

The last change is to replace the template code to:

#{list tweets, as:'tweet'}
<div><h3>${tweet.content}</h3> by ${tweet.user.login} at <i>${tweet.postedAt.pretty()}</i></h3></div>
#{/list}

How it works...

A lot of code has been written for an allegedly short example. The entity definitions, routes configuration, and controller code should by now be familiar to you. The only new thing is the call of ${tweet.postedAt.since()} in the template, which does call a standard Java Extension already shipped with Play. When calling the since() method, you must make sure that you called it on an object from the java.util.Date class. Otherwise, this extension will not be found, as they are dependent on the type called on. What the since() method does, is to reformat the boring date to a pretty printed and internationalized string, how long ago this date is from the current time. However this functionality only works for dates in the past and not for future dates.

Therefore the CustomExtensions class has been created with the pretty() method in it. Every class which inherits from JavaExtensions automatically exposes its methods as extension in your templates. The most important part of the pretty() method is actually its signature. By marking the first parameter as type java.util.Date you define for which data type this method applies. The logic inside the method is pretty straightforward as it also reuses the code from the since() extension. The only unknown thing is the call to Messages.get(), which just returns the string in the correct language, such as"3 days ago" in English and "vor 3 Tagen" in German.

Last but not least, the template code is changed to use the new extension instead of since().

There's more...

Java Extensions can be incredibly handy if used right. You should also make sure that this area of your application is properly documented, so frontend developers know what to search for, before trying to implement it somehow in the view layer.

Using parameters in extensions

It is pretty simple to use parameters as well, by extending the method with an arbitrary amount of parameters like this:

public static void pretty(Date date, String name) {

Using it in the template is as simple as ${tweet.postedAt.pretty("someStr")}

Check for more built in Java Extensions

There are tons of useful helpers already built-in. Not only for dates, but also for currency formatting, numbers, strings, or list. Check it out at http://www.playframework.org/documentation/1.2/javaextensions.

Check for internationalization on plurals

Play has the great feature and possibility of definin a plural of internationalized strings, which is incidentally also defined in the built-in JavaExtensions class.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.220.139.168