Ваадин: открытый календарь в фокусе поля для поля даты

Виджеты Vaadin просты и классны! Но они также плохо настраиваются. Мне нужен виджет DateField, чтобы открывать календарь на событии фокуса. Я не нашел этой функции в официальной документации Vaadin. Я нашел здесь сторонний виджет, но он скомпилирован для Vaadin 7.7, и я использую последнюю версию Ваадин (8.0.6). Также у него есть зависимость от Joda-time 2.1, что крайне нежелательно в моем проекте. Итак, есть ли простой способ настроить стандартный виджет vaadin DateField, чтобы открыть его календарь в фокусе поля, или мне нужно написать свой собственный компонент для этого? Любая помощь приветствуется.


person Everv0id    schedule 12.06.2017    source источник
comment
Не совсем уверен, что вы имеете в виду, возможно, вы могли бы поделиться sscce с некоторыми подробностями? Например, используя Vaadin 8.0.6, если я создаю DateField с датой где-то в прошлом (например, addComponent(new DateField("Event date", LocalDate.of(2011, Month.FEBRUARY, 6)))) и щелкаю его значок календаря, он всплывает с этой конкретной страницей и датой ...   -  person Morfic    schedule 12.06.2017
comment
@Morfic Я хочу, чтобы всплывающее окно календаря открывалось не только нажатием кнопки календаря, но и путем выделения самого поля.   -  person Everv0id    schedule 12.06.2017
comment
Теперь стало яснее, изначально я неправильно понял, что вы хотели открыть всплывающее окно с фокусом на выбранной дате события. К сожалению, пока нет простого способа программно открывать эти всплывающие окна, например: выбор даты в календаре, редактор сетки, комбинированные элементы. Потенциально вы можете добавить расширение javascript, чтобы щелкнуть кнопка в фокусе. Я попытаюсь завершить простой пример, чтобы вы начали ...   -  person Morfic    schedule 12.06.2017


Ответы (1)


Как я уже говорил в своем комментарии, насколько мне известно, в настоящее время фреймворк не предлагает неявного способа программного открытия всплывающего окна календаря. То же самое и с некоторыми другими компонентами, такими как редактор сетки или список комбинированных элементов.

Я могу придумать одно быстрое решение - добавить расширение javascript., который регистрирует слушателей фокуса для всех полей даты и нажимает кнопку, когда поле даты сфокусировано. Пожалуйста, найдите ниже образец.

P.S. Если вам нужно применить это только к некоторым полям даты, вы можете добавить идентификаторы и передать их в 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) Результат  В фокусе открыто всплывающее окно календаря


ПОЗДНЕЕ ОБНОВЛЕНИЕ

Второй подход может заключаться в назначении некоторых идентификаторов вашим полям, а затем периодической проверки, чтобы видеть, когда все они видны, и, как только они становятся, привязать слушателей фокуса.

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) Результат  В фокусе открыто всплывающее окно календаря

person Morfic    schedule 12.06.2017
comment
Выглядит круто, но у меня проблема с вашим кодом: функция bindToAllDateFields вызывается до отображения поля даты. Как я могу вызвать эту функцию после загрузки страницы? - person Everv0id; 12.06.2017
comment
@ Everv0id Я изменил имя начальной функции на bind и добавил проверку, чтобы увидеть, загружен ли уже документ при его вызове. Если это так, то он будет привязан немедленно, в противном случае он будет использовать onload функцию для привязки. Ваш пользовательский интерфейс, вероятно, более сложен, поэтому он будет завершен позже, но в моем случае, когда он был выполнен, документ был загружен. Вы можете проверить консоль разработчика (chrome, firefox и т. Д.), Поэтому просмотрите добавленные мной строки журнала (очевидно, вы также можете удалить их перед фиксацией кода, они предназначены только для целей отладки). - person Morfic; 12.06.2017
comment
спасибо, к сожалению результат такой же. Я попытался отладить код js и обнаружил, что document.readyState === "complete" истинно, когда вызывается функция bind, но в данный момент на странице нет .v-datefield элементов. На своей странице я использую сетку с некоторыми временными фильтрами, и когда вызывается bind, ни один из фильтров и строк не отображается. - person Everv0id; 13.06.2017
comment
@ Everv0id хм, странно, мне что-то не хватает ... что, если вы попытаетесь удалить if и просто использовать window.onload, например window.onload = function() {bindToAllDateFields();} - person Morfic; 13.06.2017
comment
Кроме того, я только что создал запрос функции об этом на странице Github Vaadin: github.com/vaadin/ framework / issues / 9528 - person Everv0id; 13.06.2017
comment
к сожалению, это тоже не помогает. Похоже, что содержимое сетки (и даже фильтры в заголовке) загружаются асинхронно после загрузки документа. Когда вызывается bind, документ уже имеет состояние complete, и назначение window.onload не дает никакого эффекта. Грустный :( - person Everv0id; 13.06.2017
comment
@ Everv0id хм, если вы загружаете некоторые данные в фоновом потоке, возможно, вместо вызова bind в конструкторе CalendarFocusPopupOpenerExtension вы можете переместить его в метод и вызвать его, когда загрузка будет завершена. Учитывая вашу ситуацию, возможно, вы также можете поделиться sscce, чтобы легко отладить свой сценарий. - person Morfic; 13.06.2017
comment
как я могу отследить, что загрузка завершена? Не обнаружил никаких признаков этого на Grid странице в документации Vaadin. - person Everv0id; 13.06.2017
comment
@ Everv0id Я подумывал вызвать его после вызова myService.loadData(myFilter) или чего-то еще, что загружает ваши данные. Тем не менее, одностраничные приложения затрудняют работу с windows.onload (или аналогичными проверками), и пока я не знаю ни одного события, инициированного Vaadin (есть issue, открытый для этого), поэтому мы пытаемся решить эту проблему. Вот почему я просил sscce, чтобы легко воспроизвести его и выяснить, есть ли какие-либо решения, такие как тайм-аут или проверка наличия final element в dom - person Morfic; 20.06.2017
comment
@ Everv0id Я добавил второй возможный обходной путь, основанный на периодической проверке доступности всех ожидаемых полей (идентифицируемых их идентификаторами) и привязке слушателей фокуса, как только они появятся. Если и это не сработает, я не могу думать ни о чем другом, не имея sscce, с которым можно было бы работать. - person Morfic; 21.06.2017