当我在* ngFor中更改数组时,视图不会更新

时间:2018-11-08 07:39:37

标签: angular

我正在尝试创建一些内部带有mat-table的扩展面板,我的问题是我必须调整窗口大小才能更改视图。我的数据加载正常,但几乎所有视图都不会更新。我的扩展面板应该放置的位置只是空白。直到我单击按钮或调整窗口大小。什么会引起这样的事情?

在我的ngOnInit()中,我致电

this.getSale1(); 

.HTML:

<mat-accordion>
    <mat-expansion-panel *ngFor="let data of mySaleModelArray2 ">
        <mat-expansion-panel-header>
            <mat-panel-title>
                <h6 class="salepanelheadtext">Bar:</h6>{{data.name}}
            </mat-panel-title>
        <mat-panel-description>
            <h6 class="salepanelheadtext2">Total:</h6> {{data.total_sales}}
        </mat-panel-description>
        </mat-expansion-panel-header>
            <div class="example-container mat-elevation-z8">                                
                <mat-table #table [dataSource]="data.sales"  >
                <!-- PLU Column -->                                 
                <ng-container matColumnDef="pluNo">
                    <mat-header-cell *matHeaderCellDef >
                        #
                    </mat-header-cell>
                    <mat-cell *matCellDef="let salesdata"> 
                        {{salesdata.beerline}} 
                    </mat-cell>
                </ng-container>
                <!-- Name Column -->
                <ng-container matColumnDef="name">
                    <mat-header-cell *matHeaderCellDef> Name </mat-header-cell>
                    <mat-cell *matCellDef="let salesdata"> 
                         {{salesdata.pluName}} 
                    </mat-cell>
                </ng-container>
                <!-- Sold_Count Column -->
                <ng-container matColumnDef="sold_count">
                    <mat-header-cell *matHeaderCellDef> 
                        QTY 
                    </mat-header-cell>
                    <mat-cell *matCellDef="let salesdata"> 
                        {{salesdata.sold_count}} 
                    </mat-cell>
                </ng-container>
                <!-- PLU Price Column -->
                <ng-container matColumnDef="pluPrice">
                    <mat-header-cell *matHeaderCellDef> Price </mat-header-cell>
                    <mat-cell *matCellDef="let salesdata"> 
                        {{salesdata.pluPrice}} 
                    </mat-cell>
                </ng-container>
                <!---->
                <ng-container matColumnDef="total_amount">
                    <mat-header-cell *matHeaderCellDef> 
                        Total 
                    </mat-header-cell>
                    <mat-cell *matCellDef="let salesdata"> 
                        {{salesdata.pluPrice * salesdata.sold_count}} 
                    </mat-cell>
                </ng-container>
    <mat-header-row *matHeaderRowDef="displayedColumns2"></mat-header-row>
    <mat-row *matRowDef="let row; columns: displayedColumns2;"></mat-row>
            </mat-table>
        </div>
    </mat-expansion-panel>
</mat-accordion>

.TS:

//Get data from Sale1List
getSale1() {        
    this.customersService.getSale1()
    .subscribe(
        dataList => {                    
                this.updateDataTable(dataList);
         }               
     )       
 }


updateDataTable(dataList) { 
for(var i = 0;i < dataList.length; i++){
    var saleData = <SaleDataModel>dataList[i];

   var mySaleModelTest = this.mySaleModelArray2.find(x => x.name == dataList[i].name);
   if(mySaleModelTest == null){
       //first time creating the object with the bar name
       var tempArray = Array();
       tempArray.push(saleData);

       this.mySaleModelArray2.push(new Sale1Model(dataList[i].name,dataList[i].pluPrice * dataList[i].sold_count,tempArray));

   }else{                           
       //changing the object with the bar name because it already exist
       mySaleModelTest.total_sales = mySaleModelTest.total_sales + dataList[i].pluPrice * dataList[i].sold_count;
       mySaleModelTest.sales.push(saleData);
   }                    
   }    
}

3 个答案:

答案 0 :(得分:2)

ChangeDetectorRef将解决问题。

将他注入构造函数中。

constructor(
  ... 
  private cdr: ChangeDetectorRef,
  ...
) { }

编辑getSale1以便使用cdr:

getSale1() {        
    this.customersService.getSale1()
    .subscribe(
        dataList => {                    
                this.updateDataTable(dataList);
                this.cdr.detectChanges();
         }               
     )       
 }

但是为什么我必须使用ChangeDetectorRef?

默认情况下,Angular使用ChangeDetectionStrategy.default,该逻辑使用其逻辑“唤醒”渲染组件。此处提供更多规格:https://angular.io/api/core/ChangeDetectionStrategy

在某些情况下,这还不够。一种情况是非常大的嵌套*ngFor。另一种情况是scope更改时。让我们以以下代码为例:

function something(){
  this; //A
  this.service.getData
  .subscribe( res => {
    this; //B
  });
}
引用注释this

A是组件。如果在该点上设置断点,则会看到组件的方法和属性。

释放到this(注释B)的

.subscribe不再是组件,而是订阅。那是范围的变化,并且可能引起一些刷新问题。

在javascript中,这种情况更常见,例如IE:在回调中使用this的范围是不同的。但是angular是聪明的,即使在订阅中也知道,如果您执行this.componentMethod(),则试图调用属于该组件的函数。

那为什么要使用cdr?

正如我所说,在某些情况下Angular不唤醒其渲染器。由于每种情况都不尽相同,因此不可能对此定义绝对的答案。这就是为什么我的范围示例发生变化的原因。 cdr.detectChanges()的作用是允许该方法通知Angular的渲染以强制其component.html的渲染。这样,无论您使用哪个strategy(即使它是.onPush),该组件都将重新呈现。

但是要小心。您必须先考虑自己在做什么,然后再执行此操作。例如,重新渲染html引发ngOnChanges事件。这样您就可以进入一个无限循环。

有关cdr的更多信息:https://angular.io/api/core/ChangeDetectorRef

希望这消除了一些疑问。

答案 1 :(得分:0)

如果您只是将MenuItem item = _menu.findItem(R.id.menu_item_id); item.setIcon(ContextCompat.getDrawable(getViewContext(), R.drawable.ic_search_24dp)); 或数据推入数组,则object不会将其检测为已更改属性(这很不好)。因为您没有完全重新分配它。

您可以做的是:

  1. 您可以手动执行更改检测
  2. 使用临时数组重新分配整个数组angular(虽然不是一个好的解决方案)。

答案 2 :(得分:0)

使用具有唯一的return语句的自定义trackby函数(例如,ID应该是唯一的,或者您可以在更改的属性上进行跟踪)

*ngFor="let data of mySaleModelArray2; trackBy: customTB"
customTB(item, index) {
  return `${item.id}-${index}`;
}