I'm having a few problems with Angular.
I'm trying to iterate through a JSON Api but I'm receiving the message "Property does not exist on type 'Object'.
Not exactly that, the error is "Property 'sprints' does not exist on type 'Project'"
I have this HTML template:
<mat-toolbar>
<span>{{ currentProject.title }}</span>
</mat-toolbar>
<div >
<mat-card>
<mat-toolbar style="border-radius: 4px 4px 0px 0px;">
<span>Development</span>
</mat-toolbar>
<mat-card-content>
<span>Access Code: {{ currentProject.accessCode }}</span>
<div *ngFor="let sprint of currentProject.sprints"> <---- THIS IS WERE THE ERROR HAPPENS
<span>{{ sprint }}</span>
</div>
</mat-card-content>
</mat-card>
</div>
And my JSON:
{
"id": 1,
"title": "App Komputer",
"description": "Website dedicated to computer related products",
"accessCode": "5128",
"createdAt": "2022-01-13T21:19:11.000Z",
"updatedAt": "2022-01-13T21:19:16.000Z",
"sprints": [{
"id": 1,
"title": "Sprint 1",
"releaseDate": "2022-01-20T21:37:13.000Z",
"description": "Specs up to 01/22/2022",
"createdAt": "2022-01-13T21:37:58.000Z",
"updatedAt": "2021-12-13T01:46:36.000Z",
"projectId": 1,
"specifications": [{
"id": 1,
"title": "Add product button",
"description": "New product button HTML",
"duration": 10,
"status": 1,
"createdAt": "2021-12-23T01:46:36.000Z",
"updatedAt": "2021-12-23T01:46:36.000Z",
"sprintId": 1
}]
}]
}
Also, this is my Component:
constructor(
private projectService: ProjectService,
private route: ActivatedRoute,
private router: Router,
private _titleService: Title
) { }
ngOnInit(): void {
if (!this.viewMode) {
this.message = '';
this.getProject(this.route.snapshot.params["id"]);
}
}
getProject(id: string): void {
this.projectService.get(id)
.subscribe({
next: (data) => {
this.currentProject = data;
console.log(data);
this._titleService.setTitle(data.title ' · Scrumy');
},
error: (e) => console.error(e)
});
}
How can I fix this error? I've tried many things but none worked.
Thank you!
EDIT 01/22/2022
For those who asked, here's the complete scheme of project-details-component.ts, where I get the function from:
import { Component, Input, OnInit } from '@angular/core';
import { ProjectService } from 'src/app/services/project.service';
import { ActivatedRoute, Router } from '@angular/router';
import { Project } from 'src/app/models/project.model';
import { Title } from "@angular/platform-browser";
import { Moment } from 'moment';
import { EChartsOption } from 'echarts';
@Component({
selector: 'app-project-details',
templateUrl: './project-details.component.html',
styleUrls: ['./project-details.component.css']
})
export class ProjectDetailsComponent implements OnInit {
@Input() viewMode = false;
@Input() currentProject: Project = {
title: '',
description: '',
accessCode: ''
};
message = '';
constructor(
private projectService: ProjectService,
private route: ActivatedRoute,
private router: Router,
private _titleService: Title
) { }
ngOnInit(): void {
if (!this.viewMode) {
this.message = '';
this.getProject(this.route.snapshot.params["id"]);
}
}
getProject(id: string): void {
this.projectService.get(id)
.subscribe({
next: (data) => {
this.currentProject = data;
console.log(data);
this._titleService.setTitle(data.title ' · Scrumy');
},
error: (e) => console.error(e)
});
}
}
This is the project.model.ts:
export class Project {
id?: any;
title?: string;
description?: string;
accessCode?: string;
createdAt?: Date;
updatedAt?: Date;
}
CodePudding user response:
Don't you also get errors for title and accessCode properties? Because you are mixing synchronous code with asynchronous and that's usually causing the issue you are facing.
To explain, your template expects that the currentProject is immediately available, which is not true, because you load it from some service. And depending on how long will that take your template wants to draw data from currentProject but it was not yet initialized.
If you don't want to rewrite your code to use async pipe, either put the whole block to *ngIf="!!currentProject", or add question marks before currentProject` properties.
<span>Access Code: {{ currentProject?.accessCode }}</span>
<div *ngFor="let sprint of currentProject?.sprints">
<span>{{ sprint }}</span>
</div>
CodePudding user response:
Comparing your JSON data and Product interface, you have missed out sprints property in your model.
Via json2ts, your Product interface should be as below:
export interface RootObject {
id: number;
title: string;
description: string;
accessCode: string;
createdAt: Date;
updatedAt: Date;
sprints: Sprint[];
}
export interface Sprint {
id: number;
title: string;
releaseDate: Date;
description: string;
createdAt: Date;
updatedAt: Date;
projectId: number;
specifications: Specification[];
}
export interface Specification {
id: number;
title: string;
description: string;
duration: number;
status: number;
createdAt: Date;
updatedAt: Date;
sprintId: number;
}
Another concern is the concern mentioned by @mat, as currentProject data is asynchronous. Either you have to initialize the value to currentProject:
@Input() currentProject: Project = {
title: '',
description: '',
accessCode: '',
sprints: []
};
Or using Typescript optional chaining (?.) to escape error when currentProject was null or undefined.
@Input() currentProject: Project;
<mat-toolbar>
<span>{{ currentProject?.title }}</span>
</mat-toolbar>
<div >
<mat-card>
<mat-toolbar style="border-radius: 4px 4px 0px 0px;">
<span>Development</span>
</mat-toolbar>
<mat-card-content>
<span>Access Code: {{ currentProject?.accessCode }}</span>
<div *ngFor="let sprint of currentProject?.sprints">
<span>{{ sprint | json }}</span>
</div>
</mat-card-content>
</mat-card>
</div>
