Close

Reinventing the Data Grid

A Data Grid is one of the most commonly used components in business apps. It must also be one of the most complex components to implement. There are many aspects that you need to get right – customizability, performance, usability, cross-platform support, just to name a few.

Vaadin has years of experience in building components like data tables and grids for business apps, and specifically, the Vaadin Elements team has been developing and maintaining Polymer-based Web Components like <vaadin-grid> for about two years now.

A year ago, I was looking into <iron-list> – a powerful list component built by the Polymer team, which helped me realize how expressive and user-friendly HTML templates can be in Web Components for defining customized, repeating structures like lists. Using declarative HTML is a natural way for any web developer to define contents of tables as well. So, I started to think if templates could be used to build a data grid element.

At the end of January 2016, I started experimenting and building prototypes around <iron-list>. The main goal was to validate two things: what is the user’s preference in using templates over the existing patterns and how could the column templates be technically implemented so that people would be able to style and access the contents of the cells while keeping the internal structure of the table encapsulated inside a shadow root.

As a result of the experiments, <iron-data-table> was produced. During the following months, it quickly gained many of the features you usually expect to find in a data grid. As the template approach kept gathering positive feedback, I wanted to introduce the idea to my colleagues at Vaadin.

After discussing our options, we decided to implement the <vaadin-grid>2.0 based on templates.

.     .    .

Today, with the lessons learned from <iron-data-table> and with the help of the whole Vaadin Elements team, we are really moving forwards in getting <vaadin-grid> 2.0 ready for release.

Almost a year after the first experiments with the templates, we are really excited to release the first alpha version of <vaadin-grid> 2.0.

Feature-wise the alpha is in most parts already more advanced than its predecessor, but there are still some missing pieces we will implement before entering the beta stage early next year.

Here are the highlights of some of the most important features — you can take a look at the release notes and live demos for more details on v2.0.0-alpha1.

 

Extensive Customizability

Templates

Templates allow you to declaratively define how property values are displayed, to have dynamic content based on data, edit values using two-way binding and add event listeners to any element inside the cell.

More specifically, the templates in <vaadin-grid> allow you to change the selection state of items, display item details, and edit data items easily.

 

See the live templating examples for more details.

Theming

In addition to having full control over the contents of the cells and their styles, you can also use a variety of different CSS mixins to apply custom styles to the cells to make the grid fit in nicely with the design of your app.

There are different mixins targeted for the header, body and footer cells and also, there are mixins for the body cells having different states like selected or active.

Here are some examples to give you an idea of how versatile the mixins are:

Material Design

Valo

Dark Theme

.     .    .

See the live styling examples for more details.

 

Powerful Data Rendering

As <vaadin-grid> is based on <iron-list>, it uses a similar virtual DOM structure to render and reuse row and cell elements. In practice, the number of elements in the DOM is not increased by the number of items bound to the grid.

We’ve paid extra attention to making sure the scrolling experience is silky smooth in all major browsers and mobile devices and continue working on improving it.

The rendering engine has been extended to also support lazy loading by introducing the function dataSource, which you might already be familiar with from the first generation of<vaadin-grid>. The function data source allows you to feed items from a remote service, asynchronously, or for example from a generator function.

One unique feature of the improved rendering and scrolling engine is the fact that there in practice is no upper limit for the number of items.

 

 

Empowering Helper Elements

Although the templates are powerful and allow you to do virtually anything inside the cells, there are some commonly used patterns that can be quite complex to implement. We recommend encapsulating any repeating pattern into a reusable custom element.

We are currently shipping <vaadin-grid> with helper elements for providing multi-selection, filtering, and sorting.

 


.     .    .

See the live filtering and sorting examples for more details.

 

What’s next?

The focus at the beginning of 2017 will be on adding missing features like keyboard navigation, accessibility support, column reordering and resizing.

The current plan is to release the 2.0 stable as a Polymer 1.x element, but a Polymer 2.0 compatible element will also be released later.

We are also going to publish articles about the interesting things we have learned and will learn while building Vaadin Elements — topics including at least Polymer’s Templatizer, custom overlays, accessibility and testing.

As always, all feedback is highly appreciated – the fastest ways to get in touch with us are through Gitter or GitHub. You can also follow our progress in our Waffle board.

 

