Web Apps mit
VAADIN 14
Tobse Fritz
- 💰 Reich
- 🦄 Gut aussehend
- 🐱👤 Kotlin Fanboy
- 🔨 Java Entwickler
- 🧹 Clean Coder
- 📸 Hobby Photographer
- 🎮 Gaming Nerd
- 💼 ITscope GmbH
Highlights
- Vaadin what?
- UI Entwicklung
-
- Flow Componenten
- Element API
- Custom HTML
- Polymer Templates
- Web-Components
- Progressive Web App
- Vaadin und der Rest der Welt
- Risiken und Nebenwirkungen
- Ausblick
Vaadin what?
- Vaadin 6
- 2009
- 2010
- Vaadin Directory
- 2020
- Vaadin 14
- 2007
- IT Mill Toolkit
- Ajax → GWT
- Swing like backend
- Vaadin 10
- Web Components
- Modern mobile first web
- 2018
Flow Componenten
34
Standard-
Komponenten
Clean Code App
VerticalLayout layout = new VerticalLayout();
TextField userName = new TextField();
userName.setCaption("Benutzername");
userName.setValue("Alex");
userName.addValueChangeListener(e -> profile.setName(e.getValue()));
layout.addComponent(userName);
ProfileView.java
Flow Componenten
Flow Databinding
Flow Databinding
GradeTopic
+ name: String
+ description: String
+ sectionWhys: String
GradeRating
+ Responsibility: {Team, SingleDev}
+ evolvability: Int
+ correctness: String
Collection<GradeTopic> gradeTopics = loadData();
Grid<GradeTopic> table = new Grid<>();
table.setItems(gradeTopics);
table.addColumn(GradeTopic::getName)
.setHeader(getTranslation("grade.name"))
.setSortable(true).setAutoWidth(true);
table.addColumn(this::formatTopic)
.setHeader(getTranslation("grade.grade"))
.setSortable(true).setAutoWidth(true);
table.addColumn(topic -> topic.getGradeRating().getResponsibility()
.setHeader(getTranslation("grade.rating.responsibility"))
.setSortable(true).setAutoWidth(true);
table.sort(List.of(new GridSortOrder<>(columnName, ASCENDING)));
table.addSelectionListener(event -> event.getFirstSelectedItem()
.ifPresent(this::openGradeTopic));
CompendiumView.java
Flow Databinding
Vaadin Designer
https://vaadin.com/designer
Element API
Anchor Article Aside Description DescriptionList Div Emphasis Footer H1 H2
H3 H4 H5 H6 Header Image Label ListItem Main
NativeButton Nav OrderedList Paragraph Pre Section Span Term UnorderedList
HtmlContainer
Element API
public class GradeProgressBar extends FlexLayout {
public GradeProgressBar() {
setWidthFull();
setJustifyContentMode(JustifyContentMode.EVENLY);
setAlignItems(Alignment.END);
addClassName("grade-progress");
}
public void setDays(List<ProgressDay> days) {
days.forEach(this::add);
}
}
GradeProgressBar : FlexLayout
ProgressDay : Div
Span
Element API
public static class ProgressDay extends Div {
public ProgressDay(String tooltip) {
addClassName("grade-progress-day");
addToolTip(tooltip);
}
public void setDaySubmitted() {
addClassName("day-submitted");
}
public void setDayInFuture() {
addClassName("day-future");
}
private void addToolTip(String tooltip) {
addClassName("tooltip");
Span span = new Span(tooltip);
span.addClassName("tooltiptext");
span.addClassName("tooltip-bottom");
add(span);
}
}
.grade-progress {
height: 45px;
}
.grade-progress-day {
background-color: grey;
color: grey;
width: 10px;
height: 40px;
transition: border-top 0.8s;
top: 0;
bottom: 0;
}
.grade-progress-day:hover {
border-top: solid currentColor 10px;
}
.day-submitted {
background-color: green;
}
.day-future {
background-color: black;
}
Custom HTML
content.add(new Html(getTranslation("about.info")));
content.add(new Html(getTranslation("about.info.technical")));
about.info = <p>Hast du schon einmal versucht den Weg eines <a href="https://clean-code-developer.de/" target="blank">Clean Code Developers</a> zu gehen und den weißen grad zu meistern? Diese Webapp hilft Dir diesen Weg einzuhalten und erinnert dich an deine aktuellen Ziele. Logge deinen Fortschritt und bekomme Belohnungen für deine erreichten Ziele 🏆.</p>
about.info.technical = <p>Dieses Projekt ist eine Beispielapplikation für eine <a href="https://vaadin.com/" target="blank">Vaadin 14</a> Porgressive Web App. Es nutzt <a href="https://spring.io/projects/spring-boot" target="blank">Spring Boot</a> und läuft auf <a href="https://www.oracle.com/technetwork/java/javase/12-relnote-issues-5211422.html" target="blank">Java 12</a>. Diese Demo löscht alle Nutzerdaten sobald die Session verloren geht.</p>
Web Components
Benutzerdefinierte Elemente
HTML-Vorlagen
Shadow DOM
Web Components
Polymer WebComponent - l2t-paper-color
Web Components
developer.mozilla.org/de/docs/Web/Web_Components
Polymer Templates
web components library
- Custom elements
- Shadow DOM
- Events
- Data Binding
Polymer Templates
web components library
Polymer 2.0
- HTML Imports
- Bower
05.2017
Polymer 3.0
- ES6 Modules
- npm
01.2018
LitElement
- Lightweigt
Polymer
02.2019
Polymer Templates
import {PolymerElement, html} from '@polymer/polymer/polymer-element.js';
class MyElement extends PolymerElement {
static get template() {
return html`
<style> .mood { color: orange; } </style>
<h2>Web Components are <span class="mood">[[message]]</span>!</h2>
`;
}
static get properties() { return { message: String }}
}
customElements.define('my-element', MyElement);
Polymer Templates
Polymer in Vaadin
Polymer in Vaadin
import {html, PolymerElement} from '@polymer/polymer/polymer-element.js';
import '@polymer/iron-icon/iron-icon';
import '@vaadin/vaadin-material-styles/color';
class LogEntry extends PolymerElement {
static get template() {
return html`<style>
#logentry {
width: 380px;
}
#header {
display: flex;
}
#title {
flex-grow: 1;
}
#username {
font-size: 18px;
}
#grade {
font-size: 16px;
line-height: 10px;
}
#date {
color: gray;
align-self: flex-end;
}
#content-head {
display: flex;
justify-content: space-between;
}
#experience {
display: flex;
align-self: flex-end;
}
#xp {
color: #22C4FA;
margin-left: 8px;
font-size: 18px;
}
#skill-points {
color: #20E87B;
}
#topic {
font-size: 18px;
}
#content {
display: flex;
flex-direction: column;
background-color: var(--material-secondary-background-color);
border-radius: 5px;
padding: 5px;
}
#actions {
align-self: flex-end;
}
#avatar{
margin-right: 5px;
}
iron-icon.avatar {
--iron-icon-height: 38px;
--iron-icon-width: 38px;
}
iron-icon.type {
--iron-icon-height: 18px;
--iron-icon-width: 18px;
}
iron-icon.action {
--iron-icon-height: 22px;
--iron-icon-width: 22px;
}
</style>
<div id="logentry">
<div id="header">
<div id="avatar">
<iron-icon class="avatar" icon="vaadin:user"></iron-icon>
</div>
<div id="title">
<div id="username">[[username]]</div>
<div id="grade">[[grade]]</div>
</div>
<div id="date">[[date]]</div>
</div>
<div id="content">
<div id="content-head">
<div id="topic">[[topic]]</div>
<div id="type">
<iron-icon class="type" icon="[[typeicon]]"></iron-icon>
</div>
</div>
<div id="comment">[[comment]]</div>
<div id="experience">
<div id="skill-points">[[skills]]</div>
<div id="xp">[[experience]]</div>
</div>
<div id="actions">
<iron-icon class="action" icon="vaadin:close-small"></iron-icon>
<iron-icon class="action" icon="vaadin:comment"></iron-icon>
<iron-icon class="action" icon="vaadin:pencil"></iron-icon></div>
</div>
</div>`;
}
static get is() {
return 'log-entry';
}
}
customElements.define(LogEntry.is, LogEntry);
Polymer in Vaadin
@Tag("log-entry")
@JsModule("./src/log-entry.js")
public class LogEntryComponent extends PolymerTemplate<LogEntryModel> {
public LogEntryComponent() {
setId("logentry");
}
@Override
public LogEntryModel getModel() {
return super.getModel();
}
}
public interface LogEntryModel extends TemplateModel {
String getTypeicon();
void setTypeicon(String typeicon);
String getSkills();
void setSkills(String skills);
String getTopic();
void setTopic(String topic);
String getGrade();
void setGrade(String grade);
String getExperience();
void setExperience(String experience);
String getComment();
void setComment(String comment);
String getUsername();
void setUsername(String username);
String getDate();
void setDate(String date);
}
Polymer in Vaadin
private void addLogEntry(LogEntry logEntry) {
var logEntryComponent = new LogEntryComponent();
LogEntryModel logEntryModel = logEntryComponent.getModel();
logEntryModel.setComment(logEntry.getComment());
logEntryModel.setUsername(logEntry.getName());
parent.add(logEntryComponent);
}
JournalView.java
Web Components
Web Components
@Tag("paper-fab-speed-dial")
@NpmPackage(value = "@cwmr/paper-fab-speed-dial", version = "3.0.0")
@JsModule("@cwmr/paper-fab-speed-dial/paper-fab-speed-dial.js")
public class SpeedDial extends Component implements HasEnabled {
public SpeedDialAction addMenuItem(String item, Icon icon) {
SpeedDialAction speedDialAction = new SpeedDialAction(item, icon);
getElement().appendChild(speedDialAction.getElement());
return speedDialAction;
}
public SpeedDialAction addMenuItem(String item, Icon icon,
ComponentEventListener<SpeedDialAction.ClickEvent> listener) {
SpeedDialAction speedDialAction = addMenuItem(item, icon);
speedDialAction.addClickListener(listener);
return speedDialAction;
}
@Synchronize("opened-changed")
public boolean isOpened() {
return getElement().getProperty("opened", false);
}
public void setBackdrop(boolean backdrop) {
if (backdrop) {
getElement().setAttribute("with-backdrop", EMPTY);
} else {
getElement().removeAttribute("with-backdrop");
}
}
public void close() {
getElement().setProperty("opened", false);
}
public void open() {
getElement().setProperty("opened", true);
}
@Override
public void setEnabled(boolean enabled) {
if (enabled) {
getElement().setAttribute("disabled", "disabled");
} else {
getElement().removeAttribute("disabled");
}
}
public void setColorAction(String color) {
setStyle("--paper-fab-speed-dial-action-background", color);
}
public void setColor(String color) {
setStyle("--paper-fab-speed-dial-background", color);
}
public void setMarginRight(String marginRight) {
setStyle("--paper-fab-speed-dial-right", marginRight);
}
private Style setStyle(String name, String value) {
return getElement().getStyle().set(name, value);
}
}
Web Components
@Tag("paper-fab-speed-dial-action")
@NpmPackage(value = "@cwmr/paper-fab-speed-dial", version = "3.0.0")
@JsModule("@cwmr/paper-fab-speed-dial/paper-fab-speed-dial-action.js")
public class SpeedDialAction extends Label implements HasEnabled, HasSpeedDialStyle {
public SpeedDialAction(String text, Icon icon) {
super(text);
setIcon(icon);
}
public Element setIcon(Icon icon) {
return getElement().setAttribute("icon", getIconAttribute(icon));
}
public Registration addClickListener(ComponentEventListener<SpeedDialClickEvent> listener) {
return addListener(SpeedDialClickEvent.class, listener);
}
private String getIconAttribute(Icon icon) {
return icon.getElement().getAttribute("icon");
}
}
SpeedDialAction.java
Web Components
SpeedDial speedDial = new SpeedDial();
speedDial.addMenuItem("Erfolg", VaadinIcon.TROPHY, e -> Notification.show("Clicked E"));
speedDial.addMenuItem("Log", VaadinIcon.NOTEBOOK, e -> Notification.show("Clicked L"));
speedDial.setBackdrop(true);
add(speedDial);
SpeedDialSampleView.java
Styling mit CSS Variablen
public void setColorAction(String color) {
setStyle("--paper-fab-speed-dial-action-background", color);
}
SpeedDial speedDial = new SpeedDial();
setColor("var(--material-primary-text-color)");
setColorAction("var(--material-primary-text-color)");
add(speedDial);
SampleCssVar.java
SpeedDial.java
- 1829 "Components"
- 582 Webcompontens
- 200 "Platform"
Vaadin Directory
vaadin.com/directory
Vaadin Directory
⭐ ServerSide ClickListener
⭐ Setters für alle 11 CSS Properties
⭐ Build in Support für Vaadin Icons
⭐ Vaadin Core - Pro Lizenz nicht erforderlich
Paper Speed Dial
Progressive Web App
-
Zuverlässig
- Sofort einsatzbereit
- Offlinefähig
-
Schnell
- Schnelles Nutzerfeedback
-
Integriert
- Fühlt sich wie lokale App an
- Lässt sich installieren
- Funktioniert auf Desktop & Mobil
Progressive Web Ap
@CssImport("./styles/shared-styles.css")
@Theme(value = Material.class, variant = Lumo.DARK)
@PWA(name = "Clean Code Developer Journal", backgroundColor = "#3B3B3B")
public class MainView extends AppLayout {
public MainView() {
addToNavbar(new DrawerToggle());
addToNavbar(new H4(getTranslation("app.name")));
addToDrawer(createMenuBar());
}
private VerticalLayout createMenuBar() {
RouterLink journal = new RouterLink("Journal", JournalView.class);
RouterLink profile = new RouterLink("Profil", ProfileView.class);
RouterLink achievements = new RouterLink("Erfolge", AchievementsView.class);
RouterLink compendium = new RouterLink("Compendium", CompendiumView.class);
RouterLink about = new RouterLink("Über", AboutView.class);
return new VerticalLayout(journal, profile, achievements, compendium, about);
}
}
MainView.java
Progressive Web Ap
Integriert
Progressive Web Ap
Mobile & Web
Progressive Web Ap
Offlinefähigkeit
Vaadin
und der Rest der Welt
Risiken und Nebenwirkungen
- UI muss serialisierbar sein
- Kein vorgegebenes UI Pattern
- Speicherverbrauch
- Hot Code Replacement
- DCEVM
- Spring Devtools
- JRebel
Ausblick
- Vaadin 14
- Router
- @Push
- LitElement
- Vaadin 15
- Supportende für IE 11, Safari 9-12
- Flow 3.0 - Typescript Support
- pnpm - Schnelleres Setup
- Vaadin on Kotlin (Vok)
- Offline Components
-
- Karibu-DSL
- Karibu-Testing
THE END
Clean-Code.rocks
github.com/TobseF/CleanCodeDeveloperJournal
THE END
Quellen
Clean-Code.rocks
github.com/TobseF/CleanCodeDeveloperJournal
Web Apps mit Vaadin 14
By Tobse Fritz
Web Apps mit Vaadin 14
Moderne Progressive-Web-Apps mit Java 14 und Vaadin 14
- 936