【问题标题】:Angular 2 click event causing entire component to re-initialize (losing state)Angular 2 点击事件导致整个组件重新初始化(丢失状态)
【发布时间】:2017-01-24 00:41:26
【问题描述】:

我正在尝试构建一个简单的树状结构,允许数据嵌套,以便您可以展开/折叠节点的子节点并最终通过单击元素进行选择。

我遇到的问题是,当我触发 click 事件以切换子节点的显示/隐藏时,顶级组件正在重新初始化,并将标志重置为 false,因此不显示子节点。

所有相关代码如下:

查找数据.interface.ts:

export interface LookupData {
     id: string;
     label: string;
     parentId?: string;
}

lookup-modal.component.ts:

import { Component, Input, Output, EventEmitter } from '@angular/core';
import { LookupData } from './lookup-data.interface';

@Component({
    selector: 'lookup-modal',
    host: {
        '(window:keydown)': 'handleKeyPress($event)'
    },
    templateUrl: '/app/shared/lookup-modal/lookup-modal.component.html',
    styleUrls: ['./app/assets/css/modal.css']
})
export class LookupModalComponent {
    constructor() { }

    @Input() label: string = '';
    @Input() data: LookupData[] = [];
    @Output() close: EventEmitter<string> = new EventEmitter<string>();

    search: string = '';

    get topLevelData(): LookupData[] {
        return this.data.filter((item: LookupData): boolean => !item.parentId);
    }

    handleKeyPress(event: any): void {
        if (event && event.keyCode === 27) {
            this.close.emit();
        }
    }
}

lookup-modal.component.html:

<div class="modal-backdrop fade in fsi-modal-backdrop"></div>
<div tabindex="-1"
     role="dialog"
     class="modal fade scale up in fsi-modal">
    <div class="modal-dialog modal-lg fsi-modal-dialog">
        <div class="modal-content fsi-modal-content">
            <div class="modal-header fsi-modal-header">
                <button type="button" class="close" (click)="close.emit()">
                    <span>×</span>
                </button>
                <h4 class="modal-title">{{label}}</h4>
            </div>
            <div class="modal-body nopad fsi-modal-body">  
                <input type="search" class="form-control" [(ngModel)]="search" />

                <div style="margin-top: .5rem;">
                    <div *ngFor="let topLevelItem of topLevelData; let i = index;">
                        <lookup-item [item]="topLevelItem"
                                     [data]="data"
                                     [i]="i"
                                     [search]="search"
                                     (selection)="close.emit($event)"></lookup-item>
                    </div>
                </div>
            </div>
            <div class="modal-footer fsi-modal-footer">
                <button class="btn btn-danger pull-left" type="button" (click)="close.emit('')">Clear</button>
                <button class="btn btn-default" (click)="close.emit()">Cancel</button>
            </div>
        </div>
    </div>
</div>

查找项目.component.ts:

import { Component, Input, Output, EventEmitter } from '@angular/core';
import { LookupData } from './lookup-data.interface';

@Component({
    selector: 'lookup-item',
    templateUrl: '/app/shared/lookup-modal/lookup-item.component.html'
})
export class LookupItemComponent {
    constructor() { }
    @Input() item: LookupData;
    @Input() i: number;
    @Input() data: LookupData[] = [];
    @Input() search: string = '';
    @Output() selection: EventEmitter<string> = new EventEmitter<string>();

    showChildren: boolean = false;

    // temp/test, not needed
    ngOnInit() {
        if (this.item.id === '1') 
            console.log(this.item.label);
    }

    toggleChildren(): void {
        console.log('toggle');
        this.showChildren = !this.showChildren;
    }

    get children(): LookupData[] {
        return this.data.filter((i: LookupData): boolean => i.parentId === this.item.id);
    }
}

查找项目.component.html:

<div class="row" style="padding: 5px;"
     [ngStyle]="{'border-top': i || item.parentId ? 'none' : '1px solid #eee', 'border-bottom': item.parentId ? 'none' : '1px solid #eee'}">

    <div class="col-xs-1" 
         *ngIf="children.length" 
         style="cursor: pointer; text-align: center;"
         (click)="toggleChildren()">

        <span class="glyphicon"
              [ngClass]="{'glyphicon-triangle-right': !showChildren, 'glyphicon-triangle-down': showChildren}"></span>
    </div>

    <div class="col-xs-11"
         [ngClass]="{'col-xs-offset-1': !children.length}"
         (click)="selection.emit(item.id)"
         style="cursor: pointer;">

        {{item.label}}

        <div *ngIf="showChildren">
            <div *ngFor="let child of children; let j = index;">
                <lookup-item [item]="child"
                             [data]="data"
                             [i]="j"
                             [search]="search"
                             (selection)="selection.emit($event)"></lookup-item>
            </div>
        </div>
    </div>
</div>

所以我的问题是,当 (click) 事件被触发并在查找项组件中调用 toggleChildren() 时,由于某种原因,ngOnInit() 方法再次运行(整个组件重新初始化自身)。这会导致 showChildren 属性重置为 false,即使 toggleChildren() 方法应该将其设置为 true,因此子节点永远不会显示。

我已经在这个问题上花费了数小时并尝试进行研究,但到目前为止在互联网上的任何地方都没有发现任何与我的问题相关的内容。

我真的希望有人能指出我错过的一个明显错误,因为我现在很困惑。

提前致谢。

【问题讨论】:

  • 你试过用箭头函数toggleChildren = () =&gt; { //code }吗?
  • 我认为将其定义为匿名箭头函数在这种情况下不会有所帮助。 toggleChildren 只是 LookupItemComponent 类上的一个方法,当单击包含箭头字形图标的 div 时应该执行该方法。它确实被调用了,但由于某种原因,该组件也被重新初始化,因此它消除了方法调用中发生的状态更改。我以前从未见过这样的事情发生。
  • 箭头函数将保留组件的上下文,因此它可能会有所帮助。您在控制台中看到任何错误吗?
  • 我试了一下 - 不幸的是,没有骰子。不,没有控制台错误。它只是打印出 console.logs,显示确实调用了 toggleChildren 方法,然后立即调用了 ngOnInit 方法 - 我还添加了检查以显示当调用 toggleChildren 时,showChildren 确实设置为 true,然后在 ngOnInit 中显示为 false再次。
  • 我也尝试删除递归,看看是否是导致它的原因(注释掉 在其内部引用的位置。即使没有,问题仍然存在。

标签: angular events recursion tree


【解决方案1】:

所以我只是想通了这一点——它与我生成输入数据的方式有关(原始问题中没有显示,因为我一开始认为它不相关)——而不是传递一个我使用的是 typescript getter,当我出于某种原因尝试对数据进行更改时,它会强制它重新初始化。

【讨论】:

    猜你喜欢
    • 2017-04-05
    • 1970-01-01
    • 2017-07-07
    • 2023-03-20
    • 2018-02-02
    • 2020-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多