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 升级导致错误的主要内容,如果未能解决你的问题,请参考以下文章

关于安卓固件升级的问

怎样把安卓系统升级到7.0

xbox360硬件能升级吗

KindEdito从3.51升级到4.0版本流程怎么升级?

安卓系统怎么升级 怎么升级到安卓6.0

如何升级OpenSSl版本