创建 Web 组件

可以基于 Polymer 3Lit 库创建自己的 Web 组件。实现 JavaScript Web 组件后,可以为其创建 Java API。

下面的示例展示基于 Lit 如何创建明暗主题切换的开关。

创建 JavaScript Web 组件

frontend/src/component/theme-toggle 文件夹创建 theme-toggle.js 文件。其中包含一个 Web 组件,实现了明暗主题切换的开关。

import {html, LitElement} from 'lit';
import {PolylitMixin} from '@vaadin/component-base/src/polylit-mixin.js';
import {defineCustomElement} from '@vaadin/component-base/src/define.js';
import {ElementMixin} from '@vaadin/component-base/src/element-mixin.js';
import {TooltipController} from "@vaadin/component-base/src/tooltip-controller";
import {css, ThemableMixin} from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
import {buttonStyles} from '@vaadin/button/src/vaadin-button-base.js';
import {button as buttonLumoStyles} from '@vaadin/button/theme/lumo/vaadin-button-styles.js';
import {ButtonMixin} from '@vaadin/button/src/vaadin-button-mixin.js';

const themeToggleStyles = css`
    :host {
        background: transparent;
        color: var(--lumo-text-color);
        min-width: var(--lumo-button-size);
        padding-left: calc(var(--lumo-button-size) / 4);
        padding-right: calc(var(--lumo-button-size) / 4);
    }
`;

class ThemeToggle extends ButtonMixin(ElementMixin(ThemableMixin(PolylitMixin(LitElement)))) { (1)

    static get is() {
        return 'theme-toggle'; (2)
    }

    static get styles() { (3)
        return [buttonStyles, buttonLumoStyles, themeToggleStyles];
    }

    render() { (4)
        return html`
            <div class="vaadin-button-container">
                <vaadin-icon icon="vaadin:adjust"></vaadin-icon>
            </div>

            <slot name="tooltip"></slot>
        `;
    }

    static get properties() { (5)
        return {
            ariaLabel: {
                type: String,
                value: 'Theme toggle',
                reflectToAttribute: true,
            }
        };
    }

    constructor() {
        super();

        this._storageKey = "app-theme";
        this.addEventListener('click', () => this.toggleTheme());
    }

    /** @protected */
    ready() {
        super.ready();

        this._tooltipController = new TooltipController(this); (6)
        this.addController(this._tooltipController);
        this.applyStorageTheme();
    }

    applyStorageTheme() {
        let storageTheme = this.getStorageTheme();
        let currentTheme = this.getCurrentTheme();
        if (storageTheme && currentTheme !== storageTheme) {
            this.applyTheme(storageTheme);
        }
    }

    getStorageTheme() {
        return localStorage.getItem(this._storageKey);
    }

    getCurrentTheme() {
        return document.documentElement.getAttribute("theme");
    }

    toggleTheme() {
        const theme = this.getCurrentTheme();
        this.applyTheme(theme === "dark" ? "" : "dark");
    }

    applyTheme(theme) {
        document.documentElement.setAttribute("theme", theme);
        localStorage.setItem(this._storageKey, theme);

        const customEvent = new CustomEvent('theme-changed', {detail: {value: theme}});
        this.dispatchEvent(customEvent); (7)
    }
}

defineCustomElement(ThemeToggle); (8)

export {ThemeToggle};
1 自定义组件的基类。由于 ThemeToggle 需要与按钮的外观和行为类似,因此使用了 ButtonMixin 基类,可以提供按钮相关的属性和操作。
2 定义 HTML 元素的名称。
3 定义组件的样式,使用了导入的 vaadin-button 样式和自定义样式。
4 定义 Shadow DOM 模板。
5 自定义属性。
6 初始化 TooltipController 类,用于处理提示框的变化。控制器类用作视觉组件的重复功能的代理。
7 发送名为 theme-changed 的自定义事件。
8 使用在 static get is() 方法中定义的名称导出自定义 HTML 元素。

为 Web 组件创建 Java API

创建 ThemeToggle.java 文件,这是一个 UI 组件类。为服务端代码、访问方法、事件监听器和数据源连接定义了 API。

import com.vaadin.flow.component.*;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.shared.HasTooltip;
import com.vaadin.flow.shared.Registration;

@Tag("theme-toggle") (1)
@JsModule("./src/component/theme-toggle/theme-toggle.js") (2)
public class ThemeToggle extends Component implements ClickNotifier<ThemeToggle>,
        Focusable<ThemeToggle>, HasTheme, HasEnabled, HasSize, HasStyle, HasTooltip, HasAriaLabel { (3)

    public ThemeToggle() {
    }

    public Registration addThemeChangeListener(ComponentEventListener<ThemeToggleThemeChangedEvent> listener) {
        return addListener(ThemeToggleThemeChangedEvent.class, listener);
    }

    @DomEvent("theme-changed") (4)
    public static class ThemeToggleThemeChangedEvent extends ComponentEvent<ThemeToggle> {

        protected String value;

        public ThemeToggleThemeChangedEvent(ThemeToggle source, boolean fromClient,
                                            @EventData("event.detail.value") String value) { (5)
            super(source, fromClient);
            this.value = value;
        }

        public String getValue() {
            return value;
        }
    }
}
1 定义由 Component` 类自动创建的 元素,可以通过 getElement() 方法访问。必须与 Web 组件导出的相同。
2 @JsModule 注解定义需要导入 JavaScript 模块。
3 使用 Vaadin Mixin 接口 为 Web 组件中的大部分功能集提供通用 API 和默认行为。
4 使用 @DomEvent 注解关联 ThemeToggle 组件与 theme-changed DOM 事件。
5 使用 @EventData 注解定义附加的事件数据,这里用到了主题值。
关于创建自定义组件的更多内容可以参阅 Vaadin 文档: 创建组件使用 Vaadin Mixin 接口使用组件事件

用例

组件实现完成后,可以在视图中使用,示例:

@Subscribe
public void onInit(final InitEvent event) {
    ThemeToggle themeToggle = new ThemeToggle();
    getContent().add(themeToggle);
}
theme toggle usage