首页 > 解决方案 > 如何仅在单击确定按钮后才使元素 UI 时间选择器设置值?

问题描述

目前,每次更改小时、分钟或秒时,元素时间选择器都会触发input事件(有趣的是,不是文档中提到change的事件)。对于我的用例,我需要它来允许用户选择一个值但不实际设置模型(我使用的是 v-model 但本质上是触发一个使用的事件),直到用户单击按钮。inputv-modelok

我在想一个在内部管理状态的包装器组件是一种方法。(下面的示例实现)

有没有更干净的方法(理想情况下,内置在 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>

标签: javascriptvue.jselement-ui

解决方案


当您单击时会发出一个changeok事件。因此,您可以在其上放置一个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>


推荐阅读