Как сделать значение выбора времени пользовательского интерфейса элемента только после нажатия кнопки ОК?

В настоящее время элемент Timepicker запускает input событие (и, что интересно, - не change событие, как указано в документации) каждый раз, когда меняются часы, минуты или секунды. Для моего случая использования мне нужно, чтобы пользователь мог выбрать значение, но на самом деле не установить модель (я использую v-модель, но, по сути, запускаю input событие, которое v-model использует), пока пользователь не нажмет ok кнопка.

Я думаю, что компонент-обертка, который управляет государством внутри, является одним из способов сделать это. (Пример реализации ниже)

Есть ли более чистый способ (в идеале, встроенный в Element, без использования хаков, как показано ниже), чтобы сделать это?

РЕДАКТИРОВАТЬ: Похоже, что я был неправ по поводу того, что изменение не было запущено - как объясняется в ответе @Roy J ниже, оно срабатывает, когда нажимается кнопка, но также важно, когда пользователь просто щелкает из таймера, когда он находится в фокусе, который это не то поведение, которое требуется - модель должна обновляться только при нажатии кнопки ОК.

<template>
  <el-time-picker
    v-bind="_passthrough"
    :value="_displayValue"
    @blur="handleBlur"
    @focus="handleFocus"
    @input="handleInput"
  >
  </el-time-picker>
</template>

<script>
  import { TimePicker } from "element-ui";

  /**
   * A list of props accepted by the element time picker component
   * @private
   */
  const _elTimePickerProps = [
    "isRange",
    "readonly",
    "disabled",
    "editable",
    "clearable",
    "size",
    "placeholder",
    "startPlaceholder",
    "endPlaceholder",
    "arrowControl",
    "align",
    "popperClass",
    "pickerOptions",
    "rangeSeparator",
    "defaultValue",
    "valueFormat",
    "name",
    "unlinkPanels",
    "prefixIcon",
    "clearIcon",
    "value"
  ];

  /**
   * A wrapper on the element time picker to trigger the 'input' event only when the 'ok' button is clicked - lazily.
   * The actual element timepicker fires the input event every time an internal control is changed which is
   * undesirable in some cases.
   */
  export default {
    name: "LazyTimePicker",
    props: [..._elTimePickerProps], // Accept the same props as element time picker
    components: {
      ElTimePicker: TimePicker
    },
    data() {
      return {
        /**
         * Shadow to the value prop - used to update the value while the user is selecting without affecting the
         * globally bound value
         */
        currentValue: "",
        /**
         * Tracks if the element currently has focus
         */
        hasFocus: false
      };
    },
    methods: {
      handleInput(value) {
        // Update the internal value every time the value is updated
        this.currentValue = value;
      },
      handleConfirm() {
        // Confirm button was clicked

        // Emit input event with the current value - plays nicely with v-model
        this.$emit("input", this.currentValue);

        // Remove the event listener on the confirm button
        this.$confirmButton.removeEventListener("click", this.handleConfirm);

        // Set the instance ref to the confirm button to undefined
        this.$confirmButton = undefined;
      },
      handleFocus() {
        // The time picker has gained focus, the dialogue will be open on the next tick
        // May be called multiple time (for example when switching between hours, minutes and seconds,
        // each switch triggers a focus event

        // Update focus state
        this.hasFocus = true;

        // Check if the one time setup is complete (indicated by the availability of the button ref on the
        // instance)
        if (this.$confirmButton) {
          // One time setup is complete, return early
          return;
        }
        // Initialise the instance's currentValue to the value received via props
        this.currentValue = this.value;

        // Wait until the time picker dialogue is open on the next tick as the confirm button will be on
        // the DOM only then
        this.$nextTick(() => {
          // Get a ref to the confirm button
          const $confirmButton = document.querySelector(
            ".el-time-range-picker button.el-time-panel__btn.confirm"
          );

          // If the ref is available
          if ($confirmButton) {
            // Register click handler on the `ok` button
            $confirmButton.addEventListener("click", this.handleConfirm);

            // Keep a ref to the button for future use - also doubles as an indicator that the one time
            // setup that is done every time this component is opened is complete
            this.$confirmButton = $confirmButton;
          }
        });
      },
      handleBlur() {
        // The time picker has lost focus, the dialogue will be closed on the next tick
        this.hasFocus = false;

        this.$nextTick(() => {
          // Clean up the confirm button and it's event listener in case the user clicked out or pressed
          // cancel without pressing okay
          if (this.$confirmButton) {
            // Remove the event listener on the confirm button
            //Removing the listener here will prevent the input event from being emitted - does the listener get cleaned up?
            //this.$confirmButton.removeEventListener('click', this.handleConfirm);

            // Set the instance ref to the confirm button to undefined
            this.$confirmButton = undefined;
          }
        });
      }
    },
    computed: {
      /**
       * Collect all props related to the actual element time picker to be `v-bind`ed to it in one shot
       * @returns {Object} Element time picker props
       * @private
       */
      _passthrough() {
        const self = this;
        return _elTimePickerProps.reduce(
          (acc, key) => ({ ...acc, [key]: self[key] }),
          {}
        );
      },
      /**
       * The value to be displayed. When the element is not in focus (the dialogue is closed) the value in
       * the inputs should reflect the value bound to the time picker. When the element is in focus, the dialogue
       * will be open and the user will be in the process ofmaking their new selection. At this time, the inputs
       * should not show the value as it is currently being selected
       * @returns {string}
       * @private
       */
      _displayValue() {
        return this.hasFocus && this.currentValue
          ? this.currentValue
          : this.value;
      }
    }
  };
</script>

1 ответ

Испускает change событие при нажатии ok, Таким образом, вы можете положить change обработчик, чтобы установить значение. Во фрагменте ниже value обновления по вашему выбору, но value2 только обновления, когда вы нажимаете ok,

Что интересно, v-model.lazy не изменяет поведение, чтобы задержать изменение значения после ok, Кажется, не имеет значения вообще.

new Vue({
  el: '#app',
  data() {
    return {
      value1: '',
      value2: '',
    };
  },
  methods: {
    changed(nv) {
      this.value2 = nv;
    }
  }
});
<link href="//unpkg.com/element-ui@2.0.4/lib/theme-chalk/index.css" rel="stylesheet" />
<script src="//unpkg.com/vue/dist/vue.js"></script>
<script src="//unpkg.com/element-ui@2.0.4/lib/index.js"></script>
<div id="app">
  <div class="block">
    <el-time-picker v-model.lazy="value1" @change="changed" type="date" format="HH:mm:ss A">
    </el-time-picker>
    {{value1}}
  </div>
  {{value2}}
</div>

Другие вопросы по тегам