Интеграция AutoNumeric со сторонними библиотеками часто требует учета того, как различные инструменты управления формами, компонентные фреймворки и утилиты работы с DOM обрабатывают ввод числовых значений, синхронизацию состояния и форматирование отображаемых данных.
AutoNumeric работает напрямую с DOM-элементами input,
модифицируя отображаемое значение и одновременно сохраняя числовую
модель. При подключении к сторонним библиотекам важно учитывать два слоя
данных:
Ключевой момент интеграции заключается в предотвращении конфликтов между:
React управляет состоянием через controlled components, что может
конфликтовать с прямым DOM-манипулированием AutoNumeric. Поэтому
основной подход — изоляция DOM-узла через ref.
import React, { useEffect, useRef } from "react";
import AutoNumeric from "autonumeric";
export default function NumericInput({ value, onChange }) {
const inputRef = useRef(null);
const anInstance = useRef(null);
useEffect(() => {
if (inputRef.current) {
anInstance.current = new AutoNumeric(inputRef.current, value, {
decimalPlaces: 2,
digitGroupSeparator: " ",
decimalCharacter: ".",
});
anInstance.current.set(value);
}
return () => {
if (anInstance.current) {
anInstance.current.remove();
}
};
}, []);
useEffect(() => {
if (anInstance.current) {
anInstance.current.set(value);
}
}, [value]);
const handleChange = () => {
if (anInstance.current) {
onChange(anInstance.current.getNumber());
}
};
return <input ref={inputRef} onIn put={handleChange} />;
}
Ключевые моменты интеграции с React:
ref вместо controlled
value;onInput, а не
onChange;set().При работе с React Hook Form важно учитывать, что библиотека ожидает “чистые” значения, тогда как AutoNumeric оперирует форматированными строками.
import { useForm, Controller } from "react-hook-form";
import AutoNumeric from "autonumeric";
import React, { useRef, useEffect } from "react";
function NumericField({ control, name }) {
const inputRef = useRef(null);
const anRef = useRef(null);
useEffect(() => {
if (inputRef.current) {
anRef.current = new AutoNumeric(inputRef.current, null, {
decimalPlaces: 2,
});
}
return () => anRef.current?.remove();
}, []);
return (
<Controller
control={control}
name={name}
render={({ field }) => (
<input
ref={inputRef}
onIn put={() => field.onChange(anRef.current.getNumber())}
defaultValue={field.value}
/>
)}
/>
);
}
Особенности:
Controller используется вместо
register;getNumber();Vue 3 использует реактивную модель, поэтому интеграция строится через
onMounted и watch.
<template>
<input ref="inputRef" />
</template>
<script>
import AutoNumeric from "autonumeric";
import { ref, onMounted, watch } from "vue";
export default {
props: ["modelValue"],
emits: ["update:modelValue"],
setup(props, { emit }) {
const inputRef = ref(null);
let anInstance = null;
onMounted(() => {
anInstance = new AutoNumeric(inputRef.value, props.modelValue, {
decimalPlaces: 2,
});
inputRef.value.addEventListener("input", () => {
emit("update:modelValue", anInstance.getNumber());
});
});
watch(
() => props.modelValue,
(val) => {
if (anInstance) {
anInstance.set(val);
}
}
);
return { inputRef };
},
};
</script>
Основные принципы:
v-model;watch;value input.Angular требует работы через ElementRef и жизненный цикл
директив.
import { Directive, ElementRef, Input, OnInit, OnDestroy } from "@angular/core";
import AutoNumeric from "autonumeric";
@Directive({
selector: "[appNumeric]",
})
export class NumericDirective implements OnInit, OnDestroy {
@Input() options: any;
private anInstance: any;
constructor(private el: ElementRef) {}
ngOnInit() {
this.anInstance = new AutoNumeric(this.el.nativeElement, null, {
decimalPlaces: 2,
...this.options,
});
this.el.nativeElement.addEventListener("input", () => {
const value = this.anInstance.getNumber();
this.el.nativeElement.dispatchEvent(
new CustomEvent("numericChange", { detail: value })
);
});
}
ngOnDestroy() {
this.anInstance?.remove();
}
}
Особенности Angular-интеграции:
ngOnDestroy.AutoNumeric не должен участвовать в передаче данных на сервер, так как сервер ожидает “сырое” число.
import axios from "axios";
function submitForm(anInstance) {
const payload = {
amount: anInstance.getNumber(),
};
return axios.post("/api/payment", payload);
}
Критически важно:
getFormatted();getNumber().При использовании схем валидации AutoNumeric выступает как слой представления, а не данных.
import * as yup from "yup";
const schema = yup.object({
amount: yup
.number()
.typeError("Должно быть числом")
.min(0, "Минимум 0")
.required(),
});
В связке с AutoNumeric:
getNumber();При совместном использовании с input-mask библиотеками возникает
конфликт управления input.value. Поэтому применяется
правило приоритета:
AutoNumeric может быть настроен динамически в зависимости от локали приложения.
function createNumeric(input, locale) {
const config = {
decimalCharacter: locale === "de-DE" ? "," : ".",
digitGroupSeparator: locale === "de-DE" ? "." : ",",
};
return new AutoNumeric(input, null, config);
}
При смене языка:
setOptions().Состояние хранилища всегда должно содержать числовое значение.
dispatch({
type: "SET_AMOUNT",
payload: anInstance.getNumber(),
});
Ошибочная практика:
При использовании Material UI, Ant Design или аналогов необходимо
учитывать, что многие компоненты проксируют input.
Пример подхода:
input через
inputRef;Конфликт controlled/uncontrolled состояния
Прыгающий курсор
Несоответствие типов
getNumber().Дублирование событий
На практике наиболее устойчивой является модель разделения:
Такое разделение исключает зависимость бизнес-логики от представления и снижает вероятность рассинхронизации данных при масштабировании приложения.