Merge pull request #16134 from marcushellberg/master

Updates for vaadin intro article
This commit is contained in:
Maiklins 2024-04-08 22:08:01 +02:00 committed by GitHub
commit b5bfa4697d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 709 additions and 688 deletions

1
vaadin/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
frontend/generated

View File

@ -0,0 +1,23 @@
<!DOCTYPE html>
<!--
This file is auto-generated by Vaadin.
-->
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
body, #outlet {
height: 100vh;
width: 100%;
margin: 0;
}
</style>
<!-- index.ts is included here automatically (either by the dev server or during the build) -->
</head>
<body>
<!-- This outlet div is where the views are rendered -->
<div id="outlet"></div>
</body>
</html>

View File

@ -1,13 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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"> 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> <modelVersion>4.0.0</modelVersion>
<groupId>org.test</groupId> <groupId>org.test</groupId>
<artifactId>vaadin</artifactId> <artifactId>vaadin</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
<name>vaadin</name> <name>vaadin</name>
<packaging>war</packaging>
<parent> <parent>
<groupId>com.baeldung</groupId> <groupId>com.baeldung</groupId>
@ -16,6 +15,11 @@
<relativePath>../parent-boot-3</relativePath> <relativePath>../parent-boot-3</relativePath>
</parent> </parent>
<properties>
<java.version>17</java.version>
<vaadin.version>24.3.3</vaadin.version>
</properties>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
<dependency> <dependency>
@ -30,39 +34,24 @@
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>javax.servlet</groupId> <groupId>com.vaadin</groupId>
<artifactId>javax.servlet-api</artifactId> <artifactId>vaadin-core</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-server</artifactId> <artifactId>vaadin-spring-boot-starter</artifactId>
<version>${vaadin-server.version}</version>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-push</artifactId>
<version>${vaadin-push.version}</version>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-client-compiled</artifactId>
<version>${vaadin-client-compiled.version}</version>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-themes</artifactId>
<version>${vaadin-themes.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId> <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.vaadin</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>vaadin-spring-boot-starter</artifactId> <artifactId>spring-boot-starter-validation</artifactId>
<version>${vaadin-spring-boot-starter.version}</version> </dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.h2database</groupId> <groupId>com.h2database</groupId>
@ -77,47 +66,17 @@
<build> <build>
<plugins> <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>${maven-war-plugin.version}</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> <plugin>
<groupId>com.vaadin</groupId> <groupId>com.vaadin</groupId>
<artifactId>vaadin-maven-plugin</artifactId> <artifactId>vaadin-maven-plugin</artifactId>
<version>${vaadin.plugin.version}</version> <version>${vaadin.version}</version>
</plugin> <executions>
<plugin> <execution>
<groupId>org.apache.maven.plugins</groupId> <goals>
<artifactId>maven-clean-plugin</artifactId> <goal>prepare-frontend</goal>
<version>${maven-clean-plugin.version}</version> </goals>
<!-- Clean up also any pre-compiled themes --> </execution>
<configuration> </executions>
<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> </plugin>
<plugin> <plugin>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
@ -126,68 +85,41 @@
</plugins> </plugins>
</build> </build>
<repositories>
<repository>
<id>vaadin-addons</id>
<url>https://maven.vaadin.com/vaadin-addons</url>
</repository>
</repositories>
<profiles> <profiles>
<profile> <profile>
<!-- Vaadin pre-release repositories --> <!-- Production mode is activated using -Pproduction -->
<id>vaadin-prerelease</id> <id>production</id>
<activation> <dependencies>
<activeByDefault>false</activeByDefault> <!-- Exclude development dependencies from production -->
</activation> <dependency>
<groupId>com.vaadin</groupId>
<repositories> <artifactId>vaadin-core</artifactId>
<repository> <exclusions>
<id>vaadin-prereleases</id> <exclusion>
<url>https://maven.vaadin.com/vaadin-prereleases</url> <groupId>com.vaadin</groupId>
</repository> <artifactId>vaadin-dev</artifactId>
<repository> </exclusion>
<id>vaadin-snapshots</id> </exclusions>
<url>https://oss.sonatype.org/content/repositories/vaadin-snapshots/</url> </dependency>
<releases> </dependencies>
<enabled>false</enabled> <build>
</releases> <plugins>
<snapshots> <plugin>
<enabled>true</enabled> <groupId>com.vaadin</groupId>
</snapshots> <artifactId>vaadin-maven-plugin</artifactId>
</repository> <version>${vaadin.version}</version>
</repositories> <executions>
<pluginRepositories> <execution>
<pluginRepository> <goals>
<id>vaadin-prereleases</id> <goal>build-frontend</goal>
<url>https://maven.vaadin.com/vaadin-prereleases</url> </goals>
</pluginRepository> <phase>compile</phase>
<pluginRepository> </execution>
<id>vaadin-snapshots</id> </executions>
<url>https://oss.sonatype.org/content/repositories/vaadin-snapshots/</url> </plugin>
<releases> </plugins>
<enabled>false</enabled> </build>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile> </profile>
</profiles> </profiles>
<properties>
<vaadin.version>13.0.9</vaadin.version>
<vaadin.plugin.version>13.0.9</vaadin.plugin.version>
<vaadin-spring-boot-starter.version>13.0.9</vaadin-spring-boot-starter.version>
<vaadin-server.version>8.8.5</vaadin-server.version>
<vaadin-push.version>8.8.5</vaadin-push.version>
<vaadin-client-compiled.version>8.8.5</vaadin-client-compiled.version>
<vaadin-themes.version>8.8.5</vaadin-themes.version>
<jetty.plugin.version>9.3.9.v20160517</jetty.plugin.version>
<vaadin.widgetset.mode>local</vaadin.widgetset.mode>
<vaadin.theme>mytheme</vaadin.theme>
<maven-clean-plugin.version>3.0.0</maven-clean-plugin.version>
</properties>
</project> </project>

View File

@ -1,15 +1,19 @@
package com.baeldung; package com.baeldung;
import com.baeldung.data.Employee;
import com.baeldung.data.EmployeeRepository;
import com.vaadin.flow.component.page.AppShellConfigurator;
import com.vaadin.flow.component.page.Push;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner; import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@Push
@SpringBootApplication @SpringBootApplication
public class Application { public class Application implements AppShellConfigurator {
private static final Logger log = LoggerFactory.getLogger(Application.class); private static final Logger log = LoggerFactory.getLogger(Application.class);

View File

@ -1,90 +0,0 @@
package com.baeldung;
import com.vaadin.flow.component.Key;
import com.vaadin.flow.component.KeyNotifier;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.binder.Binder;
import com.vaadin.flow.spring.annotation.SpringComponent;
import com.vaadin.flow.spring.annotation.UIScope;
import org.springframework.beans.factory.annotation.Autowired;
@SpringComponent
@UIScope
public class EmployeeEditor extends VerticalLayout implements KeyNotifier {
private final EmployeeRepository repository;
private Employee employee;
TextField firstName = new TextField("First name");
TextField lastName = new TextField("Last name");
Button save = new Button("Save", VaadinIcon.CHECK.create());
Button cancel = new Button("Cancel");
Button delete = new Button("Delete", VaadinIcon.TRASH.create());
HorizontalLayout actions = new HorizontalLayout(save, cancel, delete);
Binder<Employee> binder = new Binder<>(Employee.class);
private ChangeHandler changeHandler;
@Autowired
public EmployeeEditor(EmployeeRepository repository) {
this.repository = repository;
add(firstName, lastName, actions);
binder.bindInstanceFields(this);
setSpacing(true);
save.getElement().getThemeList().add("primary");
delete.getElement().getThemeList().add("error");
addKeyPressListener(Key.ENTER, e -> save());
save.addClickListener(e -> save());
delete.addClickListener(e -> delete());
cancel.addClickListener(e -> editEmployee(employee));
setVisible(false);
}
void delete() {
repository.delete(employee);
changeHandler.onChange();
}
void save() {
repository.save(employee);
changeHandler.onChange();
}
public interface ChangeHandler {
void onChange();
}
public final void editEmployee(Employee c) {
if (c == null) {
setVisible(false);
return;
}
final boolean persisted = c.getId() != null;
if (persisted) {
employee = repository.findById(c.getId()).get();
} else {
employee = c;
}
cancel.setVisible(persisted);
binder.setBean(employee);
setVisible(true);
firstName.focus();
}
public void setChangeHandler(ChangeHandler h) {
changeHandler = h;
}
}

View File

@ -0,0 +1,25 @@
package com.baeldung;
import com.baeldung.introduction.PushView;
import com.baeldung.introduction.basics.VaadinFlowBasics;
import com.baeldung.introduction.FormView;
import com.baeldung.introduction.GridView;
import com.baeldung.spring.EmployeesView;
import com.vaadin.flow.component.html.H1;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.router.RouterLink;
@Route("")
public class IndexView extends VerticalLayout {
public IndexView() {
add(new H1("Vaadin Flow examples"));
add(new RouterLink("Basics", VaadinFlowBasics.class));
add(new RouterLink("Grid", GridView.class));
add(new RouterLink("Form", FormView.class));
add(new RouterLink("Push", PushView.class));
add(new RouterLink("CRUD", EmployeesView.class));
}
}

View File

@ -1,67 +0,0 @@
package com.baeldung;
import org.springframework.util.StringUtils;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.value.ValueChangeMode;
import com.vaadin.flow.router.Route;
@Route
public class MainView extends VerticalLayout {
private final EmployeeRepository employeeRepository;
private final EmployeeEditor editor;
final Grid<Employee> grid;
final TextField filter;
private final Button addNewBtn;
public MainView(EmployeeRepository repo, EmployeeEditor editor) {
this.employeeRepository = repo;
this.editor = editor;
this.grid = new Grid<>(Employee.class);
this.filter = new TextField();
this.addNewBtn = new Button("New employee", VaadinIcon.PLUS.create());
HorizontalLayout actions = new HorizontalLayout(filter, addNewBtn);
add(actions, grid, editor);
grid.setHeight("200px");
grid.setColumns("id", "firstName", "lastName");
grid.getColumnByKey("id").setWidth("50px").setFlexGrow(0);
filter.setPlaceholder("Filter by last name");
filter.setValueChangeMode(ValueChangeMode.EAGER);
filter.addValueChangeListener(e -> listEmployees(e.getValue()));
grid.asSingleSelect().addValueChangeListener(e -> {
editor.editEmployee(e.getValue());
});
addNewBtn.addClickListener(e -> editor.editEmployee(new Employee("", "")));
editor.setChangeHandler(() -> {
editor.setVisible(false);
listEmployees(filter.getValue());
});
listEmployees(null);
}
void listEmployees(String filterText) {
if (StringUtils.isEmpty(filterText)) {
grid.setItems(employeeRepository.findAll());
} else {
grid.setItems(employeeRepository.findByLastNameStartsWithIgnoreCase(filterText));
}
}
}

View File

@ -0,0 +1,73 @@
package com.baeldung.data;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
@Entity
public class Contact {
@Id
@GeneratedValue
private Long id;
@NotBlank
private String name;
@Email
private String email;
@Pattern(regexp = "\\d{3}-\\d{3}-\\d{4}")
private String phone;
public Contact() {
}
public Contact(String name, String email, String phone) {
this.name = name;
this.email = email;
this.phone = phone;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
@Override
public String toString() {
return "Contact {" +
"id=" + id +
", name='" + name + "'" +
", email='" + email + "'" +
", phone='" + phone + "'" +
" }";
}
}

View File

@ -0,0 +1,6 @@
package com.baeldung.data;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ContactRepository extends JpaRepository<Contact, Long> {
}

View File

@ -0,0 +1,46 @@
package com.baeldung.data;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
@Component
public class DataInitializer implements CommandLineRunner {
private final ContactRepository contactRepository;
public DataInitializer(ContactRepository contactRepository) {
this.contactRepository = contactRepository;
}
private final List<String> firstNames = List.of(
"John", "Jane", "Emily", "Michael", "Sarah", "James", "Mary", "Robert",
"Patricia", "William", "Linda", "David", "Barbara", "Richard", "Susan",
"Joseph", "Jessica", "Thomas", "Karen", "Charles"
);
private final List<String> lastNames = List.of(
"Doe", "Smith", "Johnson", "Brown", "Davis", "Miller", "Wilson", "Moore",
"Taylor", "Anderson", "Thomas", "Jackson", "White", "Harris", "Martin",
"Thompson", "Garcia", "Martinez", "Robinson", "Clark"
);
@Override
public void run(String... args) throws Exception {
var contacts = new ArrayList<Contact>();
Random random = new Random();
for (int i = 0; i < 100; i++) {
String firstName = firstNames.get(random.nextInt(firstNames.size()));
String lastName = lastNames.get(random.nextInt(lastNames.size()));
String email = String.format("%s.%s@example.com", firstName.toLowerCase(), lastName.toLowerCase());
String phone = String.format("555-%03d-%04d", random.nextInt(1000), random.nextInt(10000));
contacts.add(new Contact(firstName + " " + lastName, email, phone));
}
contactRepository.saveAll(contacts);
}
}

View File

@ -1,8 +1,10 @@
package com.baeldung; package com.baeldung.data;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue; import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.validation.constraints.Size;
@Entity @Entity
public class Employee { public class Employee {
@ -11,11 +13,13 @@ public class Employee {
@GeneratedValue @GeneratedValue
private Long id; private Long id;
@Size(min = 2, message = "First name must have at least 2 characters")
private String firstName; private String firstName;
@Size(min = 2, message = "Last name must have at least 2 characters")
private String lastName; private String lastName;
protected Employee() { public Employee() {
} }
public Employee(String firstName, String lastName) { public Employee(String firstName, String lastName) {
@ -27,6 +31,10 @@ public class Employee {
return id; return id;
} }
public void setId(Long id) {
this.id = id;
}
public String getFirstName() { public String getFirstName() {
return firstName; return firstName;
} }

View File

@ -1,4 +1,4 @@
package com.baeldung; package com.baeldung.data;
import java.util.List; import java.util.List;

View File

@ -1,20 +0,0 @@
package com.baeldung.introduction;
public class BindData {
private String bindName;
public BindData(String bindName){
this.bindName = bindName;
}
public String getBindName() {
return bindName;
}
public void setBindName(String bindName) {
this.bindName = bindName;
}
}

View File

@ -0,0 +1,88 @@
package com.baeldung.introduction;
import com.baeldung.data.Contact;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.binder.BeanValidationBinder;
import com.vaadin.flow.data.binder.Binder;
import com.vaadin.flow.router.Route;
@Route("form")
public class FormView extends HorizontalLayout {
public FormView() {
addBeanValidationExample();
addCustomValidationExample();
}
private void addBeanValidationExample() {
var nameField = new TextField("Name");
var emailField = new TextField("Email");
var phoneField = new TextField("Phone");
var saveButton = new Button("Save");
var binder = new BeanValidationBinder<>(Contact.class);
binder.forField(nameField).bind(Contact::getName, Contact::setName);
binder.forField(emailField).bind(Contact::getEmail, Contact::setEmail);
binder.forField(phoneField).bind(Contact::getPhone, Contact::setPhone);
var contact = new Contact("John Doe", "john@doe.com", "123-456-7890");
binder.setBean(contact);
saveButton.addClickListener(e -> {
if (binder.validate().isOk()) {
Notification.show("Saved " + contact);
}
});
add(new VerticalLayout(
new H2("Bean Validation Example"),
nameField,
emailField,
phoneField,
saveButton
));
}
private void addCustomValidationExample() {
var nameField = new TextField("Name");
var emailField = new TextField("Email");
var phoneField = new TextField("Phone");
var saveButton = new Button("Save");
var binder = new Binder<>(Contact.class);
binder.forField(nameField)
.asRequired()
.bind(Contact::getName, Contact::setName);
binder.forField(emailField)
.withValidator(email -> email.contains("@"), "Not a valid email address")
.bind(Contact::getEmail, Contact::setEmail);
binder.forField(phoneField)
.withValidator(phone -> phone.matches("\\d{3}-\\d{3}-\\d{4}"), "Not a valid phone number")
.bind(Contact::getPhone, Contact::setPhone);
var contact = new Contact("John Doe", "john@doe.com", "123-456-7890");
binder.setBean(contact);
saveButton.addClickListener(e -> {
if (binder.validate().isOk()) {
Notification.show("Saved " + contact);
}
});
add(new VerticalLayout(
new H2("Custom Validation Example"),
nameField,
emailField,
phoneField,
saveButton
));
}
}

View File

@ -0,0 +1,40 @@
package com.baeldung.introduction;
import com.baeldung.data.Contact;
import com.baeldung.data.ContactRepository;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.H1;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.spring.data.VaadinSpringDataHelpers;
@Route("grid")
public class GridView extends VerticalLayout {
public GridView(ContactRepository contactRepository) {
add(new H1("Using data grids"));
// Define a grid for a Contact entity
var grid = new Grid<Contact>();
grid.addColumn(Contact::getName).setHeader("Name");
grid.addColumn(Contact::getEmail).setHeader("Email");
grid.addColumn(Contact::getPhone).setHeader("Phone");
// There are two ways to populate the grid with data:
// 1) In-memory with a list of items
var contacts = contactRepository.findAll();
grid.setItems(contacts);
// 2) with a callback to lazily load items from a data source.
grid.setItems(query -> {
// Turn the page, offset, filter, sort and other info in query into a Spring Data PageRequest
var pageRequest = VaadinSpringDataHelpers.toSpringPageRequest(query);
return contactRepository.findAll(pageRequest).stream();
});
add(grid);
}
}

View File

@ -0,0 +1,32 @@
package com.baeldung.introduction;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;
import reactor.core.publisher.Flux;
import java.time.Duration;
import java.time.Instant;
// Ensure you have @Push annotation in your Application class
@Route("push")
public class PushView extends VerticalLayout {
public PushView() {
var output = new Span();
// Publish server time once a second
var serverTime = Flux.interval(Duration.ofSeconds(1))
.map(o -> "Server time: " + Instant.now());
serverTime.subscribe(time ->
// ui.access is required to update the UI from a background thread
getUI().ifPresent(ui ->
ui.access(() -> output.setText(time))
)
);
add(output);
}
}

View File

@ -1,45 +0,0 @@
package com.baeldung.introduction;
public class Row {
private String column1;
private String column2;
private String column3;
public Row() {
}
public Row(String column1, String column2, String column3) {
super();
this.column1 = column1;
this.column2 = column2;
this.column3 = column3;
}
public String getColumn1() {
return column1;
}
public void setColumn1(String column1) {
this.column1 = column1;
}
public String getColumn2() {
return column2;
}
public void setColumn2(String column2) {
this.column2 = column2;
}
public String getColumn3() {
return column3;
}
public void setColumn3(String column3) {
this.column3 = column3;
}
}

View File

@ -1,281 +0,0 @@
package com.baeldung.introduction;
import java.time.Instant;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import com.vaadin.annotations.Push;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.data.Binder;
import com.vaadin.data.validator.StringLengthValidator;
import com.vaadin.icons.VaadinIcons;
import com.vaadin.server.ExternalResource;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.Button;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.ComboBox;
import com.vaadin.ui.DateField;
import com.vaadin.ui.FormLayout;
import com.vaadin.ui.Grid;
import com.vaadin.ui.GridLayout;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.InlineDateField;
import com.vaadin.ui.Label;
import com.vaadin.ui.Link;
import com.vaadin.ui.ListSelect;
import com.vaadin.ui.NativeButton;
import com.vaadin.ui.NativeSelect;
import com.vaadin.ui.Panel;
import com.vaadin.ui.PasswordField;
import com.vaadin.ui.RichTextArea;
import com.vaadin.ui.TextArea;
import com.vaadin.ui.TextField;
import com.vaadin.ui.TwinColSelect;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import jakarta.servlet.annotation.WebServlet;
@SuppressWarnings("serial")
@Push
@Theme("mytheme")
public class VaadinUI extends UI {
private Label currentTime;
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
protected void init(VaadinRequest vaadinRequest) {
final VerticalLayout verticalLayout = new VerticalLayout();
verticalLayout.setSpacing(true);
verticalLayout.setMargin(true);
final GridLayout gridLayout = new GridLayout(3, 2);
gridLayout.setSpacing(true);
gridLayout.setMargin(true);
final HorizontalLayout horizontalLayout = new HorizontalLayout();
horizontalLayout.setSpacing(true);
horizontalLayout.setMargin(true);
final FormLayout formLayout = new FormLayout();
formLayout.setSpacing(true);
formLayout.setMargin(true);
final GridLayout buttonLayout = new GridLayout(3, 5);
buttonLayout.setMargin(true);
buttonLayout.setSpacing(true);
final Label label = new Label();
label.setId("Label");
label.setValue("Label Value");
label.setCaption("Label");
gridLayout.addComponent(label);
final Link link = new Link("Baeldung", new ExternalResource("http://www.baeldung.com/"));
link.setId("Link");
link.setTargetName("_blank");
gridLayout.addComponent(link);
final TextField textField = new TextField();
textField.setId("TextField");
textField.setCaption("TextField:");
textField.setValue("TextField Value");
textField.setIcon(VaadinIcons.USER);
gridLayout.addComponent(textField);
final TextArea textArea = new TextArea();
textArea.setCaption("TextArea");
textArea.setId("TextArea");
textArea.setValue("TextArea Value");
gridLayout.addComponent(textArea);
final DateField dateField = new DateField("DateField", LocalDate.ofEpochDay(0));
dateField.setId("DateField");
gridLayout.addComponent(dateField);
final PasswordField passwordField = new PasswordField();
passwordField.setId("PasswordField");
passwordField.setCaption("PasswordField:");
passwordField.setValue("password");
gridLayout.addComponent(passwordField);
final RichTextArea richTextArea = new RichTextArea();
richTextArea.setCaption("Rich Text Area");
richTextArea.setValue("<h1>RichTextArea</h1>");
richTextArea.setSizeFull();
Panel richTextPanel = new Panel();
richTextPanel.setContent(richTextArea);
final InlineDateField inlineDateField = new InlineDateField();
inlineDateField.setValue(LocalDate.ofEpochDay(0));
inlineDateField.setCaption("Inline Date Field");
horizontalLayout.addComponent(inlineDateField);
Button normalButton = new Button("Normal Button");
normalButton.setId("NormalButton");
normalButton.addClickListener(e -> {
label.setValue("CLICK");
});
buttonLayout.addComponent(normalButton);
Button tinyButton = new Button("Tiny Button");
tinyButton.addStyleName("tiny");
buttonLayout.addComponent(tinyButton);
Button smallButton = new Button("Small Button");
smallButton.addStyleName("small");
buttonLayout.addComponent(smallButton);
Button largeButton = new Button("Large Button");
largeButton.addStyleName("large");
buttonLayout.addComponent(largeButton);
Button hugeButton = new Button("Huge Button");
hugeButton.addStyleName("huge");
buttonLayout.addComponent(hugeButton);
Button disabledButton = new Button("Disabled Button");
disabledButton.setDescription("This button cannot be clicked");
disabledButton.setEnabled(false);
buttonLayout.addComponent(disabledButton);
Button dangerButton = new Button("Danger Button");
dangerButton.addStyleName("danger");
buttonLayout.addComponent(dangerButton);
Button friendlyButton = new Button("Friendly Button");
friendlyButton.addStyleName("friendly");
buttonLayout.addComponent(friendlyButton);
Button primaryButton = new Button("Primary Button");
primaryButton.addStyleName("primary");
buttonLayout.addComponent(primaryButton);
NativeButton nativeButton = new NativeButton("Native Button");
buttonLayout.addComponent(nativeButton);
Button iconButton = new Button("Icon Button");
iconButton.setIcon(VaadinIcons.ALIGN_LEFT);
buttonLayout.addComponent(iconButton);
Button borderlessButton = new Button("BorderLess Button");
borderlessButton.addStyleName("borderless");
buttonLayout.addComponent(borderlessButton);
Button linkButton = new Button("Link Button");
linkButton.addStyleName("link");
buttonLayout.addComponent(linkButton);
Button quietButton = new Button("Quiet Button");
quietButton.addStyleName("quiet");
buttonLayout.addComponent(quietButton);
horizontalLayout.addComponent(buttonLayout);
final CheckBox checkbox = new CheckBox("CheckBox");
checkbox.setValue(true);
checkbox.addValueChangeListener(e -> checkbox.setValue(!checkbox.getValue()));
formLayout.addComponent(checkbox);
List<String> numbers = new ArrayList<String>();
numbers.add("One");
numbers.add("Ten");
numbers.add("Eleven");
ComboBox comboBox = new ComboBox("ComboBox");
comboBox.setItems(numbers);
formLayout.addComponent(comboBox);
ListSelect listSelect = new ListSelect("ListSelect");
listSelect.setItems(numbers);
listSelect.setRows(2);
formLayout.addComponent(listSelect);
NativeSelect nativeSelect = new NativeSelect("NativeSelect");
nativeSelect.setItems(numbers);
formLayout.addComponent(nativeSelect);
TwinColSelect twinColSelect = new TwinColSelect("TwinColSelect");
twinColSelect.setItems(numbers);
Grid<Row> grid = new Grid(Row.class);
grid.setColumns("column1", "column2", "column3");
Row row1 = new Row("Item1", "Item2", "Item3");
Row row2 = new Row("Item4", "Item5", "Item6");
List<Row> rows = new ArrayList();
rows.add(row1);
rows.add(row2);
grid.setItems(rows);
Panel panel = new Panel("Panel");
panel.setContent(grid);
panel.setSizeUndefined();
Panel serverPushPanel = new Panel("Server Push");
FormLayout timeLayout = new FormLayout();
timeLayout.setSpacing(true);
timeLayout.setMargin(true);
currentTime = new Label("No TIME...");
timeLayout.addComponent(currentTime);
serverPushPanel.setContent(timeLayout);
serverPushPanel.setSizeUndefined();
ScheduledExecutorService scheduleExecutor = Executors.newScheduledThreadPool(1);
Runnable task = () -> {
currentTime.setValue("Current Time : " + Instant.now());
};
scheduleExecutor.scheduleWithFixedDelay(task, 0, 1, TimeUnit.SECONDS);
FormLayout dataBindingLayout = new FormLayout();
dataBindingLayout.setSpacing(true);
dataBindingLayout.setMargin(true);
Binder<BindData> binder = new Binder<>();
BindData bindData = new BindData("BindData");
binder.readBean(bindData);
TextField bindedTextField = new TextField();
bindedTextField.setWidth("250px");
binder.forField(bindedTextField).bind(BindData::getBindName, BindData::setBindName);
dataBindingLayout.addComponent(bindedTextField);
FormLayout validatorLayout = new FormLayout();
validatorLayout.setSpacing(true);
validatorLayout.setMargin(true);
HorizontalLayout textValidatorLayout = new HorizontalLayout();
textValidatorLayout.setSpacing(true);
textValidatorLayout.setMargin(true);
BindData stringValidatorBindData = new BindData("");
TextField stringValidator = new TextField();
Binder<BindData> stringValidatorBinder = new Binder<>();
stringValidatorBinder.setBean(stringValidatorBindData);
stringValidatorBinder.forField(stringValidator)
.withValidator(new StringLengthValidator("String must have 2-5 characters lenght", 2, 5))
.bind(BindData::getBindName, BindData::setBindName);
textValidatorLayout.addComponent(stringValidator);
Button buttonStringValidator = new Button("Validate String");
buttonStringValidator.addClickListener(e -> stringValidatorBinder.validate());
textValidatorLayout.addComponent(buttonStringValidator);
validatorLayout.addComponent(textValidatorLayout);
verticalLayout.addComponent(gridLayout);
verticalLayout.addComponent(richTextPanel);
verticalLayout.addComponent(horizontalLayout);
verticalLayout.addComponent(formLayout);
verticalLayout.addComponent(twinColSelect);
verticalLayout.addComponent(panel);
verticalLayout.addComponent(serverPushPanel);
verticalLayout.addComponent(dataBindingLayout);
verticalLayout.addComponent(validatorLayout);
setContent(verticalLayout);
}
@WebServlet(urlPatterns = "/VAADIN/*", name = "MyUIServlet", asyncSupported = true)
@VaadinServletConfiguration(ui = VaadinUI.class, productionMode = false)
public static class MyUIServlet extends VaadinServlet {
}
}

View File

@ -0,0 +1,45 @@
package com.baeldung.introduction.basics;
import com.baeldung.data.Contact;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.splitlayout.SplitLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.Route;
import java.util.List;
@Route("example-layout")
public class ExampleLayout extends SplitLayout {
public ExampleLayout() {
var grid = new Grid<>(Contact.class);
grid.setColumns("name", "email", "phone");
grid.setItems(List.of(
new Contact("John Doe", "john@doe.com", "123 456 789"),
new Contact("Jane Doe", "jane@doe.com", "987 654 321")
));
var form = new VerticalLayout();
var nameField = new TextField("Name");
var emailField = new TextField("Email");
var phoneField = new TextField("Phone");
var saveButton = new Button("Save");
var cancelButton = new Button("Cancel");
form.add(
nameField,
emailField,
phoneField,
new HorizontalLayout(saveButton, cancelButton)
);
setSizeFull();
setSplitterPosition(70);
addToPrimary(grid);
addToSecondary(form);
}
}

View File

@ -0,0 +1,13 @@
package com.baeldung.introduction.basics;
import com.vaadin.flow.component.html.H1;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;
@Route("hello-world")
public class HelloWorldView extends VerticalLayout {
public HelloWorldView() {
add(new H1("Hello, World!"));
}
}

View File

@ -0,0 +1,63 @@
package com.baeldung.introduction.basics;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.checkbox.Checkbox;
import com.vaadin.flow.component.html.H1;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.router.RouterLink;
// The @Route annotation defines the URL path for the view
// Any component, most commonly a layout, can be used as a view
@Route("basics")
public class VaadinFlowBasics extends VerticalLayout {
public VaadinFlowBasics() {
// Add components to the layout with the add method
add(new H1("Vaadin Flow Basics"));
// Components are Java objects
var textField = new TextField("Name");
var button = new Button("Click me");
// Layouts define the structure of the UI
var verticalLayout = new VerticalLayout(
new Button("Top"),
new Button("Middle"),
new Button("Bottom")
);
add(verticalLayout);
var horizontalLayout = new HorizontalLayout(
new Button("Left"),
new Button("Center"),
new Button("Right")
);
add(horizontalLayout);
// Layouts can be nested for more complex structures
var nestedLayout = new VerticalLayout(
new HorizontalLayout(new Button("Top Left"), new Button("Top Right")),
new HorizontalLayout(new Button("Bottom Left"), new Button("Bottom Right"))
);
add(nestedLayout);
add(new RouterLink("Example layout", ExampleLayout.class));
// Use RouterLink to navigate to other views
var link = new RouterLink("Hello world view", HelloWorldView.class);
add(link);
// Use events to react to user input
var nameField = new TextField("Your name");
var helloButton = new Button("Say hello");
helloButton.addClickListener(e -> {
Notification.show("Hello, " + nameField.getValue());
});
add(nameField, helloButton);
}
}

View File

@ -0,0 +1,86 @@
package com.baeldung.spring;
import com.baeldung.data.Employee;
import com.vaadin.flow.component.Composite;
import com.vaadin.flow.component.Key;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.binder.BeanValidationBinder;
import com.vaadin.flow.data.binder.Binder;
public class EmployeeEditor extends Composite<VerticalLayout> {
public interface SaveListener {
void onSave(Employee employee);
}
public interface DeleteListener {
void onDelete(Employee employee);
}
public interface CancelListener {
void onCancel();
}
private Employee employee;
private SaveListener saveListener;
private DeleteListener deleteListener;
private CancelListener cancelListener;
private final Binder<Employee> binder = new BeanValidationBinder<>(Employee.class);
public EmployeeEditor() {
var firstName = new TextField("First name");
var lastName = new TextField("Last name");
var save = new Button("Save", VaadinIcon.CHECK.create());
var cancel = new Button("Cancel");
var delete = new Button("Delete", VaadinIcon.TRASH.create());
binder.forField(firstName).bind("firstName");
binder.forField(lastName).bind("lastName");
save.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
save.addClickListener(e -> save());
save.addClickShortcut(Key.ENTER);
delete.addThemeVariants(ButtonVariant.LUMO_ERROR);
delete.addClickListener(e -> deleteListener.onDelete(employee));
cancel.addClickListener(e -> cancelListener.onCancel());
getContent().add(firstName, lastName, new HorizontalLayout(save, cancel, delete));
}
private void save() {
// Save the form into a new instance of Employee
var updated = new Employee();
updated.setId(employee.getId());
if (binder.writeBeanIfValid(updated)) {
saveListener.onSave(updated);
}
}
public void setEmployee(Employee employee) {
this.employee = employee;
binder.readBean(employee);
}
public void setSaveListener(SaveListener saveListener) {
this.saveListener = saveListener;
}
public void setDeleteListener(DeleteListener deleteListener) {
this.deleteListener = deleteListener;
}
public void setCancelListener(CancelListener cancelListener) {
this.cancelListener = cancelListener;
}
}

View File

@ -0,0 +1,95 @@
package com.baeldung.spring;
import com.baeldung.data.Employee;
import com.baeldung.data.EmployeeRepository;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.value.ValueChangeMode;
import com.vaadin.flow.router.Route;
@Route("employees")
public class EmployeesView extends VerticalLayout {
private final EmployeeRepository employeeRepository;
private final TextField filter;
private final Grid<Employee> grid;
private final EmployeeEditor editor;
public EmployeesView(EmployeeRepository repo) {
employeeRepository = repo;
// Create components
var addButton = new Button("New employee", VaadinIcon.PLUS.create());
filter = new TextField();
grid = new Grid<>(Employee.class);
editor = new EmployeeEditor();
// Configure components
configureEditor();
addButton.addClickListener(e -> editEmployee(new Employee()));
filter.setPlaceholder("Filter by last name");
filter.setValueChangeMode(ValueChangeMode.LAZY);
filter.addValueChangeListener(e -> updateEmployees(e.getValue()));
grid.setHeight("200px");
grid.asSingleSelect().addValueChangeListener(e -> editEmployee(e.getValue()));
// Compose layout
var actionsLayout = new HorizontalLayout(filter, addButton);
add(actionsLayout, grid, editor);
// List customers
updateEmployees("");
}
private void configureEditor() {
editor.setVisible(false);
editor.setSaveListener(employee -> {
var saved = employeeRepository.save(employee);
updateEmployees(filter.getValue());
editor.setEmployee(null);
grid.asSingleSelect().setValue(saved);
});
editor.setDeleteListener(employee -> {
employeeRepository.delete(employee);
updateEmployees(filter.getValue());
editEmployee(null);
});
editor.setCancelListener(() -> {
editEmployee(null);
});
}
private void editEmployee(Employee employee) {
editor.setEmployee(employee);
if (employee != null) {
editor.setVisible(true);
} else {
// Deselect grid
grid.asSingleSelect().setValue(null);
editor.setVisible(false);
}
}
private void updateEmployees(String filterText) {
if (filterText.isEmpty()) {
grid.setItems(employeeRepository.findAll());
} else {
grid.setItems(employeeRepository.findByLastNameStartsWithIgnoreCase(filterText));
}
}
}

View File

@ -1,7 +0,0 @@
/* This file is automatically managed and will be overwritten from time to time. */
/* Do not manually edit this file. */
/* Import and include this mixin into your project theme to include the addon themes */
@mixin addons {
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

View File

@ -1,38 +0,0 @@
// If you edit this file you need to compile the theme. See README.md for details.
// Global variable overrides. Must be declared before importing Valo.
// Defines the plaintext font size, weight and family. Font size affects general component sizing.
//$v-font-size: 16px;
//$v-font-weight: 300;
//$v-font-family: "Open Sans", sans-serif;
// Defines the border used by all components.
//$v-border: 1px solid (v-shade 0.7);
//$v-border-radius: 4px;
// Affects the color of some component elements, e.g Button, Panel title, etc
//$v-background-color: hsl(210, 0%, 98%);
// Affects the color of content areas, e.g Panel and Window content, TextField input etc
//$v-app-background-color: $v-background-color;
// Affects the visual appearance of all components
//$v-gradient: v-linear 8%;
//$v-bevel-depth: 30%;
//$v-shadow-opacity: 5%;
// Defines colors for indicating status (focus, success, failure)
//$v-focus-color: valo-focus-color(); // Calculates a suitable color automatically
//$v-friendly-color: #2c9720;
//$v-error-indicator-color: #ed473b;
// For more information, see: https://vaadin.com/book/-/page/themes.valo.html
// Example variants can be copy/pasted from https://vaadin.com/wiki/-/wiki/Main/Valo+Examples
@import "../valo/valo.scss";
@mixin mytheme {
@include valo;
// Insert your own theme rules here
}

View File

@ -1,11 +0,0 @@
@import "mytheme.scss";
@import "addons.scss";
// This file prefixes all rules with the theme name to avoid causing conflicts with other themes.
// The actual styles should be defined in mytheme.scss
.mytheme {
@include addons;
@include mytheme;
}