Ответ для быстрых фильтров при вызове через Fetch

#
Ответ для быстрых фильтров при вызове через Fetch
Заменяю запрос для подсчета в быстрых фильтрах $ajax на его полный аналог с fetch
   return fetch('./', {
            method:'POST',
            body: data,
            dataType: 'json',
            ContentType: 'application/json'
        })
        .then(response => response.json())
        .then((count) => {
            console.log(count);
        });


Однако результат в этом случае - полный HTML страницы, а не count.
#
Re: Ответ для быстрых фильтров при вызове через Fetch
maria.ats,
вероятно каких-то параметров в запросе не хватает
HostDev.pw - модули для HostCMS, Telegram: @hostdev
#
Re: Ответ для быстрых фильтров при вызове через Fetch
Вот и пытаюсь понять каких. Для эксперимента заменяла в стандартном куске кода только $ajax на fetch - обычно замена работает.

function fastFilter(form)
               {
               this._timerId = false;
               this._form = form;
               
               this.filterChanged = function(obj) {
               if (this._timerId)
               {
               clearTimeout(this._timerId);
               }
               
               var $this = this;
               
               this._timerId = setTimeout(function() {
               $this._loadJson(obj);
               }, 1500);
               
               return this;
               }
               
               this._loadJson = function(obj) {
               var data = this._serializeObject();
               
               return fetch('./', {
               method:'POST',
               body: data,
               dataType: 'json',
               ContentType: 'application/json'
               })
               .then(response => response.json())
               .then((count) => {
               console.log(count);
               });
               }
               
               this._serializeObject = function () {
               var o = {fast_filter: 1};
               var a = this._form.serializeArray();
               $.each(a, function () {
               if (o[this.name] !== undefined) {
               if (!o[this.name].push) {
               o[this.name] = [o[this.name]];
               }
               o[this.name].push(this.value || '');
               } else {
               o[this.name] = this.value || '';
               }
               });
               console.log(o);
               
               return o;
               };
               
               }
#
Re: Ответ для быстрых фильтров при вызове через Fetch
Оставлю целиком класс для фильтров

export class Filter {
    constructor(container) {
        this.container = container;
        this.togglerClass ='js-toggler';
        this.form = this.container.querySelector('form');
        // переменные для построения пути фильтра
        this.path = this.form.action;
        this.priceFrom = null;
        this.priceTo = null;
        this.priceFromOriginal = null;
        this.priceToOriginal = null;
        this.producerField = this.form.querySelector('select[name = producer_id]');
        this.priceFromBox = this.form.querySelector('input[name = price_from]');
        this.sortingSelect = this.form.sorting;
        this.inputs = null;
        this.producerOption = null;
        this.sortingOption = null;
        // переменные для открытия и закрытия, а также отметки подчиненных/управляющих инбоксов
        this.toggler = null;
        this.targetElement = null;
        this.checkbox = null;
        this.parentLabel = null;
        this.childLabels = null;
        this.parentInbox = null;
        this.popup  = null;
        this.timerId = null; // таймер для быстрого фильтра
        this.filterChosenBlock = document.querySelector('.js-filter-chosen');
        this.filterChosen = null; // выбранный фильтр для удаления
        this.text = 'Больше фильтров';
        this.closeClass = 'js-close';
        this.deleteFilter = this.deleteFilter.bind(this);
        this.destroy = this.destroy.bind(this);
        this.init();
        this.setListeners();        
    }

    init() {
        this.inputs = this.form.querySelectorAll('[data-property]:not(div)');
        // проверяем все инпуты и выставляем родительскому лэйблу состояния отмечен, неотмечен, неопределен
        this.inputs
            .forEach(input => {
                if (input.classList.contains('check')) {
                    if (input.checked) {
                        const newElt = this.createElt(input, 'button', 'filters-chosen__item', input.nextElementSibling.innerHTML);
                        this.filterChosenBlock.prepend(newElt);
                    }
                }
            });
    }

