【问题标题】:Vaadin: open calendar on field focus for datefieldVaadin:在日期字段的字段焦点上打开日历
【发布时间】:2017-06-12 11:41:29
【问题描述】:

Vaadin 小部件既简单又棒!但它们的可配置性也很差。 我需要我的 DateField 小部件来打开焦点事件的日历。我没有在官方Vaadin documentation 中找到该功能。我找到了一些第 3 方小部件 here,但它是为 Vaadin 7.7 编译的,我使用最新的 Vaadin (8.0.6)。它还具有 Joda-time 2.1 依赖项,这在我的项目中非常不受欢迎。那么,是否有任何简单的方法来调整股票 vaadin DateField 小部件以在字段焦点上打开它的日历,还是我需要为此编写自己的组件?任何帮助表示赞赏。

【问题讨论】:

  • 不完全确定您的意思,也许您可​​以分享sscce 并提供更多详细信息?例如,使用 Vaadin 8.0.6,如​​果我创建一个带有过去某个日期的 DateField(例如 addComponent(new DateField("Event date", LocalDate.of(2011, Month.FEBRUARY, 6))))并单击其日历图标,它将弹出该特定页面和日期...
  • @Morfic 我想打开日历弹出窗口,不仅可以通过单击日历按钮,还可以通过关注某个字段本身。
  • 现在更清楚了,最初我误解了您要打开弹出窗口聚焦选定的事件日期。不幸的是,到目前为止,还没有简单的方法可以以编程方式打开这些弹出窗口,例如:日历日期选择、网格编辑器、组合项目。您可以添加javascript extension 以单击焦点按钮。我将尝试总结一个简单的示例来帮助您入门...

标签: vaadin vaadin8


【解决方案1】:

正如我在评论中所说,据我所知,目前该框架不提供以编程方式打开日历弹出窗口的隐式方式。其他一些组件也是如此,例如网格编辑器或组合项列表。

我能想到的一个快速解决方法是添加一个javascript extension,它为所有日期字段注册焦点侦听器,并在日期字段获得焦点时单击按钮。请在下面找到一个示例。

附:如果您只需要将此应用于某些日期字段,则可以添加 ID 并将其传递给 JS,在那里您将执行类似 document.getElementById('myDateFieldId') 而不是 document.getElementsByClassName("v-datefield") 的操作。

1) 带有组件的布局

public class MyDateFieldComponent extends HorizontalLayout {
    public MyDateFieldComponent() {
        // basic setup
        DateField fromDateField = new DateField("From", LocalDate.of(2011, Month.FEBRUARY, 6));
        DateField toDateField = new DateField("To", LocalDate.of(2018, Month.FEBRUARY, 6));
        setSpacing(true);
        addComponents(fromDateField, toDateField);

        // add the extension
        addExtension(new CalendarFocusPopupOpenerExtension());
    }
}

2) 扩展 - java/服务器端

import com.vaadin.annotations.JavaScript;
import com.vaadin.server.AbstractJavaScriptExtension;

@JavaScript("calendar-focus-popup-opener-extension.js")
public class CalendarFocusPopupOpenerExtension extends AbstractJavaScriptExtension {
    public CalendarFocusPopupOpenerExtension() {
        // call the bind function defined in the associated JS
        callFunction("bind");
    }
}

3) 扩展 - js/客户端

window.com_example_calendar_CalendarFocusPopupOpenerExtension = function () {
    this.bind = function () {
        if (document.readyState === "complete") {
            // if executed when document already loaded, just bind
            console.log("Doc already loaded, binding");
            bindToAllDateFields();
        } else {
            // otherwise, bind when finished loading
            console.log("Doc nod loaded, binding later");
            window.onload = function () {
                console.log("Doc finally loaded, binding");
                bindToAllDateFields();
            }
        }
    };

    function bindToAllDateFields() {
        // get all the date fields to assign focus handlers to
        var dateFields = document.getElementsByClassName("v-datefield");
        for (var i = 0; i < dateFields.length; i++) {
            addFocusListeners(dateFields[i]);
        }
    }

    function addFocusListeners(dateField) {
        // when focusing the date field, click the button
        dateField.onfocus = function () {
            dateField.getElementsByTagName("button")[0].click();
        };

        // or when focusing the date field input, click the button
        dateField.getElementsByTagName("input")[0].onfocus = function () {
            dateField.getElementsByTagName("button")[0].click();
        };
    }
};

4) 结果


稍后更新

第二种方法是为您的字段分配一些 ID,然后定期检查以查看所有 ID 何时可见,一旦它们可见,就绑定焦点侦听器。

1) 带有组件的布局

