Javascript 角度:随机“;找不到名为“的控件”;由于异步数据而导致错误
我有表格错误。错误是随机的,有时页面呈现良好,但有时某些内容无法呈现。我买了角模板,不能在这里修复。我想我需要这样做。过滤器=formbuilder之后的过滤器,但我不知道如何操作。因为html正在使用this.filters,无法在表单中找到内容 组件1.ts:Javascript 角度:随机“;找不到名为“的控件”;由于异步数据而导致错误,javascript,angular,typescript,Javascript,Angular,Typescript,我有表格错误。错误是随机的,有时页面呈现良好,但有时某些内容无法呈现。我买了角模板,不能在这里修复。我想我需要这样做。过滤器=formbuilder之后的过滤器,但我不知道如何操作。因为html正在使用this.filters,无法在表单中找到内容 组件1.ts: import { Component, Inject, Input, OnDestroy, OnInit, PLATFORM_ID } from '@angular/core'; import { isPlatformBrowser
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 && (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 && (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);
}
}
从'@angular/core'导入{组件、注入、输入、OnDestroy、OnInit、平台ID};
从“@angular/common”导入{isPlatformBrowser};
从“../../../shared/services/direction.service”导入{DirectionService};
从'@angular/forms'导入{FormBuilder,FormGroup};
进口{
彩色滤光片,
彩色滤光片,
过滤器,
序列化筛选值,
检查过滤器,
无线电滤波器
}来自“../../../shared/interfaces/filter”;
从“../../../shared/services/root.service”导入{RootService};
从'rxjs'导入{Subject};
从“../../shop/services/page category.service”导入{PageCategoryService};
从“rxjs/operators”导入{map,takeUntil};
接口FormFilterValue{
[filterSlug:string]:[number,number]|{[itemSlug:string]:boolean}| string;
}
@组成部分({
选择器:“应用程序小部件过滤器”,
templateUrl:'./widget filters.component.html',
样式URL:['./widget filters.component.scss']
})
导出类WidgetFiltersComponent实现OnInit、OnDestroy{
@Input()offcanvas:'始终'|'移动'='移动';
销毁$:主题=新主题();
过滤器:过滤器[];
filtersForm:FormGroup;
isPlatformBrowser=isPlatformBrowser(this.platformId);
右至左=假;
建造师(
@注入(平台ID)私有平台ID:any,
私人指导:指导服务,
私人fb:FormBuilder,
public root:RootService,
公共页面类别:PageCategoryService,
) {
this.rightToLeft=this.direction.isRTL();
}
ngOnInit():void{
此.pageCategory.list$.pipe(
映射(x=>x.filters),
takeUntil(这个.destroy$),
).订阅(筛选器=>{
this.filters=过滤器;
this.filtersForm=this.makeFiltersForm(过滤器);
this.filtersForm.valueChanges.subscribe(formValues=>{
this.pageCategory.updateOptions({
filterValues:this.convertFormToFilterValues(过滤器,formValues)
});
});
});
}
ngOnDestroy():void{
this.destroy$.next();
此.destroy$.complete();
}
trackBySlug(索引:编号,项:{slug:string}):任意{
返回项.slug;
}
makeFiltersForm(过滤器:过滤器[]):FormGroup{
常量filtersFromGroup={};
filters.forEach(filter=>{
开关(过滤器类型){
案例“范围”:
“无线电”案例:
filtersFromGroup[filter.slug]=此.fb.控件(filter.value);
打破
案例“检查”:
案例“颜色”:
filtersFromGroup[filter.slug]=此.MakeListFilterPerform(过滤器);
打破
}
});
返回此.fb.group(filtersFromGroup);
}
MakeListFilterPerform(过滤器:CheckFilter | ColorFilter):FormGroup{
常量组={};
filter.items.forEach(item=>{
const control=this.fb.control(filter.value.includes(item.slug));
//需要超时,因为如果立即执行,有时会忽略状态更改。
设置超时(()=>{
如果(此.isItemDisabled(过滤器,项目)){
禁用({emitEvent:false});
}否则{
enable({emitEvent:false});
}
}, 0);
组[项目段塞]=对照组;
});
返回此.fb.group(group);
}
isItemDisabled(过滤器:CheckFilter | RadioFilter | ColorFilter,项目:FilterItem | ColorFilterItem):布尔值{
return item.count==0&&(filter.type=='radio'| | |!filter.value.includes(item.slug));
}
convertFormToFilterValues(筛选器:筛选器[],formValues:FormFilterValues):序列化FilterValues{
常量filterValues:SerializedFilterValues={};
filters.forEach(filter=>{
const formValue=formValues[filter.slug];
开关(过滤器类型){
案例“范围”:
if(formValue&(formValue[0]!==filter.min | | formValue[1]!==filter.max)){
FilterValue[filter.slug]=`${formValue[0]}-${formValue[1]}`;
}
打破
案例“检查”:
案例“颜色”:
const filtermerformvalues=formValue作为对象| |{};
//被动表单不会添加禁用复选框的值。
//此代码将手动添加它们。
filter.value.forEach(filterValue=>{
如果(!(filterValue中的filterValue)){
filterValue[filterValue]=真;
}
});
const values=Object.keys(filtermerformvalues.filter(x=>filtermerformvalues[x]);
如果(value.length>0){
filterValues[filter.slug]=values.join(',');
<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' && 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>