    filterApply() {
        if (this.producerField) {
            this.producerOption = this.producerField.option.selected;
            if (parseInt(this.producerOption.value)) this.path += `${this.producerOption.dataset.producer.toLowerCase()}/`;
        }

        let tag_name = null;

        if (this.inputs) {
            this.inputs.forEach(input => {                
                let type = input.type;
                let value = null;
                let setValue = false;                

                if (input.name !== void 0 && input.name.indexOf('_to') !== -1) return;

                switch(type) {
                    case 'checkbox':
                    case'radio':
                    if (input.checked) {
                        value += input.value;
                        setValue = type != 'checkbox' ? true : input.name.indexOf('[]') !== -1;                        
                        }
                    break;
                    case 'option':
                        if (input.selected) {
                            value = input.value;
                            setValue = true;
                        }
                    break;
                    case 'text':
                    case 'hidden':
                        value = input.value;
                        setValue = true;
                    break;
                }

                if (value && input.dataset.property !== tag_name) {
                    tag_name = input.dataset.property.toLowerCase();

                    if (input.name !== void 0 && input.name.indexOf('_from') !== -1) {
                        this.path +='';
                    } else {
                        this.path += `${tag_name}/`;
                    }
                }

                if (setValue && value) {
                    if (input.name !== void 0 && input.name.indexOf('_from') !== -1) {
                        const value2 = input.closest('.filter__fieldset-inside').querySelector('input[name $= "_to"]').value;
                        this.path += `${tag_name}-${value}-${value2}/`;
                    } else {
                        this.path += input.dataset.value !== ''
                            ?  `${input.dataset.value.toString().toLowerCase()}/`
                            : `${value.toString().toLowerCase()}/`
                    }
                }
            });
        }

        if (this.priceFromBox) {
            this.priceFrom = this.priceFromBox.value;
            this.priceTo = this.form.querySelector('input[name = price_to]').value;
            this.priceFromOriginal = this.form.querySelector('input[name = price_from_original]').value
            this.priceToOriginal = this.form.querySelector('input[name = price_to_original]').value
            if (this.priceFrom && this.priceTo
                && (this.priceFroOriginalm !== this.priceFrom || this.priceToOriginal !== this.pticeTo)) {            
                this.path += `price-${priceFrom}-${priceTo}/`;
            }            
        }  
        
        if (this.sortingSelect) {
            this.sortingOption = this.sortingSelect.options.selectedIndex;
            if (this.sortingOption !== 0) {
                const sortingOptionValue = this.sortingSelect.options[this.sortingOption].value;
                this.path += `?sorting=${sortingOptionValue.toLowerCase()}`;
            }
        }

        window.location.href = this.path;
    }

    setListeners() {
        this.container.addEventListener('click', (evt) => {
            this.toggler = evt.target.closest(`.${this.togglerClass}`);
            if (this.toggler) {
                evt.preventDefault();
                evt.stopImmediatePropagation();
                this.openHandler();
                return;
            }

            this.checkbox = evt.target.closest(`.check`);

            if (this.checkbox) {
                this.__fastFilter(this.checkbox);
                this.changeState(this.checkbox);                
            }

            if (evt.target.classList.contains('filter__btn')) {
                evt.preventDefault();
                this.filterApply();
            }
        });

        if (this.filterChosenBlock) {
            this.filterChosenBlock.addEventListener('click', this.deleteFilter);
        }

        this.form.addEventListener('submit',(evt) => {
            evt.preventDefault();
            this.filterApply();
        })
    }

    // определяем состояние родительского эл-та
    determineState(elt) {        
        this.parentContainer = elt.closest('.filter__container--parent');
        if (!this.parentContainer) return; // если нет родительского - возвращаемся
        this.parentLabel = this.parentContainer.querySelector('.filter__label--parent'); // определяем управляющий лэйбел
        this.parentInbox = this.parentLabel.querySelector('input'); // определяем управляющий инпут
        this.getChildren(this.parentContainer); //получаем подчиненные инпуты

        this.checkParentInput();  // отправляем на проверку для установления состояния      
    }

    // получаем подсиненные инпуты
    getChildren(elt) {
        this.childLabels = elt.querySelector('div').querySelectorAll('.check');        
    }

