Итерация по экземплярам

В приложениях с большим количеством числовых полей часто возникает необходимость централизованно обрабатывать все экземпляры AutoNumeric: изменять настройки, получать значения, выполнять синхронизацию, массовое обновление интерфейса или очистку данных. Для решения таких задач библиотека предоставляет механизмы доступа к коллекциям экземпляров и инструменты обхода зарегистрированных объектов.

Итерация по экземплярам позволяет:

  • выполнять массовые операции над группами полей;
  • синхронизировать форматирование;
  • собирать данные формы;
  • применять одинаковые настройки ко всем элементам;
  • обновлять значения после загрузки данных;
  • управлять жизненным циклом множества компонентов.

Коллекция экземпляров AutoNumeric

Каждый объект AutoNumeric представляет собой независимый экземпляр со своим состоянием, настройками и связанным DOM-элементом.

Пример создания нескольких экземпляров:

const price = new AutoNumeric('#price', {
    currencySymbol: '$',
    decimalPlaces: 2
});

const tax = new AutoNumeric('#tax', {
    suffixText: '%',
    decimalPlaces: 1
});

const quantity = new AutoNumeric('#quantity', {
    decimalPlaces: 0
});

При большом количестве элементов хранение ссылок вручную становится неудобным. Обычно экземпляры объединяют в массивы или используют встроенные групповые механизмы библиотеки.


Хранение экземпляров в массиве

Самый простой способ организации итерации — хранение объектов в массиве.

const fields = [
    new AutoNumeric('#price'),
    new AutoNumeric('#discount'),
    new AutoNumeric('#tax'),
    new AutoNumeric('#total')
];

После этого становятся доступны любые стандартные методы перебора JavaScript.


Использование forEach

Метод forEach подходит для последовательной обработки всех экземпляров.

fields.forEach(instance => {
    instance.clear();
});

В данном случае очищаются все поля одновременно.

Пример установки одинакового значения:

fields.forEach(instance => {
    instance.set(1000);
});

Получение числовых значений

Частая задача — извлечение чисел из всех экземпляров.

const values = fields.map(instance => {
    return instance.getNumber();
});

console.log(values);

Результат:

[1000, 250, 20, 1270]

Суммирование значений

Итерация часто используется для вычислений.

const total = fields.reduce((sum, instance) => {
    return sum + Number(instance.getNumber());
}, 0);

console.log(total);

Перебор через for...of

Конструкция for...of особенно удобна при сложной логике.

for (const instance of fields) {
    if (instance.getNumber() < 0) {
        instance.node().classList.add('negative');
    }
}

Использование entries()

Иногда требуется индекс элемента.

for (const [index, instance] of fields.entries()) {
    console.log(index, instance.getFormatted());
}

Массовое изменение настроек

AutoNumeric позволяет изменять конфигурацию после инициализации.

fields.forEach(instance => {
    instance.update({
        digitGroupSeparator: ' ',
        decimalCharacter: ','
    });
});

После выполнения все поля будут использовать одинаковые локализованные настройки.


Изменение валютного символа

fields.forEach(instance => {
    instance.update({
        currencySymbol: '€'
    });
});

Переключение режима отображения

Часто применяется динамическое изменение параметров в зависимости от состояния интерфейса.

function switchToPercentageMode(instances) {
    instances.forEach(instance => {
        instance.update({
            suffixText: '%',
            decimalPlaces: 2
        });
    });
}

Работа с DOM-элементами экземпляров

Каждый экземпляр связан с HTML-узлом.

fields.forEach(instance => {
    const element = instance.node();

    element.style.backgroundColor = '#f5f5f5';
});

Фильтрация экземпляров

Можно обрабатывать только определённые элементы.

const emptyFields = fields.filter(instance => {
    return instance.getNumber() === null;
});

Выборочная очистка

emptyFields.forEach(instance => {
    instance.set(0);
});

Группировка экземпляров по назначению

В крупных приложениях удобно разделять поля по категориям.

const financeFields = [
    new AutoNumeric('#salary'),
    new AutoNumeric('#bonus'),
    new AutoNumeric('#expenses')
];

const percentFields = [
    new AutoNumeric('#tax'),
    new AutoNumeric('#interest')
];

После этого можно применять разные сценарии обработки.


Итерация по NodeList

AutoNumeric часто создаётся динамически для группы элементов.

const elements = document.querySelectorAll('.numeric');

const instances = [];

elements.forEach(element => {
    instances.push(new AutoNumeric(element));
});

Создание экземпляров через map

Более компактный вариант:

const instances = [...document.querySelectorAll('.numeric')]
    .map(element => new AutoNumeric(element));

Итерация после AJAX-загрузки

При динамическом добавлении элементов необходимо повторно создавать экземпляры.

function initializeNumericFields() {
    return [...document.querySelectorAll('.numeric')]
        .map(element => new AutoNumeric(element));
}

Обновление данных во всех экземплярах

function applyData(instances, data) {
    instances.forEach((instance, index) => {
        instance.set(data[index]);
    });
}

Проверка валидности данных

fields.forEach(instance => {
    const value = instance.getNumber();

    if (value > 1000000) {
        console.warn('Превышен лимит');
    }
});

Получение форматированных строк

const formattedValues = fields.map(instance => {
    return instance.getFormatted();
});

Пример результата:

[
    '$1,000.00',
    '$250.00',
    '$20.00'
]

Получение необработанных значений

