Угловой: Случайная ошибка «Не удается найти элемент управления с именем» Из-за асинхронных данных

#javascript #angular #typescript

Вопрос:

Я получаю ошибки в форме. Ошибки случайны, иногда страница хорошо отображается, но иногда что-то не отображается. Я купил угловой шаблон и не могу его исправить здесь. Я думаю, что мне нужно это сделать.фильтры = фильтры после formbuilder, но я не знаю, как это сделать. Потому что html использует этот.фильтры и не может найти что-то в форме.

компонент.ts:

 import { Component, Inject, Input, OnDestroy, OnInit, PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
import { DirectionService } from '../../../shared/services/direction.service';
import { FormBuilder, FormGroup } from '@angular/forms';
import {
    ColorFilter,
    ColorFilterItem,
    Filter,
    SerializedFilterValues,
    CheckFilter,
    FilterItem, RadioFilter
} from '../../../shared/interfaces/filter';
import { RootService } from '../../../shared/services/root.service';
import { Subject } from 'rxjs';
import { PageCategoryService } from '../../shop/services/page-category.service';
import { map, takeUntil } from 'rxjs/operators';  
  
interface FormFilterValues {
        [filterSlug: string]: [number, number] | {[itemSlug: string]: boolean} | string;
    }
    
    @Component({
        selector: 'app-widget-filters',
        templateUrl: './widget-filters.component.html',
        styleUrls: ['./widget-filters.component.scss']
    })
    export class WidgetFiltersComponent implements OnInit, OnDestroy {
        @Input() offcanvas: 'always'|'mobile' = 'mobile';
    
        destroy$: Subject<void> = new Subject<void>();
    
        filters: Filter[];
        filtersForm: FormGroup;
        isPlatformBrowser = isPlatformBrowser(this.platformId);
        rightToLeft = false;
    
        constructor(
            @Inject(PLATFORM_ID) private platformId: any,
            private direction: DirectionService,
            private fb: FormBuilder,
            public root: RootService,
            public pageCategory: PageCategoryService,
        ) {
            this.rightToLeft = this.direction.isRTL();
        }
    
        ngOnInit(): void {
            this.pageCategory.list$.pipe(
                map(x => x.filters),
                takeUntil(this.destroy$),
            ).subscribe(filters => {
                this.filters = filters;
                this.filtersForm = this.makeFiltersForm(filters);
    
                this.filtersForm.valueChanges.subscribe(formValues => {
                    this.pageCategory.updateOptions({
                        filterValues: this.convertFormToFilterValues(filters, formValues)
                    });
                });
            });
        }
    
        ngOnDestroy(): void {
            this.destroy$.next();
            this.destroy$.complete();
        }
    
        trackBySlug(index: number, item: {slug: string}): any {
            return item.slug;
        }
    
        makeFiltersForm(filters: Filter[]): FormGroup {
            const filtersFromGroup = {};
    
            filters.forEach(filter => {
                switch (filter.type) {
                    case 'range':
                    case 'radio':
                        filtersFromGroup[filter.slug] = this.fb.control(filter.value);
                        break;
                    case 'check':
                    case 'color':
                        filtersFromGroup[filter.slug] = this.makeListFilterForm(filter);
                        break;
                }
            });
    
            return this.fb.group(filtersFromGroup);
        }
    
        makeListFilterForm(filter: CheckFilter|ColorFilter): FormGroup {
            const group = {};
    
            filter.items.forEach(item => {
                const control = this.fb.control(filter.value.includes(item.slug));
    
                // A timeout is needed because sometimes a state change is ignored if performed immediately.
                setTimeout(() => {
                    if (this.isItemDisabled(filter, item)) {
                        control.disable({emitEvent: false});
                    } else {
                        control.enable({emitEvent: false});
                    }
                }, 0);
    
                group[item.slug] = control;
            });
    
            return this.fb.group(group);
        }
    
        isItemDisabled(filter: CheckFilter|RadioFilter|ColorFilter, item: FilterItem|ColorFilterItem): boolean {
            return item.count === 0 amp;amp; (filter.type === 'radio' || !filter.value.includes(item.slug));
        }
    
        convertFormToFilterValues(filters: Filter[], formValues: FormFilterValues): SerializedFilterValues {
            const filterValues: SerializedFilterValues = {};
    
            filters.forEach(filter => {
                const formValue = formValues[filter.slug];
    
                switch (filter.type) {
                    case 'range':
                        if (formValue amp;amp; (formValue[0] !== filter.min || formValue[1] !== filter.max)) {
                            filterValues[filter.slug] = `${formValue[0]}-${formValue[1]}`;
                        }
                        break;
                    case 'check':
                    case 'color':
                        const filterFormValues = formValue as object || {};
    
                        // Reactive forms do not add a values of disabled checkboxes.
                        // This code will add them manually.
                        filter.value.forEach(filterValue => {
                            if (!(filterValue in filterFormValues)) {
                                filterFormValues[filterValue] = true;
                            }
                        });
    
                        const values = Object.keys(filterFormValues).filter(x => filterFormValues[x]);
    
                        if (values.length > 0) {
                            filterValues[filter.slug] = values.join(',');
                        }
                        break;
                    case 'radio':
                        if (formValue !== filter.items[0].slug) {
                            filterValues[filter.slug] = formValue as string;
                        }
    
                        break;
                }
            });
    
            return filterValues;
        }
    
        reset(): void {
            const formValues = {};
    
            this.filters.forEach(filter => {
                switch (filter.type) {
                    case 'range':
                        formValues[filter.slug] = [filter.min, filter.max];
                        break;
                    case 'check':
                    case 'color':
                        formValues[filter.slug] = {};
    
                        filter.items.forEach(item => {
                            formValues[filter.slug][item.slug] = false;
                        });
                        break;
                    case 'radio':
                        formValues[filter.slug] = filter.items[0].slug;
                        break;
                }
            });
    
            this.filtersForm.setValue(formValues);
        }
    }
 

component.html:

     <div class="widget-filters widget" [ngClass]="{
    'widget-filters--offcanvas--always': offcanvas === 'always',
    'widget-filters--offcanvas--mobile': offcanvas === 'mobile'
}" appCollapse>
    <h4 class="widget-filters__title widget__title">Filters</h4>

    <div class="widget-filters__list" [formGroup]="filtersForm" *ngIf="filtersForm">
        <div *ngFor="let filter of filters; trackBy: trackBySlug" class="widget-filters__item">
            <div class="filter filter--opened" appCollapseItem="filter--opened" #collapse="appCollapseItem">
                <button type="button" class="filter__title" (click)="collapse.toggle()">
                    {{ filter.name }}
                    <app-icon class="filter__arrow" name="arrow-rounded-down-12x7" size="12x7"></app-icon>
                </button>
                <div class="filter__body" appCollapseContent>
                    <div class="filter__container">
                        <div *ngIf="filter.type === 'categories'" class="filter-categories" [ngClass]="{'filter-categories--root': filter.root}">
                            <ul class="filter-categories__list">
                                <li *ngIf="!filter.root" class="filter-categories__item filter-categories__item--parent">
                                    <app-icon class="filter-categories__arrow"  name="arrow-rounded-left-6x9" size="6x9"></app-icon>
                                    <a [routerLink]="this.root.shop()">All Products</a>
                                </li>
                                <li *ngFor="let item of filter.items; trackBy: trackBySlug" class="filter-categories__item filter-categories__item--{{ item.type }}">
                                    <app-icon *ngIf="item.type == 'parent'" class="filter-categories__arrow"  name="arrow-rounded-left-6x9" size="6x9"></app-icon>
                                    <a [routerLink]="this.root.category(item.category)">{{ item.name }}</a>
                                    <div class="filter-categories__counter" *ngIf="item.type === 'child'">{{ item.count }}</div>
                                </li>
                            </ul>
                        </div>

                        <div *ngIf="filter.type === 'range' amp;amp; isPlatformBrowser" class="filter-price">
                            <div class="filter-price__slider">
                                <div class="ngx-slider-custom">
                                    <ngx-slider
                                        [formControlName]="filter.slug"
                                        [options]="{
                                            animate: false,
                                            mouseEventsInterval: 10,
                                            rightToLeft: rightToLeft,
                                            floor: filter.min,
                                            ceil: filter.max,
                                            step: 1
                                        }"
                                        #slider
                                    ></ngx-slider>
                                </div>
                            </div>
                            <div class="filter-price__title">
                                <span class="filter-price__min-value">{{ (rightToLeft ? slider.highValue : slider?.value)|currencyFormat }}</span> –
                                <span class="filter-price__max-value">{{ (rightToLeft ? slider.value : slider?.highValue)|currencyFormat }}</span>
                            </div>
                        </div>

                        <div *ngIf="filter.type === 'check'" class="filter-list" [formGroupName]="filter.slug">
                            <div class="filter-list__list">
                                <label
                                    *ngFor="let item of filter.items; trackBy: trackBySlug"
                                    class="filter-list__item"
                                    [ngClass]="{'filter-list__item--disabled': isItemDisabled(filter, item)}"
                                >
                                    <span class="filter-list__input input-check">
                                        <span class="input-check__body">
                                            <input
                                                class="input-check__input"
                                                type="checkbox"
                                                [value]="item.slug"
                                                [name]="'filter_'   filter.slug"
                                                [formControlName]="item.slug"
                                            >
                                            <span class="input-check__box"></span>
                                            <app-icon class="input-check__icon" name="check-9x7" size="9x7"></app-icon>
                                        </span>
                                    </span>

                                    <span class="filter-list__title">{{ item.name }}</span>
                                    <span class="filter-list__counter">{{ item.count }}</span>
                                </label>
                            </div>
                        </div>

                        <div *ngIf="filter.type === 'radio'" class="filter-list">
                            <div class="filter-list__list">
                                <label
                                    *ngFor="let item of filter.items; trackBy: trackBySlug"
                                    class="filter-list__item"
                                    [ngClass]="{'filter-list__item--disabled': isItemDisabled(filter, item)}"
                                >
                                    <span class="filter-list__input input-radio">
                                        <span class="input-radio__body">
                                            <input
                                                class="input-radio__input"
                                                type="radio"
                                                [attr.disabled]="isItemDisabled(filter, item) ? true : null"
                                                [value]="item.slug"
                                                [formControlName]="filter.slug"
                                            >
                                            <span class="input-radio__circle"></span>
                                        </span>
                                    </span>

                                    <span class="filter-list__title">{{ item.name }}</span>
                                    <span class="filter-list__counter">{{ item.count }}</span>
                                </label>
                            </div>
                        </div>

                        <div *ngIf="filter.type === 'color'" class="filter-color" [formGroupName]="filter.slug">
                            <div class="filter-color__list">
                                <label *ngFor="let item of filter.items; trackBy: trackBySlug" class="filter-color__item">
                                    <span
                                        class="filter-color__check input-check-color"
                                        [ngClass]="['input-check-color--'   (item.color|colorType)]"
                                        [style.color]="item.color"
                                    >
                                        <label class="input-check-color__body">
                                            <input
                                                class="input-check-color__input"
                                                type="checkbox"
                                                [value]="item.slug"
                                                [name]="'filter_'   filter.slug"
                                                [formControlName]="item.slug"
                                            >
                                            <span class="input-check-color__box"></span>
                                            <app-icon class="input-check-color__icon" name="check-12x9" size="12x9"></app-icon>
                                            <span class="input-check-color__stick"></span>
                                        </label>
                                    </span>
                                </label>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="widget-filters__actions d-flex">
        <button class="btn btn-secondary btn-sm" (click)="reset()">Reset</button>
    </div>
</div>
 

Комментарии:

1. Ошибки никогда не бывают случайными, чувак. Ваш файл .ts не заполнен. Исправь это.

2. Коды отдыха были просто импортом. Я знаю, что они не случайны, я имею в виду, что они зависят от времени загрузки фильтров.

3. вместо того *ngIf="filtersForm" , чтобы пытаться *ngIf="filters amp;amp; filtersForm"

4. Не сработало. Все те же ошибки. Мне нужна форма фильтра перед фильтрами, но как?