Originally published in medium.com

Customer Sponsored Vaadin TreeGrid Released

We are very pleased to announce the release of the Vaadin TreeGrid add-on, which is now freely available in Vaadin Directory. Similar to the TreeTable extension to Table, Vaadin TreeGrid allows users to edit and present hierarchical data sources in the Grid component.

 

The development of the add-on has been sponsored by our long-term customer Magnolia International Ltd., the company behind Magnolia CMS, an open-source Web Content Management System (CMS). Magnolia focuses on an intuitive user experience for enterprise-scale systems and thus a TreeGrid component was imperative for their users. Today this component is release to the entire Vaadin open source community.

 

Vaadin TreeGrid features:

  • Vaadin TreeGrid is an official Vaadin add-on and is fully maintained by our experts.

  • Supports custom renderers on hierarchy columns, can now be set programmatically.

  • Supports custom collapse logic as well as custom depth calculation logic.

  • Hierarchy indicators are fully configurable in CSS.

  • Supports Keyboard navigation (Alt/Option + Arrow keys).

 

Download the Vaadin TreeGrid add-on now

Implementing “remember me” with Vaadin

One of the most common features for a login form is the remember me feature that allows users to be automatically signed in from a certain machine even after the HTTP session expires:

Screen Shot 2017-01-04 at 0.31.39.png

Web applications usually implement this feature by using cookies. When the user selects the remember me option and signs in, the application stores a cookie to identify the user. The next time the user requests the web application, the standard authentication process is skipped and the user is automatically logged in.

Never store sensitive information in cookies. It’s not safe to store, for example, the ID of the user. If an attacker knows a valid user ID, they can create a new cookie containing such ID and sign in as the impersonated user. In this example, I’m going to store a random string and associate it with a username in a HashMap. In real-world applications, you can use an SQL database, for example, and store not only a random ID, but the hash of a token similarly to what you should do with the user’s passwords.

Implementing the business logic

Let’s see how to do this with Vaadin 8. The first step is to implement a UserService class (or a UserRepository class) to encapsulate any logic related to user data persistence. This UserService class should provide methods to:

  • Authenticate the user (username and password check).

  • Store a remembered user.

  • Get a remembered user.

  • Remove a remembered user.

A remembered user in this example is a random id:username pair. The following is an example implementation of the UserService class:

public class UserService {

    private static SecureRandom random = new SecureRandom();

    private static Map<String, String> rememberedUsers
            = new HashMap<>();

    public static boolean isAuthenticUser(String username,
            String password) {
        return username.equals("admin")
                && password.equals("password");
    }

    public static String rememberUser(String username) {
        String randomId = new BigInteger(130, random).toString(32);
        rememberedUsers.put(randomId, username);
        return randomId;
    }

    public static String getRememberedUser(String id) {
        return rememberedUsers.get(id);
    }

    public static void removeRememberedUser(String id) {
        rememberedUsers.remove(id);
    }
}

Notice that for simplicity reasons, there’s no database connection and the isAuthentic method has only one hardcoded user (admin/password). You should implement your own database related logic depending on your specific persistence technologies. Notice also that the remembered users are stored in memory using a HashMap. Here again, you should use a database and implement the corresponding logic to persist and delete data.

Implementing the web-related logic

With a UserService like the previous one, you can implement the web related logic for authentication, that is, handling values in the HTTP session and cookies. This class should provide methods to:

  • Check whether a user is currently authenticated, either by the standard authentication mechanism or by the remember me functionality.

  • Login a certain user by credentials.

  • Logout the current user.

Of course, this class delegates user data manipulation to the UserService class. The following is an example implementation:

public class AuthService {

    private static final String COOKIE_NAME = "remember-me";
    public static final String SESSION_USERNAME = "username";

    public static boolean isAuthenticated() {
        return VaadinSession.getCurrent()
                .getAttribute(SESSION_USERNAME) != null
                        || loginRememberedUser();
    }

    public static boolean login(String username, String password,
            boolean rememberMe) {
        if (UserService.isAuthenticUser(username, password)) {
            VaadinSession.getCurrent().setAttribute(
                    SESSION_USERNAME, username);

            if (rememberMe) {
                rememberUser(username);
            }
            return true;
        }

        return false;
    }