const rawValues = fields.map(instance => {
    return instance.rawValue;
});

Удаление экземпляров

При уничтожении компонентов полезно централизованно удалять экземпляры.

fields.forEach(instance => {
    instance.remove();
});

После удаления:

  • снимаются обработчики событий;
  • прекращается форматирование;
  • освобождаются внутренние структуры.

Итерация с асинхронными операциями

Иногда данные загружаются с сервера.

async function updateFields(instances) {
    const response = await fetch('/api/prices');
    const data = await response.json();

    instances.forEach((instance, index) => {
        instance.set(data[index]);
    });
}

Синхронизация связанных полей

function syncFields(source, targets) {
    source.node().addEventListener('autoNumeric:rawValueModified', () => {
        const value = source.getNumber();

        targets.forEach(instance => {
            instance.set(value);
        });
    });
}

Итерация внутри форм

Сбор значений формы:

function collectFormData(instances) {
    const result = {};

    instances.forEach(instance => {
        const name = instance.node().name;

        result[name] = instance.getNumber();
    });

    return result;
}

Использование every

Проверка всех значений:

const allPositive = fields.every(instance => {
    return instance.getNumber() >= 0;
});

Использование some

Проверка наличия отрицательных значений:

const hasNegative = fields.some(instance => {
    return instance.getNumber() < 0;
});

Сортировка экземпляров

fields.sort((a, b) => {
    return a.getNumber() - b.getNumber();
});

Итерация с обработкой ошибок

fields.forEach(instance => {
    try {
        console.log(instance.getNumber());
    } catch(error) {
        console.error(error);
    }
});

Работа с динамическими таблицами

AutoNumeric особенно часто используется в табличных интерфейсах.

const rows = [...document.querySelectorAll('.row-price')];

const priceInstances = rows.map(row => {
    return new AutoNumeric(row);
});

Массовая блокировка полей

fields.forEach(instance => {
    instance.node().disabled = true;
});

Массовое включение

fields.forEach(instance => {
    instance.node().disabled = false;
});

Переключение CSS-классов

fields.forEach(instance => {
    instance.node().classList.toggle('highlight');
});

Использование WeakMap для хранения экземпляров

В сложных приложениях экземпляры могут храниться в WeakMap.

const storage = new WeakMap();

document.querySelectorAll('.numeric').forEach(element => {
    const instance = new AutoNumeric(element);

    storage.set(element, instance);
});

Получение экземпляра:

const instance = storage.get(element);

Итерация через Object.values

Если экземпляры организованы как объект:

const instances = {
    salary: new AutoNumeric('#salary'),
    bonus: new AutoNumeric('#bonus'),
    tax: new AutoNumeric('#tax')
};

Перебор:

Object.values(instances).forEach(instance => {
    instance.clear();
});

Перебор с использованием ключей

Object.entries(instances).forEach(([name, instance]) => {
    console.log(name, instance.getFormatted());
});

Создание универсального обработчика

function forEachInstance(instances, callback) {
    instances.forEach(instance => {
        callback(instance);
    });
}

Использование:

forEachInstance(fields, instance => {
    instance.set(0);
});

Массовое применение локализации

fields.forEach(instance => {
    instance.update({
        digitGroupSeparator: '.',
        decimalCharacter: ',',
        currencySymbol: '₸'
    });
});

Итерация при изменении темы интерфейса

function applyDarkTheme(instances) {
    instances.forEach(instance => {
        instance.node().classList.add('dark-input');
    });
}

Массовая валидация диапазонов

fields.forEach(instance => {
    const value = instance.getNumber();

    if (value < 0 || value > 10000) {
        instance.node().classList.add('invalid');
    }
});

Обработка больших коллекций

При работе с сотнями экземпляров важно учитывать производительность.

Проблемы:

  • частые перерисовки DOM;
  • повторные вызовы update;
  • большое количество событий;
  • синхронная обработка крупных массивов.

Оптимизация:

requestAnimationFrame(() => {
    fields.forEach(instance => {
        instance.set(100);
    });
});

Пакетная обработка

function processInChunks(instances, size = 50) {
    for (let i = 0; i < instances.length; i += size) {
        const chunk = instances.slice(i, i + size);

        chunk.forEach(instance => {
            instance.clear();
        });
    }
}

Использование Set для уникальных экземпляров

const instanceSet = new Set();

instanceSet.add(new AutoNumeric('#a'));
instanceSet.add(new AutoNumeric('#b'));

for (const instance of instanceSet) {
    instance.set(1);
}

Синхронизация значений между группами

function copyValues(sourceGroup, targetGroup) {
    sourceGroup.forEach((instance, index) => {
        targetGroup[index].set(
            instance.getNumber()
        );
    });
}

Централизованное управление экземплярами

Пример простого менеджера:

class NumericManager {
    constructor() {
        this.instances = [];
    }

    register(instance) {
        this.instances.push(instance);
    }

    clearAll() {
        this.instances.forEach(instance => {
            instance.clear();
        });
    }

    disableAll() {
        this.instances.forEach(instance => {
            instance.node().disabled = true;
        });
    }

    enableAll() {
        this.instances.forEach(instance => {
            instance.node().disabled = false;
        });
    }

    destroyAll() {
        this.instances.forEach(instance => {
            instance.remove();
        });

        this.instances = [];
    }
}

Использование:

const manager = new NumericManager();

manager.register(new AutoNumeric('#price'));
manager.register(new AutoNumeric('#tax'));

manager.clearAll();