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