    public static void logOut() {
        Optional<Cookie> cookie = getRememberMeCookie();
        if (cookie.isPresent()) {
            String id = cookie.get().getValue();
            UserService.removeRememberedUser(id);
            deleteRememberMeCookie();
        }

        VaadinSession.getCurrent().close();
        Page.getCurrent().setLocation("");
    }

    private static Optional<Cookie> getRememberMeCookie() {
        Cookie[] cookies =
                VaadinService.getCurrentRequest().getCookies();
        return Arrays.stream(cookies)
                .filter(c -> c.getName().equals(COOKIE_NAME))
                .findFirst();
    }

    private static boolean loginRememberedUser() {
        Optional<Cookie> rememberMeCookie = getRememberMeCookie();

        if (rememberMeCookie.isPresent()) {
            String id = rememberMeCookie.get().getValue();
            String username = UserService.getRememberedUser(id);

            if (username != null) {
                VaadinSession.getCurrent()
                        .setAttribute(SESSION_USERNAME, username);
                return true;
            }
        }

        return false;
    }

    private static void rememberUser(String username) {
        String id = UserService.rememberUser(username);

        Cookie cookie = new Cookie(COOKIE_NAME, id);
        cookie.setPath("/");
        cookie.setMaxAge(60 * 60 * 24 * 30); // valid for 30 days
        VaadinService.getCurrentResponse().addCookie(cookie);
    }

    private static void deleteRememberMeCookie() {
        Cookie cookie = new Cookie(COOKIE_NAME, "");
        cookie.setPath("/");
        cookie.setMaxAge(0);
        VaadinService.getCurrentResponse().addCookie(cookie);
    }
}

A user is considered authenticated if there’s an attribute in the HTTP session or if a cookie is present and valid. Notice how the login method accepts not only the credentials (username and password), but a flag indicating if the user should be remembered. Notice also how the cookie is removed when the user is logged out.

Implementing the UI

Implementing a Vaadin UI that uses this class is pretty straightforward. The simplest application to test this behavior should contain at least two “screens” or components. A public component (the login form) and a private component (the actual functionality of the application).

The Vaadin UI implementation couldn't be simpler:

public class VaadinUI extends UI {

    @Override
    protected void init(VaadinRequest vaadinRequest) {
        if (!AuthService.isAuthenticated()) {
            showPublicComponent();
        } else {
            showPrivateComponent();
        }
    }

    public void showPublicComponent() {
        setContent(new PublicComponent());
    }

    public void showPrivateComponent() {
        setContent(new PrivateComponent());
    }
}

The public component looks like this:

Screen Shot 2017-01-04 at 0.31.39.png

The interesting part of the implementation is the button’s ClickListener:

public class PublicComponent extends CustomComponent {

    public PublicComponent() {
        ...
        Button button = new Button(
                "Login",
                e -> onLogin(
                    username.getValue(),
                    password.getValue(),
                    rememberMe.getValue())
        );
        ...
    }

    private void onLogin(String username, String password,
            boolean rememberMe) {
        if (AuthService.login(username, password, rememberMe)) {
            VaadinUI ui = (VaadinUI) UI.getCurrent();
            ui.showPrivateComponent();
        } else {
            Notification.show(
                "Invalid credentials (for demo use: admin/password)",
                Notification.Type.ERROR_MESSAGE
            );
        }
    }
}

As you can see, the onLogin method uses the AuthService.login method to check if the user is authentic.

The private component is pretty simple as well. It shows a message with the username and a button to sign out:

Screen Shot 2017-01-04 at 0.31.49.png

The following is the full implementation:

public class PrivateComponent extends CustomComponent {

    public PrivateComponent() {
        String username = (String) VaadinSession.getCurrent()
                .getAttribute(AuthService.SESSION_USERNAME);

        Label label = new Label("Welcome, " + username);
        label.addStyleName(ValoTheme.LABEL_HUGE);

        Button button = new Button("Sign out", this::onLogout);

        setCompositionRoot(new VerticalLayout(label, button));
    }

    private void onLogout(Button.ClickEvent event) {
        AuthService.logOut();
    }
}

Nothing special about it. The username is read from the HTTP session and the logout button invokes the AuthService.logOut() method that removes the remember me cookie and invalidates the HTTP session.

You can find the full source code of this example on GitHub