Имитация изменения ввода текста, как если бы пользователь набрал указанную клавишу c для запуска обработчика React onChange? - PullRequest
0 голосов
/ 14 апреля 2020

Я использую обработчик onKeyDown, чтобы пользователь не мог добавить более 1 DECIMAL_SEPARATOR (в данном примере это ".") В поле ввода.

И это работает как Вы можете увидеть из GIF ниже.

Но я хотел бы принять THOUSAND_SEPARATOR (в данном примере это ","), как если бы пользователь набрал DECIMAL_SEPARATOR.

Другими словами, если пользователь нажимает 15, а затем нажимает THOUSAND_SEPARATOR, я хотел бы преобразовать его в DECIMAL_SEPARATOR.

Примечание. Я не хочу заменять THOUSAND_SEPARATOR в строке значения, потому что в некоторый момент в строке могут присутствовать другие THOUSAND_SEPARATOR s, и это будет сложно.

QUESTION

Is is возможно ли мне смоделировать событие keyPress, keyDown, onChange ??? для клавиши DECIMAL_SEPARATOR из моего блока else из приведенного ниже фрагмента?

По сути, я хочу: "Запретить событие по умолчанию для THOUSAND_SEPARATOR (что я уже делаю) и я должен создать событие, которое должно вести себя так же, как если бы пользователь набрал клавишу DECIMAL_SEPARATOR.

Как я могу это сделать?

const DECIMAL_SEPARATOR = ".";
const THOUSAND_SEPARATOR = ",";

function App() {

  const [inputValue, setInputValue] = React.useState("");
  
  function onChange(event) {
    const newValue = event.target.value;
    setInputValue(newValue);
  }

  function onKeyDown(event) {
    console.log("onKeyDown...");
    if (event.key === DECIMAL_SEPARATOR || event.key === THOUSAND_SEPARATOR) {
      const currentValue = event.target.value;
      // PREVENTS USER OF ENTERING MORE THAN ONE DECIMAL_SEPARATOR
      if (currentValue === "" || currentValue.indexOf(DECIMAL_SEPARATOR) >= 0) {
        event.preventDefault();
      }
      else {
        // AT THIS POINT I WOULD LIKE TO CONVERT THE THOUSAND_SEPARATOR INTO DECIMAL_SEPARATOR
      }
    }
  }

  return(
    <input 
      type="text"
      onChange={onChange}
      onKeyDown={onKeyDown}
    />
  );
}

ReactDOM.render(<App/>, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>

Вот что происходит сейчас:

enter image description here

Ответы [ 2 ]

0 голосов
/ 15 апреля 2020

Ну, из этого вопроса:

https://github.com/facebook/react/issues/10135

Кажется, что то, что мне нужно, невозможно. Я смог запустить метод onChange, отправив событие change, выполнив следующее (предложено в выпуске):

function onKeyDown(event) {
    if (event.key === ",") {
      event.preventDefault();
      const previousValue = input_ref.current.value;
      const caretPosition = input_ref.current.selectionStart;
      const point = ".";
      const newValue = [previousValue.slice(0, caretPosition), point, previousValue.slice(caretPosition)].join('');

      input_ref.current.value = newValue;

      const tracker = input_ref.current._valueTracker;

      if (tracker) {
        tracker.setValue(previousValue);
      }

      input_ref.current.dispatchEvent(new Event('change', { bubbles: true }));
    }
  }

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

Итак, я закончил тем, что сделал:

https://codesandbox.io/s/broken-forest-1e3g3

import React, { useState, useRef } from "react";
import "./styles.css";

const DECIMAL_SEPARATOR = ".";
const THOUSAND_SEPARATOR = ",";

export default function App() {
  console.log("Rendering App...");

  const [inputValue, setInputValue] = useState("");
  const input_ref = useRef(null);

  function __setCaretPosition(caretPos) {
    if (input_ref.current.createTextRange) {
      var range = input_ref.current.createTextRange();
      range.move("character", caretPos);
      range.select();
    } else {
      if (input_ref.current.selectionStart) {
        input_ref.current.focus();
        input_ref.current.setSelectionRange(caretPos, caretPos);
      } else {
        input_ref.current.focus();
      }
    }
  }

  function onChange(event, moveCaret = false) {
    console.log("From onChange...");
    const newValue = event.target.value;
    const caretPosition = input_ref.current.selectionStart;

    setInputValue(newValue);

    __setCaretPosition(caretPosition + (moveCaret ? 1 : 0));

    setTimeout(() => {
      __setCaretPosition(caretPosition + (moveCaret ? 1 : 0));
    }, 0);
  }

  function onKeyDown(event) {
    console.log("From onKeyDown...");
    const inputValue = input_ref.current.value;
    const caretPosition = input_ref.current.selectionStart;
    if (event.key === THOUSAND_SEPARATOR) {
      event.preventDefault();
      const fakeEvent = {
        target: {
          value: [
            inputValue.slice(0, caretPosition),
            DECIMAL_SEPARATOR,
            inputValue.slice(caretPosition)
          ].join("")
        }
      };
      const moveCaret = true;
      onChange(fakeEvent, moveCaret);
    }
  }

  return (
    <div className="App">
      <input
        type="text"
        value={inputValue}
        onChange={onChange}
        onKeyDown={onKeyDown}
        ref={input_ref}
      />
    </div>
  );
}

0 голосов
/ 14 апреля 2020

Если вам нужен не валютный формат, вы можете сделать это, например,

function numberFormat(num) {
   return num.toFixed(2).replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,')
}
console.log(numberFormat(5000)); // 5,000.00

, и вы можете решить, хотите ли вы . или ,, изменив $1, или $1. здесь

num.toFixed(2).replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,')

Простой способ сделать валютный формат - использовать JavaScript Интернационализация API, и это динамически c.

Объект Intl.NumberFormat является конструктор для объектов, которые разрешают форматирование чисел с учетом языка.

function App() {
    let [inputValue, setInputValue] = React.useState("");
    function onChange(event) {
        let value = new Intl.NumberFormat("en-US").format((event.target.value).replace(/\D+/g, ""));
        setInputValue(value === '0' ? '' : value);
    }
    return (
        <div className="App">
            <input type="text" onChange={onChange} value={inputValue} />
        </div>
    );
}

замените пустую строку, если не число

(event.target.value).replace(/\D+/g, "")

проверьте рабочий пример здесь

Однако в долларах США.

В разных странах существуют разные соглашения для отображения значений.

JavaScript делает его очень простым с помощью ECMAScript Интернационализация API , сравнительно новый браузерный API, который предоставляет множество функций интернационализации, таких как форматирование даты и времени.

Он очень хорошо поддерживается:

const formatter = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
  minimumFractionDigits: 2
})

formatter.format(1000) // "$1,000.00"
formatter.format(10) // "$10.00"
formatter.format(123233000) // "$123,233,000.00"

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

В этом примере создается средство форматирования чисел для валюты евро для итальянской страны:

const formatter = new Intl.NumberFormat('it-IT', {
  style: 'currency',
  currency: 'EUR'
})
...