Basil Bourque
Basil Bourque

Reputation: 338316

Invoke validation user-feedback without binder & bean in Vaadin 8

In Vaadin 8, the built-in validation feature is established with a Binder hooked up to a bean, as discussed in the Manual and as shown in this Answer. I understand how this works, and it works well, I appreciate the Vaadin team’s thinking and effort there.

However… I just want a simple quick-and-dirty data-entry field accepting only -1, 0, & 1 as values. I like the Vaadin validation’s feature of rejecting bad input by marking the field/caption with a bang and red color and so on.

➥ Is there some way to invoke the validation-style feedback to user without going to all the elaborate trouble of defining a bean and establishing a binder?

As a workaround, I could replace my data-entry field with a pop-up menu (a.k.a. drop-down list). But my question is still useful for all the times when we need quick simple input from user such as in a dialog box, without all the ceremony of a data-entry form.

Upvotes: 0

Views: 836

Answers (2)

Basil Bourque
Basil Bourque

Reputation: 338316

Example app

The Answer by Tatu Lund is correct, and solves the problem. Solution is a call to AbstractComponent::setComponentError.

Here is a complete little Vaadin 8.5.2 app to show this in action.

This app has two widgets, a TextField and a Button, that can be marked as in normal condition…

enter image description here

…or in an erroneous condition.

enter image description here

Here is the entire .java file for the app.

package com.basilbourque.example;

import javax.servlet.annotation.WebServlet;

import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.server.UserError;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.*;

import java.util.List;
import java.util.Set;

/**
 * This UI is the application entry point. A UI may either represent a browser window
 * (or tab) or some part of an HTML page where a Vaadin application is embedded.
 * <p>
 * The UI is initialized using {@link #init(VaadinRequest)}. This method is intended to be
 * overridden to add component to the user interface and initialize non-component functionality.
 */
@Theme ( "mytheme" )
public class MyUI extends UI {

    @Override
    protected void init ( VaadinRequest vaadinRequest ) {

        // TextField allows only 'dog' or 'cat' as values.
        Set< String > animals = Set.of( "dog" , "cat" );
        TextField dogOrCat = new TextField();
        dogOrCat.setCaption( "Type dog or cat:" );
        dogOrCat.addValueChangeListener( valueChangeEvent -> {
            if ( animals.contains( dogOrCat.getValue() ) ) {
                dogOrCat.setComponentError( null );
            } else {
                dogOrCat.setComponentError( new UserError( "Oops! You typed something other than 'dog' or 'cat'." ) );
            }
        } );

        // Button which is deemed to be in good condition or erroneous condition by a radio buttons pair.
        Button button = new Button( "Example" );
        button.addClickListener( e -> {
            Notification.show( "This button does nothing." , Notification.Type.HUMANIZED_MESSAGE );
        } );

        // Radio-buttons, to control the good or error condition of button above.
        List< String > radioItems = List.of( "No error" , "Error" );
        RadioButtonGroup< String > radios =
        new RadioButtonGroup<>( "Make button:" );
        radios.setItems( radioItems );
        radios.setValue( radioItems.get( 0 ) ); // Set 1st item by default (index counting = 0).
        radios.addValueChangeListener( valueChangeEvent -> {
            if ( radios.getValue().equals( radioItems.get( 1 ) ) ) {  // Index-counting, so `1` = 2nd list item "Error".
                button.setComponentError( new UserError( "Bad button" ) );
            } else {
                button.setComponentError( null );
            }
        } );

        // Arrange
        final VerticalLayout layout = new VerticalLayout();
        layout.addComponents( dogOrCat , button , radios );
        setContent( layout );
    }

    @WebServlet ( urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true )
    @VaadinServletConfiguration ( ui = MyUI.class, productionMode = false )
    public static class MyUIServlet extends VaadinServlet {
    }
}