public class MyDateFieldComponent extends HorizontalLayout {
    public MyDateFieldComponent() {
        // basic setup
        DateField fromDateField = new DateField("From", LocalDate.of(2011, Month.FEBRUARY, 6));
        fromDateField.setId("fromDateField"); // use id to bind
        fromDateField.setVisible(false); // initially hide it

        DateField toDateField = new DateField("To", LocalDate.of(2018, Month.FEBRUARY, 6));
        toDateField.setId("toDateField"); // use id to bind
        toDateField.setVisible(false); // initially hide it

        // simulate a delay until the fields are available
        Button showFieldsButton = new Button("Show fields", e -> {
            fromDateField.setVisible(true);
            toDateField.setVisible(true);
        });

        setSpacing(true);
        addComponents(showFieldsButton, fromDateField, toDateField);

        // add the extension
        addExtension(new CalendarFocusPopupOpenerExtension(fromDateField.getId(), toDateField.getId()));
    }
}

2) 扩展 - java/服务器端

@JavaScript("calendar-focus-popup-opener-extension.js")
public class CalendarFocusPopupOpenerExtension extends AbstractJavaScriptExtension {
    public CalendarFocusPopupOpenerExtension(String... idsToBindTo) {
        // send the arguments as an array of strings
        JsonArray arguments = Json.createArray();
        for (int i = 0; i < idsToBindTo.length; i++) {
            arguments.set(i, idsToBindTo[i]);
        }

        // call the bind defined in the associated JS
        callFunction("bind", arguments);
    }
}

3) 扩展 - js/客户端

window.com_example_calendar_CalendarFocusPopupOpenerExtension = function () {
    var timer;

    this.bind = function (idsToBindTo) {
        // check every second to see if the fields are available. interval can be tweaked as required
        timer = setInterval(function () {
            bindWhenFieldsAreAvailable(idsToBindTo);
        }, 1000);
    };

    function bindWhenFieldsAreAvailable(idsToBindTo) {
        console.log("Looking for the following date field ids: [" + idsToBindTo + "]");
        var dateFields = [];
        for (var i = 0; i < idsToBindTo.length; i++) {
            var dateFieldId = idsToBindTo[i];
            var dateField = document.getElementById(dateFieldId);
            if (!dateField) {
                // field not present, wait
                console.log("Date field with id [" + dateFieldId + "] not found, sleeping");
                return;
            } else {
                // field present, add it to the list
                console.log("Date field with id [" + dateFieldId + "] found, adding to binding list");
                dateFields.push(dateField);
            }
        }

        // all fields present and accounted for, bind the listeners!
        clearInterval(timer);
        console.log("All fields available, binding focus listeners");
        bindTo(dateFields);
    }

    function bindTo(dateFields) {
        // assign focus handlers to all date fields
        for (var i = 0; i < dateFields.length; i++) {
            addFocusListeners(dateFields[i]);
        }
    }

    function addFocusListeners(dateField) {
        // when focusing the date field, click the button
        dateField.onfocus = function () {
            dateField.getElementsByTagName("button")[0].click();
        };

        // or when focusing the date field input, click the button
        dateField.getElementsByTagName("input")[0].onfocus = function () {
            dateField.getElementsByTagName("button")[0].click();
        };
    }
};

4) 结果

【讨论】:

  • 看起来很酷,但我的代码有问题:函数bindToAllDateFields 在呈现日期字段之前被调用。页面加载后如何调用此函数?
  • @Everv0id 我将初始函数的名称更改为bind,并添加了一个检查以查看调用时是否已加载文档。如果是,则立即绑定,否则将使用onload 函数进行绑定。您的 UI 可能更复杂,因此它会在稍后完成,但在我的情况下,当它执行时,文档已加载。您可以检查开发者控制台(chrome、firefox 等)查看我添加的日志行(显然您也可以在提交代码之前将其删除,它们仅用于调试目的)。
  • 谢谢你,不幸的是结果是一样的。我尝试调试js代码,发现bind函数调用时document.readyState === "complete"为真,但此时页面上没有.v-datefield元素。在我的页面上,我使用 Grid 和一些时间过滤器,当调用 bind 时,不会呈现任何过滤器和行。
  • @Everv0id 嗯,奇怪,我一定遗漏了一些东西...如果您尝试删除 if 并只使用 window.onload,例如 window.onload = function() {bindToAllDateFields();},会怎样?
  • 另外,我刚刚在 Vaadin github 页面上创建了关于此的功能请求:github.com/vaadin/framework/issues/9528
猜你喜欢
  • 2021-02-11
  • 1970-01-01
  • 1970-01-01
  • 2015-06-26
  • 1970-01-01
  • 1970-01-01
  • 2013-04-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多