集成 JavaScript 库

可以基于第三方 JavaScript 库创建自定义的 UI 组件。本节中的示例演示了如何集成 jQuery UI Slider

创建 JavaScript Web 组件

frontend/src/component/slider 内创建如下 slider.js 文件,其中带有包装 jQuery UI Slider 小部件的 Web 组件。ready() 方法用于初始化小部件。

slider.js
import 'jquery/jquery.js'
import 'jquery-ui/dist/jquery-ui.js'
import {PolymerElement} from '@polymer/polymer/polymer-element.js';
import {defineCustomElement} from '@vaadin/component-base/src/define.js';
import {ElementMixin} from '@vaadin/component-base/src/element-mixin.js';

class Slider extends ElementMixin(PolymerElement) { (1)

    static get is() {
        return 'demo-slider'; (2)
    }

    static get properties() { (3)
        return {
            min: {
                type: Number,
                value: 0,
                observer: '_onMinChange' (4)
            },

            max: {
                type: Number,
                value: 100,
                observer: '_onMaxChange'
            },

            value: {
                type: Number,
                notify: true,
                observer: '_onValueChange'
            },

            /** @private */
            _slider: {
                type: Object
            }
        }
    }

    ready() {
        super.ready();

        this._slider = $(this); (5)
        this._slider.slider({ (6)
            min: this.min,
            max: this.max,

            change: function (event, ui) {
                if (this.value === ui.value) {
                    return;
                }
                this.value = ui.value;
                const slideChangeEvent = new CustomEvent(
                    'custom-slide-changed',
                    {detail: {value: ui.value}}
                );
                this.dispatchEvent(slideChangeEvent); (7)
            }
        });
    }

    /**
     * @protected
     */
    _onValueChange(value) { (8)
        if (this._slider === undefined) {
            return;
        }

        this._slider.slider("value", value);
    }

    /**
     * @protected
     */
    _onMinChange(value) {
        if (this._slider === undefined) {
            return;
        }

        this._slider.slider("option", "min", value);
    }

    /**
     * @protected
     */
    _onMaxChange(value) {
        if (this._slider === undefined) {
            return;
        }

        this._slider.slider("option", "max", value);
    }
}

defineCustomElement(Slider); (9)

export {Slider};
1 定义自定义组件的基类。
2 定义 HTML 元素的名称。
3 自定义属性。
4 定义属性更改的监听器。
5 获取根元素的链接。
6 在根元素初始化 jQuery UI Slider
7 发送 custom-slide-changed 名称的自定义事件。
8 当属性改变时,为 slider 设置新值。
9 使用在 static get is() 方法中定义的名称导出自定义 HTML 元素。

为 Web 组件创建 Java API

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

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

@Tag("demo-slider") (1)
@NpmPackage(value = "jquery", version = "1.9.1") (2)
@NpmPackage(value = "jquery-ui", version = "1.13.2")
@CssImport("jquery-ui/dist/themes/base/jquery-ui.css")
@JsModule("./src/component/slider/slider.js") (3)
public class Slider extends Component implements HasSize {

    private static final String VALUE_PROPERTY = "value";
    private static final String MIN_PROPERTY = "min";
    private static final String MAX_PROPERTY = "max";

    public Slider() {
    }

    public int getMin() {
        return getElement().getProperty(MIN_PROPERTY, 0);
    }

    public void setMin(int min) {
        getElement().setProperty(MIN_PROPERTY, min);
    }

    public int getMax() {
        return getElement().getProperty(MAX_PROPERTY, 100);
    }

    public void setMax(int max) {
        getElement().setProperty(MAX_PROPERTY, max);
    }

    @Synchronize("custom-slide-changed") (4)
    public int getValue() {
        return getElement().getProperty(VALUE_PROPERTY, 0);
    }

    public void setValue(int value) {
        getElement().setProperty(VALUE_PROPERTY, value);
    }

}
1 定义由 Component` 类自动创建的 元素,可以通过 getElement() 方法访问。必须与 Web 组件导出的相同。
2 @NpmPackage 注解定义依赖的 npm 包,其中包含一个或多个 JS 模块。
3 @JsModule 注解定义需要导入 JavaScript 模块。
4 为 getter 添加 @Synchronize 注解,确保浏览器将属性的更改发送到服务端。注解定义了触发同步的 DOM 事件的名称,在本例中为 custom-slide-changed 事件。

在客户端处理事件

可以将组件事件与浏览器中元素触发的 DOM 事件相关联。为此,需在事件类上使用 @DomEvent 注释来指定要监听的 DOM 事件的名称。如果存在组件事件的监听器,Vaadin Flow 将自动为元素添加 DOM 事件的监听器。

事件可以包含其他附加信息,例如在单击事件中使用的鼠标键。@DomEvent 构造函数支持额外的参数。可以使用 @EventData 指定需要从浏览器发送的数据。

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

@DomEvent("custom-slide-changed") (1)
public static class SlideChangedEvent extends ComponentEvent<Slider> {

    protected int value;

    public SlideChangedEvent(Slider source, boolean fromClient,
                             @EventData("event.detail.value") int value) { (2)
        super(source, fromClient);
        this.value = value;
    }

    public int getValue() {
        return value;
    }
}
1 使用 @DomEvent 注解将 Slider 组件与 custom-slide-changed DOM 事件相关联
2 使用 @EventData 注解定义附加的事件数据,本例中为 slider 的值。
关于创建自定义组件的更多内容可以参阅 Vaadin 文档: 导入 JavaScript & CSS集成 Web 组件Element API

用例

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

@Autowired
protected Notifications notifications;

@Subscribe
public void onInit(final InitEvent event) {
    Slider slider = new Slider();
    slider.setWidth("20em");
    slider.setMin(10);
    slider.setMax(100);

    slider.addValueChangeListener(changedEvent ->
            notifications.create("New value is : " + changedEvent.getValue())
                    .withPosition(Notification.Position.MIDDLE)
                    .show());

    getContent().add(slider);
}
slider usage