#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. Не сработало. Все те же ошибки. Мне нужна форма фильтра перед фильтрами, но как?