未定义角度的甜甜圈图变量



提前感谢您的回复。我有一个有角度的材料表,我需要在显示可扩展表时显示一些绘图。尽管情节是动画,但我在控制台中收到了以下消息。无法读取未定义的属性"devices"。知道这个想法可能在哪里吗?结尾的console.log正确地显示了数据,而不是像预期的那样未定义。


<table mat-table
[dataSource]="dataSource" multiTemplateDataRows
class="mat-elevation-z8" matSort>

<ng-container matColumnDef="lastSeen">
<th mat-header-cell *matHeaderCellDef> Last seen </th>
<td mat-cell *matCellDef="let element"> {{element.telemetryUtc | date: 'medium'}} </td>
</ng-container> 
<ng-container matColumnDef="detail">
<th mat-header-cell *matHeaderCellDef> Last seen </th>
<td mat-cell *matCellDef="let element"> 
<button (click)="goToDeviceDetails(element)">detail</button>
</td>
</ng-container> 

<!-- Expanded Content Column - The detail row is made up of this one column that spans across all columns -->
<ng-container matColumnDef="expandedDetail">
<td mat-cell *matCellDef="let element" [attr.colspan]="displayedColumns.length">
<div class="example-element-detail"
[@detailExpand]="element == expandedElement ? 'expanded' : 'collapsed'" fxLayout="row" fxLayoutAlign="end end">
<div class="telemetry">
<div class="cards" >
<div class="widget-group p-12 extraPadding" fxLayout="row" fxFlex="100" *fuseIfOnDom [@animateStagger]="{value:'50'}">
<!-- WIDGET 1 -->
<fuse-widget [@animate]="{value:'*',params:{y:'100%'}}" class="widget" fxLayout="column"
>
<div class="fuse-widget-front fixWidthandHeight">
<div  fxLayout="row" fxLayoutAlign="space-between center">
<div class="h2">CPU Temperature</div>
</div>
<div  fxLayout="column" fxLayoutAlign="center center" style="height: 100%">
<div class="temp" *ngIf="telemetry.cpuTemperature" class="h2">
{{telemetry.cpuTemperature}}º C
</div>
<div class="temp" *ngIf="!telemetry.cpuTemperature" class="h2">
No data
</div>
</div>
</div>
</fuse-widget>
<!-- / WIDGET 1 -->

<!-- WIDGET 2 -->
<fuse-widget [@animate]="{value:'*',params:{y:'100%'}}" class="widget" fxLayout="column"
>
<div class="fuse-widget-front">
<div class="pl-16 pr-8 py-16 h-52" fxLayout="row"
fxLayoutAlign="space-between center">
<div class="h2">CPU</div>
</div>
<div class="pt-8 pb-32" fxLayout="column" fxLayoutAlign="center center">
<ngx-charts-pie-chart
[scheme]="cpuPlot.scheme"
[results]="cpuPlot.devices"
[doughnut]="true" class="fixWidthandHeight">
</ngx-charts-pie-chart>
</div>
<div class="p-16 border-top" fxLayout="row" fxLayoutAlign="start center">
<div *ngIf="telemetry.ramUsage" class="h4 secondary-text">{{telemetry.cpuUsage || "0"}}% is used.</div>
<div *ngIf="!telemetry.ramUsage" class="h4 secondary-text">No data.</div>
</div>
</div>
</fuse-widget>
<!-- / WIDGET 2 -->

<!-- WIDGET 3 -->
<fuse-widget [@animate]="{value:'*',params:{y:'100%'}}" class="widget" fxLayout="column"
>
<div class="fuse-widget-front">
<div class="pl-16 pr-8 py-16 h-52" fxLayout="row" fxLayoutAlign="space-between center">
<div class="h2">RAM</div>
</div>
<div class="pt-8 pb-32" fxLayout="column" fxLayoutAlign="center center">
<ngx-charts-pie-chart
[scheme]="ramPlot.scheme"
[results]="ramPlot.devices"
[doughnut]="true" class="fixWidthandHeight">
</ngx-charts-pie-chart>
</div>
<div class="p-16 border-top" fxLayout="row" fxLayoutAlign="start center">
<div *ngIf="telemetry.ramUsage" class="h4 secondary-text">{{telemetry.ramUsage | number:'1.0-0'}}MB of {{element.ramTotal | number:'1.0-0'}}MB used.</div>
<div *ngIf="!telemetry.ramUsage" class="h4 secondary-text">No data.</div>
</div>
</div>
</fuse-widget>
<!-- / WIDGET 3 -->