By the way, here is my POM (pom.xml) file updated for Java 10 and the latest versions of various libraries.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.basilbourque.example</groupId>
    <artifactId>set-comp-error</artifactId>
    <packaging>war</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>set-comp-error</name>

    <prerequisites>
        <maven>3</maven>
    </prerequisites>

    <properties>
        <vaadin.version>8.5.2</vaadin.version>
        <vaadin.plugin.version>8.5.2</vaadin.plugin.version>
        <jetty.plugin.version>9.4.12.v20180830</jetty.plugin.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>10</maven.compiler.source>
        <maven.compiler.target>10</maven.compiler.target>
        <!-- If there are no local customizations, this can also be "fetch" or "cdn" -->
        <vaadin.widgetset.mode>local</vaadin.widgetset.mode>
    </properties>

    <repositories>
        <repository>
            <id>vaadin-addons</id>
            <url>http://maven.vaadin.com/vaadin-addons</url>
        </repository>
    </repositories>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.vaadin</groupId>
                <artifactId>vaadin-bom</artifactId>
                <version>${vaadin.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.vaadin</groupId>
            <artifactId>vaadin-server</artifactId>
        </dependency>
        <dependency>
            <groupId>com.vaadin</groupId>
            <artifactId>vaadin-push</artifactId>
        </dependency>
        <dependency>
            <groupId>com.vaadin</groupId>
            <artifactId>vaadin-client-compiled</artifactId>
        </dependency>
        <dependency>
            <groupId>com.vaadin</groupId>
            <artifactId>vaadin-themes</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.2.2</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                    <!-- Exclude an unnecessary file generated by the GWT compiler. -->
                    <packagingExcludes>WEB-INF/classes/VAADIN/widgetsets/WEB-INF/**</packagingExcludes>
                </configuration>
            </plugin>
            <plugin>
                <groupId>com.vaadin</groupId>
                <artifactId>vaadin-maven-plugin</artifactId>
                <version>${vaadin.plugin.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>update-theme</goal>
                            <goal>update-widgetset</goal>
                            <goal>compile</goal>
                            <!-- Comment out compile-theme goal to use on-the-fly theme compilation -->
                            <goal>compile-theme</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-clean-plugin</artifactId>
                <version>3.1.0</version>
                <!-- Clean up also any pre-compiled themes -->
                <configuration>
                    <filesets>
                        <fileset>
                            <directory>src/main/webapp/VAADIN/themes</directory>
                            <includes>
                                <include>**/styles.css</include>
                                <include>**/styles.scss.cache</include>
                            </includes>
                        </fileset>
                    </filesets>
                </configuration>
            </plugin>

            <!-- The Jetty plugin allows us to easily test the development build by
                running jetty:run on the command line. -->
            <plugin>
                <groupId>org.eclipse.jetty</groupId>
                <artifactId>jetty-maven-plugin</artifactId>
                <version>${jetty.plugin.version}</version>
                <configuration>
                    <scanIntervalSeconds>2</scanIntervalSeconds>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <profiles>
        <profile>
            <!-- Vaadin pre-release repositories -->
            <id>vaadin-prerelease</id>
            <activation>
                <activeByDefault>false</activeByDefault>
            </activation>

            <repositories>
                <repository>
                    <id>vaadin-prereleases</id>
                    <url>http://maven.vaadin.com/vaadin-prereleases</url>
                </repository>
                <repository>
                    <id>vaadin-snapshots</id>
                    <url>https://oss.sonatype.org/content/repositories/vaadin-snapshots/</url>
                    <releases>
                        <enabled>false</enabled>
                    </releases>
                    <snapshots>
                        <enabled>true</enabled>
                    </snapshots>
                </repository>
            </repositories>
            <pluginRepositories>
                <pluginRepository>
                    <id>vaadin-prereleases</id>
                    <url>http://maven.vaadin.com/vaadin-prereleases</url>
                </pluginRepository>
                <pluginRepository>
                    <id>vaadin-snapshots</id>
                    <url>https://oss.sonatype.org/content/repositories/vaadin-snapshots/</url>
                    <releases>
                        <enabled>false</enabled>
                    </releases>
                    <snapshots>
                        <enabled>true</enabled>
                    </snapshots>
                </pluginRepository>
            </pluginRepositories>
        </profile>
    </profiles>

</project>

Upvotes: 0

Tatu Lund
Tatu Lund

Reputation: 10623

Binder is not designed for single field use.

Vaadin 8

The best alternative to do single-field validation with Vaadin 8, is just do hook with ValueChangeListener of the field and do what ever is needed in value change event.

AbstractComponent::setComponentError(ErrorMessage componentError)

Note, however in Vaadin 8 field components do have setComponentError(..) method. By calling this method, your single-field validation with ValueChangeListener by setComponentError(…) you have the same look and feel analogous to a binder in a form. This works with textfields, buttons, and so on.

See the Handling Errors page of the manual.

button.setComponentError( new UserError( "Bad click" ) ) ;

Vaadin 7

There is a difference between Vaadin 7 and Vaadin 8. With Vaadin 7 it was possible to assign validator directly to a field.

Upvotes: 1

Related Questions