Angular 从Typescript获取程序初始化输入数组时,在新对象上迭代时,角度材质按钮不可单击

Angular 从Typescript获取程序初始化输入数组时,在新对象上迭代时,角度材质按钮不可单击,angular,angular-material,Angular,Angular Material,我有以下令人困惑的角度问题 编辑:我设法在Stackblitz示例中重现了这一点,并更新了此处的文本。 以闪电战为例 这显示了四组三个列表项,带有删除图标,并具有以下配对: string input/passed from a getter string input/passed from a field array input/passed from a getter array input/passed from a field 在array/getter案例中当我单击第一个(从项目1)时

我有以下令人困惑的角度问题

编辑:我设法在Stackblitz示例中重现了这一点,并更新了此处的文本。

以闪电战为例

这显示了四组三个列表项,带有删除图标,并具有以下配对:

string input/passed from a getter
string input/passed from a field
array input/passed from a getter
array input/passed from a field
在array/getter案例中当我单击第一个(从
项目1
)时,控制台中没有任何记录;但是单击第二个(来自
items2
)或第三个(来自
items3
),它会记录
“删除”
。在其他情况下,它总是按照预期记录delete

这里可能发生了什么事?

代码如下,尽管很容易使用

首先是父HTML,它设置了四种情况:

带getter的字符串

不带getter的字符串
带getter的数组
无getter的数组
和打字稿:

@组件({
选择器:“我的应用程序”,
templateUrl:“./app.component.html”,
样式URL:['./app.component.css']
})
导出类AppComponent{
获取名称1():字符串{
返回“角度”;
}
名称2=‘角度’;
获取名称1():字符串[]{
返回['Angular'];
}
名称2=['Angular'];
}
然后是组件HTML:


一些文本
删除
一些文本
删除
一些文本
删除
和打字稿:

@组件({
选择器:“你好”,
templateUrl:“./hello.component.html”,
样式:[`h1{font-family:Lato;}`]
})
导出类HelloComponent{
@Input()名称:string;
@Input()名称:字符串[];
项目1:编号[]=[];
项目2:编号[]=[];
项目3=[新编号(42)];
ngOnChanges(){
this.items1=[新编号(42)];
this.items2=[42];
}
删除(){
console.log('delete');
}
}

hello.component.ts
中,如果您更改
this.items1=[新编号(42)]
to
this.items1=[42]
它正在工作

老实说,我无法确切地告诉您原因,但它是有效的:-)似乎与使用常规类型
编号
有关,可能与
ngochanges()
中Angular的ChangeDetection结合使用。我个人知道这些类型存在,但从未见过有人使用它们。
关于这一点,我在网上读到:

永远不要使用数字、字符串、布尔值或对象类型。这些类型指的是在JavaScript代码中几乎从未正确使用过的非基本装箱对象

这意味着您最好使用小写字母,如
number
来指定类型,而不要使用构造函数来创建新的
number
s

我希望这有帮助

这里的一个问题(虽然不是根本原因)是,当使用
ngFor
循环时,Angular使用对象标识跟踪是否需要重新渲染。有关讨论,请参阅。因此(见下文)添加一个
trackBy
函数可以解决这个问题

在糟糕的情况下:

  • 将getter与列表一起用于输入会导致使用相同的值调用两次
    ngOnChanges
    。Angular可能会访问输入两次(我不知道为什么),但每次访问时,getter都会返回一个新列表,该列表具有不同的标识,因此
    ngOnChanges
    会被调用两次
  • 它不会被
    字符串
    getter调用两次;
    字符串
    对象是相同的
  • 调用
    ngOnChanges
    时,它会重置坏列表(
    items1
    )以包含新对象
结果是,在本例中,Angular渲染列表两次,因为它认为所有元素从一个过程更改为下一个过程

现在:我不知道为什么这会导致按钮无法点击。但是Angular允许您覆盖
trackBy
函数,该函数用于确定列表中的元素是否已更改。将其更改为按索引跟踪(此处不更改)可以解决问题

Forked Stackblitz解决了这个问题,它增加了:

到组件的Typescript:

trackByIndex(索引,项){
收益指数;
}
在HTML循环中:


一些文本
删除

你本质上是。。。某种程度上。。。创建递归

  • 通过将输入传递到触发器
    ngochanges
    ,从而初始化
    *ngFor
    的值
  • 然后更新视图,因为初始值为空
  • 然后,当视图“更改”并更改基础标识时,它会触发另一个
    ngOnChanges
    事件
  • 等等。。。等等
  • 正如您指出的,在每次迭代中,“身份”都会发生变化

    迭代器中元素的标识可以在 数据不存在。*例如,如果迭代器是 从RPC生成到服务器,并重新运行该*RPC。即使 数据没有更改,第二个响应生成具有* 不同的身份和角度必须破坏整个DOM和 重建它(就好像所有旧*元素都被删除,所有新元素都被删除一样 插入的元素)

    setTimeout
    ngOnChanges
    中的块包装起来,并在其前面放置一个
    console.log
    ,这将暴露我试图说明的潜在问题并破坏堆栈

    • 我知道这是一个非常激烈的例子来说明这里正在发生的事情,但我认为重要的是要把它发挥到极致,以了解你正在尝试的方法正在发生什么

      ngOnChanges() {
              console.log('test')
              //setTimeout(()=>{
              // this.items1 = [new Number(42)];
              // this.items2 = [42];
              // })
      
            }
      
      ngAfterViewInit(){
            //passing the input to the *ngFor or figuring out some other way to update the 
            `hello.component` array would be a more stable solution. Leveraging `ngOnChanges` 
            to modify the view via component variable will not be a stable approach.
      
             for (const name in this.names) {
                this.items1 = [new Number(42)];
                this.items2 = [42];
              }
          }
      

    刚刚在一个本地项目中尝试了你的代码,对我来说效果不错。只是一个随机猜测:可能是你的CSS导致了这种行为。是