<!-- WIDGET 4 -->
<fuse-widget [@animate]="{value:'*',params:{y:'100%'}}" class="widget" fxLayout="column"
>
<div class="fuse-widget-front">
<div class="pl-16 pr-8 py-16 h-52" fxLayout="row"
fxLayoutAlign="space-between center">
<div class="h2">DISK</div>
</div>    
<div class="pt-8 pb-32" fxLayout="column" fxLayoutAlign="center center">
<ngx-charts-pie-chart
[scheme]="diskPlot.scheme"
[results]="diskPlot.devices"
[doughnut]="true" class="fixWidthandHeight">
</ngx-charts-pie-chart>
</div>
<div class="p-16 border-top" fxLayout="row" fxLayoutAlign="start center">
<div *ngIf="telemetry.diskUsage" class="h4 secondary-text">{{telemetry.diskUsage | number:'1.0-0'}}MB of {{element.diskTotal | number:'1.0-0'}}MB used.</div>
<div *ngIf="!telemetry.diskUsage" class="h4 secondary-text">No data.</div>
</div>
</div>
</fuse-widget>
</div>
</div>
</div>
</div>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let element; columns: displayedColumns;"
class="example-element-row"
[class.example-expanded-row]="expandedElement === element"
(click)="expandedElement = expandedElement === element ? null : element; getTelemetryData(element._id)">
</tr>
<tr mat-row *matRowDef="let element; columns: ['expandedDetail']" class="example-detail-row"></tr>
</table>
<mat-paginator #paginator [pageSize]=50 [pageSizeOptions]="[5, 10, 20, 50]" showFirstLastButtons>
</mat-paginator>

和我的ts:


import { Component, OnInit, OnDestroy, ViewEncapsulation, ViewChild } from '@angular/core';
import { MatSort, Sort } from '@angular/material/sort';
import { DeviceModel } from '../device.model';
import { Subject, Subscription, fromEvent, merge, interval, Observable, of, } from 'rxjs';
import { ProvisionedService } from './provisioned.service';
import { fuseAnimations } from '@fuse/animations';
import { Router } from '@angular/router';
import { MatTableDataSource } from '@angular/material';
import {animate, state, style, transition, trigger} from '@angular/animations';
import {Telemetry} from '../detail/telemetry/telemetry.model'


