Kendo-angular-dropdown 升级导致错误
Posted
技术标签:
【中文标题】Kendo-angular-dropdown 升级导致错误【英文标题】:Kendo-angular-dropdown upgrade causing error 【发布时间】:2021-01-24 01:04:57 【问题描述】:我最近将我的工作应用程序从 Angular 6 升级到 Angular 7.2。 这包括升级 Kendo-Angular 组件/模块。 升级后,在回归测试期间,我们注意到在使用模板的 kendo-combobox 上,我们收到以下错误:
V2: CustomExceptionHandler Error occurred TypeError: Cannot read property 'scrollToItem' of undefined
at main.js:89941
at SafeSubscriber.schedulerFn [as _next] (main.js:14671)
at SafeSubscriber.__tryOrUnsub (main.js:45229)
at SafeSubscriber.next (main.js:45167)
at Subscriber._next (main.js:45113)
at Subscriber.next (main.js:45090)
at EventEmitter.Subject.next (main.js:86078)
at EventEmitter.emit (main.js:14643)
at PopupComponent.onAnimationEnd (main.js:125483)
at main.js:125443
这看起来像是来自 kendo-popup,但我似乎无法修复它。 我试过的: 升级到最新版本的 kendo-angular-dropdowns 和 kendo-angular-popup 删除 node-modules 文件夹并清除 npm 缓存
降级到 kendo-angular-dropdowns@3.4.0 可以修复它,但是如果我升级到 3.5.0 或更高版本,则会返回错误。 我不知道如何将 kendo-angular-dropdowns 更新到最新版本并解决此错误。
谢谢
来自 package.json 的依赖项
"dependencies":
"@angular/animations": "7.2.16",
"@angular/cdk": "7",
"@angular/common": "7.2.16",
"@angular/compiler": "7.2.16",
"@angular/compiler-cli": "7.2.16",
"@angular/core": "7.2.16",
"@angular/forms": "7.2.16",
"@angular/http": "7.2.16",
"@angular/material": "7",
"@angular/platform-browser": "7.2.16",
"@angular/platform-browser-dynamic": "7.2.16",
"@angular/platform-server": "7.2.16",
"@angular/router": "7.2.16",
"@ngrx/effects": "^7.4.0",
"@ngrx/store": "^7.4.0",
"@ngrx/store-devtools": "^7.4.0",
"@ngui/auto-complete": "0.16.0",
"@progress/kendo-angular-buttons": "5.4.2",
"@progress/kendo-angular-charts": "4.1.4",
"@progress/kendo-angular-common": "1.2.3",
"@progress/kendo-angular-dateinputs": "4.2.2",
"@progress/kendo-angular-dialog": "4.2.0",
"@progress/kendo-angular-dropdowns": "3.4.0",
"@progress/kendo-angular-excel-export": "3.1.3",
"@progress/kendo-angular-grid": "4.7.2",
"@progress/kendo-angular-inputs": "6.6.0",
"@progress/kendo-angular-intl": "2.0.1",
"@progress/kendo-angular-l10n": "2.0.1",
"@progress/kendo-angular-layout": "4.2.1",
"@progress/kendo-angular-pdf-export": "2.0.3",
"@progress/kendo-angular-popup": "3.0.6",
"@progress/kendo-angular-ripple": "2.0.1",
"@progress/kendo-angular-scrollview": "3.0.1",
"@progress/kendo-angular-sortable": "3.0.2",
"@progress/kendo-angular-tooltip": "2.1.3",
"@progress/kendo-angular-treeview": "4.2.0",
"@progress/kendo-angular-upload": "6.0.0",
"@progress/kendo-data-query": "1.5.4",
"@progress/kendo-drawing": "1.9.2",
"@progress/kendo-theme-default": "2.54.0",
"@progress/kendo-theme-material": "^1.5.0",
"@types/clipboard": "1.5.35",
"@types/file-saver": "1.3.0",
"@types/highcharts": "5.0.11",
"@types/lodash": "^4.14.62",
"@types/moment-timezone": "^0.5.4",
"angular-l10n": "7.2.0",
"angular-resizable-element": "^3.3.3",
"angular-router-loader": "0.7.0",
"angular2-busy": "^2.0.4",
"aspnet-prerendering": "3.0.1",
"aspnet-webpack": "1.0.29",
"babel-polyfill": "^6.23.0",
"bootstrap": "3.3.7",
"clipboard": "^1.6.1",
"compression-webpack-plugin": "1.0.1",
"exceljs": "3.5.0",
"file-saver": "1.3.3",
"font-awesome": "4.7.0",
"hammerjs": "^2.0.8",
"highcharts": "6.0.3",
"html-webpack-plugin": "^2.28.0",
"husky": "^0.14.3",
"isomorphic-fetch": "2.2.1",
"lodash": "^4.17.4",
"lscache": "1.1.0",
"moment": "^2.17.1",
"moment-timezone": "^0.5.14",
"ng2-appinsights": "^1.0.0-beta.1",
"ng2-auto-complete": "^0.12.0",
"ng2-dnd": "^5.0.2",
"ngx-bootstrap": "2.0.0-beta.1",
"ngx-tinymce": "^7.0.0",
"ngx-toastr": "8.8.0",
"normalize.css": "7.0.0",
"optimize-js-plugin": "^0.0.4",
"preboot": "5.1.7",
"rxjs": "6.6.3",
"rxjs-compat": "^6.6.3",
"zone.js": "0.8.29"
,
组件 HTML
<div *ngIf="ttsConfig?.formgroup" #form="ngForm" [formGroup]="ttsConfig.formgroup">
<!--value:ttsConfig.formgroup.controls.counterpartyId.value-->
<div class="tts-wrapper" >
<span class="tts-fieldname">ttsConfig.translate? friendlyFieldName : ttsConfig.friendlyFieldName<span *ngIf="ttsConfig.required" style="padding-left:3px;">*</span></span>
<div *ngIf="!AuthInfo">
<kendo-combobox #autocomplete [id]="id"
[data]="data"
[popupSettings]="width: ttsConfig.width, appendTo: ViewContainerRef, animate: false"
[filterable]="true"
[formControlName]="ttsConfig.formcontrolname"
(filterChange)="callDebouncer($event)"
[valueField]="ttsConfig.apiIdName"
[textField]="ttsConfig.displayTextColoumn"
[valuePrimitive]="true"
(open)="checkMinLength($event)"
[placeholder]="ttsConfig.translate? placeholder : ttsConfig.placeholder"
[required]="ttsConfig.required"
class="ttsConfig.cssClass"
(valueChange)="onGridSelectionChange($event)">
<ng-template kendoAutoCompleteHeaderTemplate>
<table >
<tr>
<td *ngFor="let i of ttsConfig.columns" style="font-weight:bold"><span *ngIf="ttsConfig.translate" l10nTranslate>i</span><span *ngIf="!ttsConfig.translate">i</span></td>
</tr>
</table>
</ng-template>
<ng-template kendoAutoCompleteItemTemplate let-dataItem>
<table >
<tr>
<td *ngFor="let i of ttsConfig.apiDisplayObjectName">
dataItem[i]
</td>
</tr>
</table>
</ng-template>
</kendo-combobox>
<button *ngIf="ttsConfig.displayIcon === true" #anchor (click)="toggle()" class="k-button k-button-icon" [hidden]="data.length === 0"><i class="fa fa-info" aria-hidden="true"></i></button>
<div class="custom-error-tts" *ngIf="ttsConfig.formgroup.controls[ttsConfig.formcontrolname]?.errors && ttsConfig.formgroup.controls[ttsConfig.formcontrolname].touched === true">
<strong l10nTranslate>ttsConfig.formgroup.controls[ttsConfig.formcontrolname].errors | errorPipe</strong>
</div>
</div>
<div *ngIf="AuthInfo">
<kendo-combobox #autocomplete [id]="id"
[data]="data"
[popupSettings]="width: ttsConfig.width, appendTo: ViewContainerRef, animate: false"
[filterable]="true"
[formControlName]="ttsConfig.formcontrolname"
(filterChange)="callDebouncer($event)"
[valueField]="ttsConfig.apiIdName"
[textField]="ttsConfig.displayTextColoumn"
[valuePrimitive]="true"
(open)="checkMinLength($event)"
[placeholder]="ttsConfig.translate? placeholder : ttsConfig.placeholder"
[required]="ttsConfig.required"
class="ttsConfig.cssClass"
(valueChange)="onGridSelectionChange($event)" counterpartyAccess [authInfo]="AuthInfo">
<!-- <ng-template kendoAutoCompleteHeaderTemplate>
<table >
<tr>
<td *ngFor="let i of ttsConfig.columns" style="font-weight:bold"><span *ngIf="ttsConfig.translate" l10nTranslate>i</span><span *ngIf="!ttsConfig.translate">i</span></td>
</tr>
</table>
</ng-template>
<ng-template kendoAutoCompleteItemTemplate let-dataItem>
<table >
<tr>
<td *ngFor="let i of ttsConfig.apiDisplayObjectName">
dataItem[i]
</td>
</tr>
</table>
</ng-template> -->
</kendo-combobox>
<button *ngIf="ttsConfig.displayIcon === true" #anchor (click)="toggle()" class="k-button k-button-icon" [hidden]="data.length === 0"><i class="fa fa-info" aria-hidden="true"></i></button>
<div class="custom-error-tts" *ngIf="ttsConfig.formgroup.controls[ttsConfig.formcontrolname]?.errors && ttsConfig.formgroup.controls[ttsConfig.formcontrolname].touched === true">
<strong l10nTranslate>ttsConfig.formgroup.controls[ttsConfig.formcontrolname].errors | errorPipe</strong>
</div>
</div>
<kendo-popup #popup [anchor]="anchor" popupClass="content" *ngIf="show">
<table>
<tr>
<td *ngFor="let i of ttsConfig.columns" style="font-weight:bold"><span *ngIf="ttsConfig.translate" l10nTranslate>i</span><span *ngIf="!ttsConfig.translate">i</span></td>
</tr>
</table>
<table *ngIf="data.length > 0">
<tr>
<td style="max-height: 100px; max-width:200px; white-space:pre-wrap;" *ngFor="let i of ttsConfig.apiDisplayObjectName">
data[0][i]
</td>
</tr>
</table>
</kendo-popup>
</div>
<span *ngIf="IsMDM">*MDM</span>
</div>
Component.ts 文件
import Component, ViewChild, Input, Output, EventEmitter, ElementRef, HostListener, OnInit, OnChanges, OnDestroy, LOCALE_ID, Inject from '@angular/core';
import FormGroup, FormControl, Validators, FormBuilder from '@angular/forms';
import AutoCompleteComponent from '@progress/kendo-angular-dropdowns';
import DropdownService from '@ldc/core/shared/services';
import takeUntil, debounceTime, distinctUntilChanged from 'rxjs/operators';
import Subject from 'rxjs';
//import TranslationService from 'angular-l10n';
import LdcLocale, LocaleService, TranslationService from '@ldc/core/shared/classes/LdcLocale';
import LocaleSubscriptionService from '@ldc/core/shared/services';
import CldrIntlService from '@progress/kendo-angular-intl';
@Component(
selector: 'ldc-type-to-search',
templateUrl: './type-to-search.component.html',
styleUrls: ['./type-to-search.component.scss']
)
export class TypeToSearchComponent extends LdcLocale implements OnInit, OnChanges, OnDestroy
@ViewChild('autocomplete') public autocomplete: AutoCompleteComponent;
@Input() ttsConfig: TypeToSearchConfig = new TypeToSearchConfig();
@Input() id: string = undefined;
@Output() public selectedItemChange: EventEmitter<any> = new EventEmitter();
@ViewChild('anchor') public anchor: ElementRef;
@ViewChild('popup', read: ElementRef ) public popup: ElementRef;
@Input() IsMDM = false;
@Input() AuthInfo: any;
public gridSelection: string[] = [];
public dataList: any[] = [];
public data: any[] = [];
private formSubscribed: boolean = false;
private ngUnsubscribe = new Subject();
private debouncer: Subject<any> = new Subject();
public value: any;
private toggleText = 'Show';
private show = false;
private initalLoadComplete = false;
private friendlyFieldName: string = '';
private placeholder: string = '';
constructor(@Inject(LOCALE_ID) public localeId: string,
private dropdownService: DropdownService,
private localeService: LocaleService,
private localeSubscriptionService: LocaleSubscriptionService,
private intlService: CldrIntlService,
private translationService: TranslationService)
super(localeService, translationService);
ngOnInit()
this.localeSubscriptionService.currentLocaleId.pipe(takeUntil(this.ngUnsubscribe)).subscribe(localeId => this.setLocaleId(localeId));
if (this.ttsConfig.apiByTermTypeNoSearchUrl)
this.callApi();
this.debouncer.pipe(takeUntil(this.ngUnsubscribe), debounceTime(100)).subscribe(event =>
this.handleFilter(event);
);
ngOnChanges(change)
if (change.ttsConfig && change.ttsConfig.currentValue.formgroup !== null && this.formSubscribed === false)
this.formSubscribed = true;
if (this.ttsConfig.formgroup.value[this.ttsConfig.formcontrolname] !== null && change.previousValue === undefined)
this.callApi(this.ttsConfig.formgroup.value[this.ttsConfig.formcontrolname]);
//this was originally subscribed to the entire form and could negatively impact performance
//we need to only subscribe to the control itself
this.ttsConfig.formgroup.controls[this.ttsConfig.formcontrolname].valueChanges.pipe(takeUntil(this.ngUnsubscribe)).subscribe((val) =>
if (val !== null)
this.callApi(val);
);
let formcontrolname = this.ttsConfig.formcontrolname;
let formgroup = this.ttsConfig.formgroup;
let formControlValue = formgroup.controls[formcontrolname].value;
if (this.ttsConfig.useDirectBinding === true && formControlValue !== undefined && formControlValue !== null && formControlValue !== '')
this.callApi(formControlValue);
setLocaleId(localeId: string)
this.localeId = localeId;
this.intlService.localeId = localeId;
if (this.ttsConfig.translate)
this.translationService.init().then((data) =>
this.placeholder = this.translationService.translate(this.ttsConfig.placeholder, null, this.localeService.getCurrentLanguage());
this.friendlyFieldName = this.translationService.translate(this.ttsConfig.friendlyFieldName, null, this.localeService.getCurrentLanguage());
);
ngOnDestroy()
this.ngUnsubscribe.next();
this.ngUnsubscribe.complete();
private callDebouncer(event)
this.debouncer.next(event);
public onGridSelectionChange(val): void
if (!val)
this.selectedItemChange.emit( value: null, control: this.ttsConfig.formcontrolname );
return;
if (this.ttsConfig.emitPrimitive)
if (this.ttsConfig.apiUrl === 'CRS/ViewTTSGinInvoiceCounterparty/')
let selectedobj = this.dataList.find(x => x[this.ttsConfig.apiIdName] == val);
this.selectedItemChange.emit( value: selectedobj, control: this.ttsConfig.formcontrolname );
else
this.selectedItemChange.emit( value: val, control: this.ttsConfig.formcontrolname );
else
let selectedobj = this.dataList.find(x => x[this.ttsConfig.apiIdName] == val);
this.selectedItemChange.emit( value: selectedobj, control: this.ttsConfig.formcontrolname );
this.value = val;
handleFilter(value)
if (value.length >= this.ttsConfig.minLength)
this.callApi(value);
else
this.autocomplete.toggle(false);
checkMinLength(e)
if (this.autocomplete.text.length >= this.ttsConfig.minLength || this.ttsConfig.apiByTermTypeNoSearchUrl)
else
this.autocomplete.toggle(false);
callApi(searchTerm?)
if (searchTerm !== undefined && searchTerm.trim().length > 0)
let term = `$this.ttsConfig.apiUrl$encodeURIComponent(searchTerm)`;
this.dropdownService.getData(term, 0).pipe(takeUntil(this.ngUnsubscribe)).subscribe(resp =>
this.dataList = resp;
this.data = this.dataList.slice();
const fgvalue = this.ttsConfig.formgroup.controls[this.ttsConfig.formcontrolname].value;
if (this.ttsConfig.emitInitalLoad && !this.initalLoadComplete && fgvalue)
this.onGridSelectionChange(fgvalue);
this.initalLoadComplete = true;
if (this.ttsConfig.apiByTermTypeNoSearchUrl)
this.data.sort((a, b) => a.TermDescription.localeCompare(b.TermDescription));
);
else
let term = this.ttsConfig.apiByTermTypeNoSearchUrl ? `$this.ttsConfig.apiByTermTypeNoSearchUrl` : undefined;
if (term)
this.dropdownService.getData(term, 0).pipe(takeUntil(this.ngUnsubscribe)).subscribe(resp =>
this.dataList = resp;
this.data = this.dataList.slice();
this.data.sort((a, b) => a.TermDescription.localeCompare(b.TermDescription));
);
@HostListener('keydown', ['$event'])
public keydown(event: any): void
if (event.keyCode === 27) //escape key
this.toggle(false);
@HostListener('document:click', ['$event'])
public documentClick(event: any): void
if (this.ttsConfig.displayIcon && this.data.length > 0)
if (!this.contains(event.target))
this.toggle(false);
public toggle(show?: boolean): void
this.show = show !== undefined ? show : !this.show;
this.toggleText = this.show ? 'Hide' : 'Show';
private contains(target: any): boolean
if (this.ttsConfig.displayIcon && this.data.length > 0)
return this.anchor.nativeElement.contains(target) ||
(this.popup ? this.popup.nativeElement.contains(target) : false);
else
return false;
export class TypeToSearchConfig
columns: string[];
required: boolean;
apiUrl: string;
apiDisplayObjectName: string[];
displayTextColoumn: string;
apiIdName: string;
formcontrolname: string;
formgroup: FormGroup;
placeholder: string;
friendlyFieldName: string;
width: number;
cssClass: string;
displayIcon: boolean;
minLength: number;
apiByTermTypeNoSearchUrl?: string;
emitPrimitive: boolean;
emitInitalLoad: boolean;
useDirectBinding: boolean;
translate: boolean;
constructor(
columns: string[] = [],
requried: boolean = false,
apiUrl: string = null,
apiDisplayObjectName: string[] = null,
displayTextColoumn: string = null,
apiIdName: string = null,
fromcontrolname: string = null,
formgroup: FormGroup = null,
placeholder: string = '',
friendlyFieldName: string = '',
width: number = 750,
cssClass: string = 'lc-combobox',
displayIcon: boolean = true,
minLength: number = 3,
apiByTermTypeNoSearchUrl: string = null,
translate: boolean = false)
this.columns = columns;
this.required = requried;
this.apiUrl = apiUrl;
this.displayTextColoumn = displayTextColoumn;
this.apiDisplayObjectName = apiDisplayObjectName;
this.apiIdName = apiIdName;
this.formcontrolname = fromcontrolname;
this.formgroup = formgroup;
this.placeholder = placeholder;
this.friendlyFieldName = friendlyFieldName;
this.width = width;
this.cssClass = cssClass;
this.displayIcon = displayIcon;
this.minLength = minLength;
this.apiByTermTypeNoSearchUrl = apiByTermTypeNoSearchUrl;
this.translate = translate;
this.emitPrimitive = true;
this.emitInitalLoad = false;
this.useDirectBinding = false;
``
【问题讨论】:
【参考方案1】:所以我遇到的问题是我不想在组合框中显示弹出窗口,直到 usr 在输入中输入了最少的字符数。
以前(另一个开发人员)通过连接到组合框的打开事件 (open)="checkMinLength($event)"
来完成此操作
这是这样做的:
checkMinLength(e)
if (this.autocomplete.text.length <= this.minLength)
this.autocomplete.toggle(false);
我找到了一个处理类似问题的telerk support question here。
基本上我的解决方案是用e.preventDefault();
替换this.autocomplete.toggle(false);
【讨论】:
以上是关于Kendo-angular-dropdown 升级导致错误的主要内容,如果未能解决你的问题,请参考以下文章