    // изменение состояния управляющего инпута при клике на дочерний или дочернего при щелчке на подчиненный
    changeState(elt) {
        this.parentContainer = elt.closest('.filter__container--parent'); // получаем ближайший управляющий контейнер
        if (!this.parentContainer) return; //если его нет, завершаем цикл
        this.parentLabel = this.parentContainer.querySelector('.filter__label'); // получаем управляющий  лэйбел

        this.parentInbox = this.parentLabel.querySelector('.check'); // получаем управляющий инпут

        this.getChildren(this.parentContainer); // собираем дочерние инпуты

        // если целевой элт является уравляющим, проставляем всем подчиненным ставим им то же состояние, что и родительским
        if (this.parentInbox === elt) {
            const labelState = this.parentInbox.checked;
            [...this.childLabels].forEach(label => {
                label.indeterminate = 0;
                label.checked = labelState;
            });
            return;
        }

        // если это один из подчиненных эл-тов, перепроверяем состояние управляющий эл-т
        this.determineState(elt);
    }

    // проверка управляющего инпута
    checkParentInput() {
        // считаем все отмеченные подчиненные инпуты
        const checkedChildren = this.parentContainer.querySelector('div').querySelectorAll('.check:checked');
        const checkedChildrenCount = checkedChildren.length;

        // если все отмечены, отмечаем родительский
        if (checkedChildrenCount === this.childLabels.length) {
            this.parentInbox.indeterminate = 0;
            this.parentInbox.checked = 'checked';
            if (this.parentContainer.classList.contains('closed')) {
                this.parentContainer.classList.toggle('closed');
            }
            return;
        }

        // если со всех снята отметка, снимаем отметку с родительского
        if (checkedChildrenCount === 0) {
            this.parentInbox.indeterminate = 0;
            this.parentInbox.checked = 0;
            return;
        }

        // если отмечены не все, ставим indeterminate
        this.parentInbox.indeterminate = true;
        if (this.parentContainer.classList.contains('closed')) {
            this.parentContainer.classList.toggle('closed');
        }
    }

    __fastFilter(obj) {
        if (this.timerId) {
            clearTimeout(this.timerId);
        }
        
        this.timerId = setTimeout(() => {this._loadJson(obj)}, 500);
    }

    _loadJson(obj) {
        const data = this._serializeObject();
        return fetch('./', {
            method:'POST',
            body: data,
            dataType: 'json',
            headers: {
                "Accept": "application/json, text/javascript, */*; q=0.01",
                'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
                'X-Requested-With': 'XMLHttpRequest',
            },
        })
        .then(response => response.json())
        .then(result => {
            const text = `<div>Найдено ${result.count} элементов(та)</div><button class="btn filter__btn">Применить</button>`;
            this.popup = this.createElt(obj, 'div', 'filter__popup',  text);
            obj.closest('label').appendChild(this.popup);        
            setTimeout(() => this.destroy(this.popup), 3500);            
        });
    }

    // собираем результаты формы в Json
    _serializeObject() {
        const formData = new FormData(this.form);
        let filter = 'fast_filter=1&';

        formData.forEach((value, key) => {
             filter +=  `${key}=${value}&`;
        });
        
        return  encodeURI(filter.slice(0, -1));
    }

    deleteFilter(evt) {
        if (!evt.target.classList.contains('filters-chosen__item')) return;
        this.filterChosen = evt.target;
        [...this.inputs].find(item => item.dataset.value === this.filterChosen.dataset.id).checked = false;
        this.filterApply();
    }

    createElt(elt, eltType, className, text) {
        const newElt = document.createElement(eltType);
        newElt.classList.add(className);
        newElt.tabindex = 0;        
        newElt.dataset.id = elt.dataset.value;
        newElt.innerHTML = text;        
        return newElt;
    }

    openHandler() {
        this.changeText();
        this.targetElement = this.toggler.parentNode;
        this.targetElement.classList.toggle('closed');
    }

    changeText() {
        if (!this.toggler.dataset.text) return;
        this.toggler.dataset.text === 'more' ?  this.text = 'Отметить все' : this.text = 'снфть все отметки';
        this.toggler.innerHTML = '';
        this.toggler.innerHTML = this.text;
        this.toggler.dataset.text === 'more' ? this.toggler.dataset.text = 'less' : this.toggler.dataset.text = 'more';
    }

    destroy(elt) {
        elt.parentNode.removeChild(elt);
        elt = null;
    }
}

Модератор
#
Re: Ответ для быстрых фильтров при вызове через Fetch
maria.ats,
верно ли, что вопрос решен?
Авторизация