@Component({
selector: 'app-provisioned-list',
templateUrl: './provisioned-list.component.html',
styleUrls: ['./provisioned-list.component.scss'],
encapsulation: ViewEncapsulation.None,
animations: [
trigger('detailExpand', [
state('collapsed', style({height: '0px', minHeight: '0',  visibility: 'hidden'})),
state('expanded', style({height: '*',  visibility: 'hidden'})),
transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
]),
fuseAnimations],
})
export class ProvisionedListComponent implements OnInit, OnDestroy {
confirmDialogRef: MatDialogRef<FuseConfirmDialogComponent>;
DeviceErrorMessage: string;
deviceList: DeviceModel[];
displayedColumns: string[] = ['checkbox', 'online', 'deviceName', 'tags', 'actualVersion', 'status', 'runningApps', 'appVersion', 'lastSeen', 'detail'];
deviceErrorMessage: string;
checkboxes: {};
private _unsubscribeAll: Subject<any>;
devices: any;
selectedDevices: any[];
private deviceListChange: Subscription = null;
hasSelectedDevices: boolean;
getDevicesInfoLoop: any;
dataSource = new MatTableDataSource(this.deviceList);
tagsInput: string[];
readonly separatorKeysCodes: number[] = [ENTER, COMMA];
@ViewChild(MatSort, { static: true }) sort: MatSort;
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
visible = true;
selectable = true;
removable = true;
telemetry: Telemetry;
ramTotal: number;
diskTotal: number;
cpuPlot: any;
ramPlot: any;
diskPlot: any;
uptimeInterval: any;
uptimeDisplay: string;
deviceId: string;

constructor(private provisionedService: ProvisionedService,
private router: Router, private detailService: DetailService,
private storageService: StorageService, private snackMessageService: SnackMessageService,  public matDialog: MatDialog) {
this._unsubscribeAll = new Subject();
this.dataSource = new MatTableDataSource();
this.selectedDevices = [];
this.telemetry = {
_id: "", 
deviceId: "", 
cpuTemperature: null,
cpuUsage: null,
diskUsage: null,
ramUsage: null,
storeDate: new Date(0),
utc: new Date(0),
upSince: new Date(0)
}
this.recalculatePlots(this.deviceId);
}
ngOnDestroy(): void {
this.deviceListChange.unsubscribe();

}
ngOnInit(): void {
this.getDeviceList();
this.getTelemetryData(this.deviceId);

this.dataSource.paginator = this.paginator;
if (this.storageService.retrieveListPagination()) {
this.paginator = JSON.parse(this.storageService.retrieveListPagination());
}

public goToDeviceDetails(deviceId): void {
this.router.navigate(['/devices/detail'], { queryParams: { device: deviceId._id } });
}

public getTelemetryData(deviceId): void{
this.provisionedService.getDeviceTelemetry(deviceId).then(telemetry=>{
this.telemetry = telemetry;
this.recalculatePlots(deviceId);
}).catch(err=>{});
}

private recalculatePlots(deviceId): void{
this.dataSource.data.forEach(element=> {
if (!this.telemetry.cpuUsage){
this.cpuPlot = {
scheme : {
domain: ["#039be5", "gainsboro"]
},
devices: [
{
"name"  : "Used",
"value" : 0
},
{
"name"  : "Available",
"value" : 100
}
]
};

} else {
this.cpuPlot = {
scheme : {
domain: ["#039be5", "gainsboro"]
},
devices: [
{
"name"  : "Used",
"value" : this.telemetry.cpuUsage
},
{
"name"  : "Available",
"value" : 100 - this.telemetry.cpuUsage
}
]
};
}

if (!this.telemetry.ramUsage || !element.ramTotal){
this.ramPlot = {
scheme : {
domain: ["#039be5", "gainsboro"]
},
devices: [
{
"name"  : "Used",
"value" : 0
},
{
"name"  : "Available",
"value" : 100
}
]
};
} else {
this.ramPlot = {
scheme : {
domain: ["#039be5", "gainsboro"]
},
devices: [
{
"name"  : "Used",
"value" : (this.telemetry.ramUsage / element.ramTotal) * 100
},
{
"name"  : "Available",
"value" : 100 - (this.telemetry.ramUsage / element.ramTotal) * 100
}
]
};
}

if (!this.telemetry.diskUsage || !element.diskTotal) {
this.diskPlot = {
scheme : {
domain: ["#039be5", "gainsboro"]
},
devices: [
{
"name"  : "Used",
"value" : 0
},
{
"name"  : "Available",
"value" : 100
}
]
};
} else {
this.diskPlot = {
scheme : {
domain: ["#039be5", "gainsboro"]
},
devices: [
{
"name"  : "Used",
"value" : (this.telemetry.diskUsage / element.diskTotal) * 100
},
{
"name"  : "Available",
"value" : 100 - (this.telemetry.diskUsage / element.diskTotal) * 100
}
]
};
}
console.log(this.cpuPlot, this.ramPlot, this.diskPlot )
})
}

}

recalculatePlots()方法是异步触发的。因此,当在DOM中渲染图时,ramPlotdiskPlotcpuPlot中的任何一个都可能是未定义的。尝试访问它们上的属性devices会引发undefined错误。

然而,数据会被快速提取,变量也会被正确定义。因此,当错误也显示出来时,该图已初始化。

要解决此问题,可以在访问属性时在HTML模板中使用安全导航运算符?.。它确保在尝试访问变量的属性之前定义变量。

<ngx-charts-pie-chart [scheme]="cpuPlot?.scheme" [results]="cpuPlot?.devices" [doughnut]="true" class="fixWidthandHeight">
</ngx-charts-pie-chart>
...
<ngx-charts-pie-chart [scheme]="ramPlot?.scheme" [results]="ramPlot?.devices" [doughnut]="true" class="fixWidthandHeight">
</ngx-charts-pie-chart>
...
<ngx-charts-pie-chart [scheme]="diskPlot?.scheme" [results]="diskPlot?.devices" [doughnut]="true" class="fixWidthandHeight">
</ngx-charts-pie-chart>

请注意devices属性之前的?.运算符。

最新更新