仅在单击确定按钮后,如何才能设置元素UI时间选择器的值?

时间:2019-02-22 00:17:16

标签: javascript vue.js element-ui

当前,每次更改小时,分钟或秒时,元素时间选择器都会触发input事件(并且有趣的是-不是mentioned in the docschange事件)。对于我的用例,我需要它来允许用户选择一个值,但实际上并未设置模型(我正在使用v-model,但本质上是触发input使用的v-model事件),直到用户单击ok按钮。

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

是否有一种更干净的方法(理想情况下,是内置于Element中并且不涉及如下所示的hack)?

编辑:似乎我对更改没有被触发是错误的-如下面@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 个答案:

答案 0 :(得分:0)

emits a change event when you click 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>