Angular:如何订阅 @Input 更改

Posted

技术标签:

【中文标题】Angular:如何订阅 @Input 更改【英文标题】:Angular: How to subscribe to @Input changes 【发布时间】:2018-03-31 08:26:30 【问题描述】:

我正在尝试创建一个角度组件,它使用提供的对象数组呈现一个 html 表。

我已经实现OnChanges 来检测对@Input 的更改,但它似乎不起作用。

为了测试,我正在使用setInterval 添加一些数据,但它没有反映在子组件中。

这是我到目前为止的代码,

ng-table.component.ts:

export class NgTableComponent implements OnInit, OnChanges 

    @Input() data: any[];

    private columns: string[];
    private rows: string[][];

    constructor() 
        this.columns = [];
        this.rows = [];
    

    ngOnInit() 
    

    ngOnChanges(changes: SimpleChanges) 
        console.log(changes);
        if (changes.data) 
            this.extractRowsAndColumns(this.data);
        
    

    extractRowsAndColumns(objArray: any[]): void 
        const colSet = new Set<string>();
        for (let i = 0; i < objArray.length; i++) 
            const keys = Object.keys(objArray[i]);
            for (let j = 0; j < keys.length; j++) 
                colSet.add(keys[j]);
            
        
        this.columns = Array.from(colSet);

        for (let i = 0; i < objArray.length; i++) 
            const obj = objArray[i];
            const row = [];
            for (let j = 0; j < this.columns.length; j++) 
                if (obj.hasOwnProperty(this.columns[j])) 
                    row.push(obj[this.columns[j]]);
                 else 
                    row.push(null);
                
            
            this.rows.push(row);
        
    

ng-table.component.html:

<div>
    <table class="ngtable">
        <tr>
            <th *ngFor="let col of columns">
                col
            </th>
        </tr>
        <tr *ngFor="let row of rows">
            <td *ngFor="let cell of row">cell</td>
        </tr>
    </table>
</div>

我在 app.component 中使用上面的组件

app.component.ts:

export class AppComponent 
  timer: any;
  count = 0;
  constructor() 
    const o: any = 
      'id': 1,
      'name': 'Jeanette',
      'last_name': 'Penddreth',
      'email': 'jpenddreth0@census.gov',
      'gender': 'Female',
      'ip_address': '26.58.193.2'
    ;
    this.timer = setInterval(() => 
      this.data.push(o);
      console.log(this.data.length);
      if ( this.count++ === 5) 
        clearInterval(this.timer);
      
    , 1000 * 1);
  

  data = [
    'id': 1,
    'name': 'Jeanette',
    'last_name': 'Penddreth',
    'email': 'jpenddreth0@census.gov',
    'gender': 'Female',
    'ip_address': '26.58.193.2'
  , 
    'id': 2,
    'name': 'Giavani',
    'last_name': 'Frediani',
    'email': 'gfrediani1@senate.gov',
    'gender': 'Male',
    'ip_address': '229.179.4.212'
  , 
    'id': 3,
    'name': 'Noell',
    'last_name': 'Bea',
    'email': 'nbea2@imageshack.us',
    'gender': 'Female',
    'ip_address': '180.66.162.255'
  , 
    'id': 4,
    'name': 'Willard',
    'last_name': 'Valek',
    'email': 'wvalek3@vk.com',
    'gender': 'Male',
    'ip_address': '67.76.188.26'
  ];

app.component.html:

<app-ng-table [data]="data"></app-ng-table>

@Input 发生变化时如何更新组件?

更新:我创建了一个 plunkr 来证明这一点:https://plnkr.co/edit/szz1SNooQ1vIZhnFgiys?p=preview

【问题讨论】:

通过引用answer来尝试观察输入 【参考方案1】:

只需让您的 @Input() 双向绑定...

_data;
@Input() set data(val) 
  this._data = val;

get data() 
  return this._data;

【讨论】:

【参考方案2】:

我之前也遇到过同样的问题; ngOnChanges 方法只监听数据类型 ==! 数组的变化。

解释(ngOnChanges not firing for nested object):

在变更检测期间,当 Angular 检查组件的输入时 更改的属性,它(基本上)使用 === 进行脏检查。 对于数组,这意味着(仅)对数组引用进行了脏检查。 由于 rawLapsData 数组引用没有改变,ngOnChanges() 不会被调用。

【讨论】:

你是对的,孩子中的数据确实在更新,但ngOnChanges 事件没有被触发。我尝试使用setInterval 在子组件中记录数据,我可以看到它正在更新。【参考方案3】:

作为解决问题的简单方法,您可以像这样检测@Input 中的ngOnChanges 变化:

ngOnChanges(changes: SimpleChanges)    
   for (let propName in changes) 
      // when your @Input value is changed  
      if(propName === "yourInputName")
         // update the component here 
      
   

希望对你有帮助:)

【讨论】:

我已经实现了OnChanges,但它不起作用。 你想要什么:如果data 的值发生了变化,你可以通过使用新的data 调用方法extractRowsAndColumns 来更新组件,对吗? 是的,这正是我想要的 有什么错误吗? ,当您实施我的答案时究竟会发生什么? 我在调试控制台中找不到任何错误,表没有得到更新(新行没有被添加)。【参考方案4】:

正如 TimHovius 所说,angular 只是做参考检查。由于您正在推送到同一个数组,因此 ng-table.component 没有检测到更改(对输入的引用仍然相同)。您可以做的一件事是创建数组的浅表副本。

addItem(): void 
    this.data.push(this.data[this.data.length-1]);
    // create shallow copy of array, since this is a new array (and new reference) ngOnChanges hook of the ng-table.component will fire
    this.data = this.data.slice(0);

现在 ng-table.component 将检测到输入已经改变并触发 ngOnChanges

这是一个演示这个的 plunkr (https://plnkr.co/edit/2tdMEA9PLc2TEL4Gy4Lp?p=preview)

【讨论】:

@cyberpirate92 忘记添加我的 plnkr 感谢@LLai,浅拷贝工作。我想知道是否有任何其他方式我不必在父组件中进行浅拷贝?我想知道kendo-grid 在没有显式浅拷贝的情况下是如何做到的。 @cyberpirate92 我不熟悉 kendo-grid,但它可能不依赖于 ngOnChanges 钩子。 (plnkr.co/edit/G1dlSJqtcJ9NX7F44zaz?p=preview) 在这个 plunkr 中,您可以看到子组件确实接收了 ngFor 中的更改。但是在您的情况下,由于您的 ngOnChanges 挂钩中有 extractRowsAndColumns 方法,因此您需要它来运行。【参考方案5】:

您需要为其分配新对象。大部分都可以通过Object.assign完成

在你的例子中,你只需要改变几行,休息就会顺利。

...
let data:[] = this.data;
data.push(o);
this.data = Object.assign([], data);
...

【讨论】:

以上是关于Angular:如何订阅 @Input 更改的主要内容,如果未能解决你的问题,请参考以下文章

在使用带有 Angular 的 FromEvent RxJS 订阅输入标签值更改时如何避免使用“任何”?

Angular X - 如何更改 index.html 中已编译 JavaScript 文件的路径 [重复]

Angular 9 在每次更改时滚动到顶部

Angular 2 Material Input 动态更改占位符

如何在不订阅 Angular 表单提交的情况下创建/更新项目

canActivate 在订阅更改时不响应 Angular 2 路由器 AngularFire2