Property 'does not exist on type 'DishdetailComponent' - javascript

I'm using angular 9 and i got an error.
ERROR in src/app/dishdetail/dishdetail.component.ts:81:9 - error TS2339: Property 'comment' does not exist on type 'DishdetailComponent'.
I want to show a real-time preview of the comment on the page. Here is my code.
dishdetail.component.ts
import { Component, OnInit, ViewChild } from '#angular/core';
import { Params, ActivatedRoute } from '#angular/router';
import { Location } from '#angular/common';
import { Dish } from '../shared/dish';
import { DishService } from '../services/dish.service';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
import { Review } from '../shared/review';
import { Comment } from '../shared/comment';
#Component({
selector: 'app-dishdetail',
templateUrl: './dishdetail.component.html',
styleUrls: ['./dishdetail.component.scss']
})
export class DishdetailComponent implements OnInit {
reviewForm: FormGroup;
review: Review;
#ViewChild('rvwform') reviewFormDirective;
dish: Dish;
formErrorss = {
'author': '',
'comment': ''
};
validationMessagess = {
'author': {
'required': 'Name is required.',
'minlength': 'Author name must be at least 2 characters long.'
},
'comment': {
'required': 'Comment is required.',
'minlength': 'Comment must be at least 5 characters long.'
}
}
constructor(private dishService: DishService, private location: Location, private route: ActivatedRoute, private rf: FormBuilder) {
this.createReview();
}
createReview(){
this.reviewForm = this.rf.group({
'author': ['', [Validators.required, Validators.minLength(2)] ],
'comment': ['', [Validators.required, Validators.minLength(5)] ]
});
this.reviewForm.valueChanges
.subscribe(data => this.onValueChanged(data));
this.onValueChanged();
}
onValueChanged(data?: any) {
if (!this.reviewForm) { return; }
const form = this.reviewForm;
for (const field in this.formErrorss) {
if (this.formErrorss.hasOwnProperty(field)) {
// clear previous error message (if any)
this.formErrorss[field] = '';
const control = form.get(field);
if (control && control.dirty && !control.valid) {
const messages = this.validationMessagess[field];
for (const key in control.errors) {
if (control.errors.hasOwnProperty(key)) {
this.formErrorss[field] += messages[key] + ' ';
}
}
}
}
}
}
onSubmit(reviewForm) {
this.review = this.reviewForm.value;
this.comment.date = new Date().toISOString();
console.log(this.review);
this.dish.comments.push(this.comment)
this.reviewForm.reset({
author: '',
rating: 5,
comment: ''
});
this.reviewFormDirective.resetForm();
}
ngOnInit(): void {
let id = this.route.snapshot.params['id'];
this.dishService.getDish(id)
.subscribe(dish => this.dish = dish);
}
goBack(): void {
this.location.back();
}
}
Here is my code for Review.ts
Review.ts
export class Review{
author: string;
rating: number;
comment: string;
}
enter image description here
Upon submitting the valid comment, the comment should join the regular comments on the page. But im face Property 'comment' does not exist on type 'DishdetailComponent'. Please guide me. Also please check i'm updated the question.
dish.ts
import { Comment } from './Comment';
export class Dish {
id: string;
name: string;
image: string;
category: string;
featured: boolean;
label: string;
price: string;
description: string;
comments: Comment[];
}
This is dishdetails.ts
dishdetails.ts
export class Dishdetails{
id: string;
name: string;
image: string;
category: string;
featured: boolean;
label: string;
price: string;
description: string;
rating: number;
comment: string;
author: string;
date: string;
}
This is comment.ts
comment.ts
export class Comment {
rating: number;
comment: string;
author: string;
date: string;
}
Please check below image for error in my command line.

You are using "this.comment.date" inside OnSubmit(), but "comment" is not defined.
First declare comment.
Or it looks like you want to do this (as date is not used):
this.review = this.reviewForm.value;
//this.comment.date = new Date().toISOString(); //Comment this
console.log(this.review);
this.dish.comments.push(this.review) //changed from this.comment
Additionally, If you're looking to add date in comment, then I suggest to change -
class Review{
author: string;
rating: number;
comment: string;
}
TO
class Review{
author: string;
rating: number;
comment: string,
date: string
}

Related

Angular showing error: Type '{ }[]' is not assignable to type '[{ }]'

I need some help and explanation with the error I am receiving in my app... I get a JSON from an API that gives me some data and that data has an array products. On click I want to copy these products(izdelki) from this array to a new empty array and send it over an API call to the backend.
But I have a problem with getting the products from this array I receive. My code is returning me this error:
error TS2322: Type '{ sifra: string; naziv: string; kolicina: number; ean: string; em: string; cena: number; rabat1: number; rabat2: number; prednarocilo: number; ismail: number; }[]' is not assignable to type '[{ sifra: string; naziv: string; kolicina: number; ean: string; em: string; cena: number; rabat1: number; rabat2: number; prednarocilo: number; ismail: number; }]'.
[ng] Target requires 1 element(s) but source may have fewer.
[ng]
[ng] 38 this.orderProducts = data.map(this.order['izdelki']);
I am new to angular and Arrays are giving me some trouble :)
single-order.ts code:
export interface SingleOrder {
id: number;
datum: string;
datum_dobave: string;
dostava: number;
g_popust: number;
opomba: string;
predkoci1narocilo: number;
kc: number;
prevoznik: string;
narocilnica: string;
narocilnicadate: string;
izdelki: {
sifra: string;
naziv: string;
kolicina: number;
ean: string;
em: string;
cena: number;
rabat1: number;
rabat2: number;
prednarocilo: number;
ismail: number;
}[];
}
Service to get the single order:
getSingleOrder(id: number): Observable<SingleOrder[]> {
return from(Preferences.get({ key: 'TOKEN_KEY' })).pipe(
switchMap(token => {
const headers = new HttpHeaders().set('Authorization', `Bearer ${token.value}`);
return this.httpClient.get<SingleOrder[]>(`${environment.apiUrl}customer/orders/${id}`, { headers, observe: 'response' });
}),
catchError(err => {
console.log(err.status);
if (err.status === 400) {
console.log(err.error.message);
}
if (err.status === 401) {
this.authService.logout();
this.router.navigateByUrl('/login', { replaceUrl: true });
}
return EMPTY;
}),
map(res => res.body)
);
};
Here is my order-view.page.ts code:
export class Izdelki {
sifra: string;
naziv: string;
kolicina: number;
ean: string;
em: string;
cena: number;
rabat1: number;
rabat2: number;
prednarocilo: number;
ismail: number;
}
#Component({
selector: 'app-order-view',
templateUrl: './order-view.page.html',
styleUrls: ['./order-view.page.scss'],
})
export class OrderViewPage implements OnInit, OnDestroy {
order: SingleOrder[];
// orderProducts: SingleOrder['izdelki'][];
orderProducts: SingleOrder['izdelki'][];
repeatOrderArr: Izdelki[];
private orderSubscription: Subscription;
constructor(
private route: ActivatedRoute,
private customerService: CustomerService,
) { }
ngOnInit() {
this.getOrder();
}
getOrder() {
const id = Number(this.route.snapshot.paramMap.get('id'));
this.orderSubscription = this.customerService.getSingleOrder(id).subscribe(
data => {
this.order = data;
console.log('Order data:', this.order);
this.orderProducts = data.map(this.order['izdelki']);
},
error => {
console.log('Error', error);
});
}
repeatThisPurchase() {
this.repeatOrderArr= [...this.orderProducts];
console.log(this.repeatOrderArr);
}
ngOnDestroy(): void{
this.orderSubscription.unsubscribe();
}
}
Here is an image of console.log(data) so you can see whats inside the JSON response:
HTML file code:
<ion-button color="vigros" class="purchase-btn" size="default" type="submit" (click)="repeatThisPurchase()" expand="block">Ponovi nakup</ion-button>
let say izdelki is a class
export class Izdelki {
sifra: string;
naziv: string;
kolicina: number;
ean: string;
em: string;
cena: number;
rabat1: number;
rabat2: number;
prednarocilo: number;
ismail: number;
}
so inSingleORder you declared izdelki with type [Izdelki]
export interface SingleOrder {
izdelki: [Izdelki]
}
but in your subscribe you used it directly like if izdelki is of type Izdelki
So SingleOrder izdelki became
export interface SingleOrder {
izdelki: Izdelki
}
or if izdelki is an array
export interface SingleOrder {
izdelki: Izdelki[]
}
To solve your issue you have to
declare SingleOrder izdelki with the type Izdelki
declare orderProducts as an array of SingleOrder['izdelki']
orderProducts: SingleOrder['izdelki'][];
You have inversed the declaration of your array. Start by declaring the type of the array fist, like :
export interface SingleOrder {
...
izdelki: {
sifra: string;
naziv: string;
kolicina: number;
ean: string;
em: string;
cena: number;
rabat1: number;
rabat2: number;
prednarocilo: number;
ismail: number;
}[];
}

How to use Typescript and Interfaces when passing props down through React components?

I'm passing down a pretty large array of objects through several React components. I was wondering, what is the syntax for writing the types of all the properties in each object (the objects are nested several times)?
I currently have interfaces like below. These are two components, MainContent, which passes props down into Chart:
MainContent component:
interface ComparatorTypes {
id: string;
name: string;
}
interface DataTypes {
jobId: string;
jobTitle: string;
descriptionUrl: string;
totalCompensation: number;
baseSalary: number;
longevityPay: number;
specialPay: number;
allowances: number;
paidTimeOff: number;
holidays: number;
retirementBenefit: Array<{
formula: string;
details: any;
}>;
healthBenefit: Array<{
premium: number;
details: any;
}>;
remoteWork: {
isAllowed: string;
details: any;
};
}
interface QueryTypes {
agencyName: string;
id: string;
data: DataTypes[];
}
interface params {
comparatorData: ComparatorTypes[];
queryData: QueryTypes[];
}
export default function MainContent({ comparatorData, queryData }: params) {
return (
<S.MainContentComponent>
<Header />
<Summary comparatorData={comparatorData} />
<Chart queryData={queryData} />
</S.MainContentComponent>
);
}
and Chart component:
interface ComparatorTypes {
id: string;
name: string;
}
interface DataTypes {
jobId: string;
jobTitle: string;
descriptionUrl: string;
totalCompensation: number;
baseSalary: number;
longevityPay: number;
specialPay: number;
allowances: number;
paidTimeOff: number;
holidays: number;
retirementBenefit: Array<{
formula: string;
details: any;
}>;
healthBenefit: Array<{
premium: number;
details: any;
}>;
remoteWork: {
isAllowed: string;
details: any;
};
}
interface QueryTypes {
agencyName: string;
id: string;
data: DataTypes[];
}
interface params {
// comparatorData: ComparatorTypes[];
queryData: QueryTypes[];
}
export default function Chart({ queryData }: params): JSX.Element {
...
You can see how redundant it is to be naming these giant, several-times-nested interfaces before every component that uses this array of objects. Is this normal for Typescript? Is there a better way to do something like this? Or does all this data need to be typed upon being passed down through every component?
What forces you to define these identical interfaces explictly for each component?
On the contrary, factorizing them would be the normal choice: that way, they are defined in a single place (single source of truth), and by importing them, you explictly say that you re-use the exact same types.
// Chart.tsx
export interface QueryTypes {
agencyName: string;
id: string;
data: DataTypes[];
}
export interface DataTypes {
jobId: string;
jobTitle: string;
// etc.
}
export default function Chart({
queryData
}: {
queryData: QueryTypes[];
}) {}
// Main.tsx
import Chart, { QueryTypes } from ".Chart";
import Summary, { ComparatorTypes } from "./Summary"; // Same for ComparatorTypes
export default function MainContent({
comparatorData,
queryData
}: {
comparatorData: ComparatorTypes[];
queryData: QueryTypes[];
}) {
return (
<S.MainContentComponent>
<Header />
<Summary comparatorData={comparatorData} />
<Chart queryData={queryData} />
</S.MainContentComponent>
);
}

Validation pipe works with one value type but not several types at the same time

I created 3 DTOs which extends from another parent DTO and then in the controller I use class-validator library to validate the data the user pass to the controller.
parent.dto.ts
import { IsNotEmpty, IsString, IsDateString, IsMongoId } from 'class-validator';
export class Parent {
#IsNotEmpty()
#IsMongoId()
platform: string;
#IsNotEmpty()
#IsString({ each: true })
admins: string[];
#IsDateString()
purchaseDate: Date;
#IsDateString()
validFrom: Date;
#IsDateString()
validTo: Date;
}
a.dto.ts
import { IsMongoId, IsNotEmpty, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
import { Parent } from './parent.dto';
class A_options {
#IsNotEmpty()
#IsMongoId()
dataA: string;
}
export class A extends Parent {
#IsNotEmpty()
testA: string;
#ValidateNested()
#Type(() => A_options)
data: A_options;
}
b.dto.ts
import { IsMongoId, IsNotEmpty, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
import { Parent } from './parent.dto';
class B_options {
#IsNotEmpty()
#IsMongoId()
dataB: string;
}
export class B extends Parent {
#IsNotEmpty()
testB: string;
#ValidateNested()
#Type(() => B_options)
data: B_options;
}
c.dto.ts
import { IsMongoId, IsNotEmpty, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
import { Parent } from './parent.dto';
class C_options {
#IsNotEmpty()
#IsMongoId()
dataC: string;
}
export class C extends Parent {
#IsNotEmpty()
testC: string;
#ValidateNested()
#Type(() => C_options)
data: C_options;
}
And in the controller I'm using ValidationPipe setting body: A
controller.ts
#UsePipes(ValidationPipe)
#Post()
async createItem(#Res() res, #Body() body: A) {
const result = await this.createTest.createObject(body);
return res.status(HttpStatus.OK).json({
message: 'Item has been created successfully',
newLicense,
});
}
}
This works also with body: B and body: C
But it doesn't work when I do body: A | B | C
How can I make it work so the code will be like this?
#UsePipes(ValidationPipe)
#Post()
async createItem(#Res() res, #Body() body: A | B | C) {
const result = await this.createTest.createObject(body);
return res.status(HttpStatus.OK).json({
message: 'Item has been created successfully',
newLicense,
});
}
}
In your case I would simply recommend to implement a IParent interface and extend it in your Parent class.
Then you can use the IParent in your controller, as such:
controller.ts
#UsePipes(ValidationPipe)
#Post()
async createItem(#Res() res, #Body() body: IParent) {
const result = await this.createTest.createObject(body);
return res.status(HttpStatus.OK).json({
message: 'Item has been created successfully',
newLicense,
});
}
parent.interface.ts
export interface IParent {
platform: string;
admins: string[];
purchaseDate: Date;
validFrom: Date;
validTo: Date;
}
parent.class.ts
import { IsNotEmpty, IsString, IsDateString, IsMongoId } from 'class-validator';
export class Parent implements IParent {
#IsNotEmpty()
#IsMongoId()
platform: string;
#IsNotEmpty()
#IsString({ each: true })
admins: string[];
#IsDateString()
purchaseDate: Date;
#IsDateString()
validFrom: Date;
#IsDateString()
validTo: Date;
}
Let me know if it helps; if so validate answer, otherwise please provide a link to a repository with minimal reproduction so we can help further.

Argument of type date is not assignable to parameter of type string

I'm following a Angular and .net course.
I'm trying to set the photourl to a method.
I'm getting the following error\
Argument of type date is not assignable to parameter of type string
The error is on the following line.
this.changeMemberPhoto(this.currentUser.photoUrl);
The argument is the issue
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { BehaviorSubject } from 'rxjs';
import { map } from 'rxjs/operators';
import { JwtHelperService} from '#auth0/angular-jwt';
import { environment } from 'src/environments/environment';
import { User } from '../_models/user';
#Injectable({
providedIn: 'root'
})
export class AuthService {
baseUrl = environment.apiUrl + 'auth/';
jwtHelpter = new JwtHelperService();
decodedToken: any;
currentUser: User;
photoUrl = new BehaviorSubject<string>('../../assets/user.png');
currentPhotoUrl = this.photoUrl.asObservable();
constructor(private http: HttpClient) {}
changeMemberPhoto(photoUrl: string) {
this.photoUrl.next(photoUrl);
}
login(model: any) {
return this.http.post(this.baseUrl + 'login', model).pipe(
map((response: any) => {
const user = response;
if (user) {
localStorage.setItem('token', user.token);
localStorage.setItem('user', JSON.stringify(user.user));
this.decodedToken = this.jwtHelpter.decodeToken(user.token);
this.currentUser = user.user;
this.changeMemberPhoto(this.currentUser.photoUrl);
}
})
);
}
register(model: any) {
return this.http.post(this.baseUrl + 'register', model);
}
loggedIn() {
const token = localStorage.getItem('token');
return !this.jwtHelpter.isTokenExpired(token);
}
}
The code is identical to the instructors, so I don't know what the issue is.
I've included the User class as requested
import { Photo } from './photo';
export interface User {
id: number;
username: string;
knownAs: string;
age: number;
gender: string;
created: Date;
lastActive: Date;
photoUrl: Date;
city: string;
country: string;
interests?: string;
introduction?: string;
lookingFor?: string;
photos?: Photo[];
}
photoUrl is a date in your class and is a string in your changeMemberPhoto method
In User class, change it to :
photoUrl: string;

Cannot find namespace error for model in Angular2/TypeScript

The FeaturedCategories model
export class FeaturedCategories {
categories: Array<{ id: number, title: string, graphic: string, categorycards: Array<{}> }>;
}
Also tried this:
export class FeaturedCategories {
id: number;
title: string;
graphic: string;
categorycards: Object[];
}
The Component
import { Component, ChangeDetectionStrategy, ViewEncapsulation } from '#angular/core';
import { ApiService } from '../shared/services/api.service';
import { FeaturedCategories } from '../shared/models/home/featuredcategories';
#Component({
changeDetection: ChangeDetectionStrategy.Default,
encapsulation: ViewEncapsulation.Emulated,
selector: 'home',
styleUrls: [ './home.component.css' ],
templateUrl: './home.component.html'
})
export class HomeComponent {
testFeaturedCategories: Array<FeaturedCategories>;
constructor(private api: ApiService) {
// we need the data synchronously for the client to set the server response
// we create another method so we have more control for testing
this.universalInit();
}
universalInit() {
console.log('universalInit...')
this.api.getFeatured()
.subscribe(categories => {
console.log('categories', categories);
this.testFeaturedCategories = categories
});
}
}
This will work: testFeaturedCategories: Array<{}>;
However I'm trying to use TypeScript to let my App know what type of model to expect.
This causes the error above:
testFeaturedCategories: FeaturedCategories.categories;
And if I just try this: testFeaturedCategories: FeaturedCategories;
I get a type [{}] is not assignable error.
UPDATE
So I noticed that when I commented out all the keys in my FeaturedCategories model finally the error goes away and
featuredCategories: FeaturedCategories[]; will work.
However now this is just an empty object without keys to expect :(
export class FeaturedCategories {
// id: number;
// title: string;
// graphic: string;
// categorycards: Object[];
}
this is working fine for me.
export class MyComponent {
categories: FeaturedCategories[] = [{
id: 1,
title: "",
graphic: "",
categorycards: [{}]
}];
}
export class FeaturedCategories{
id: number;
title: string;
graphic: string;
categorycards: Object[];
}
My problem was trying to type my Array, instead of just using the Typed objects that exist in the larger Array.
Also had a problem in my service, originally I had this:
/**
* Get featured categories data for homepage
* /wiki
*/
getFeatured(): Observable<[{}]> {
return this.http.get(`${this.getFeaturedUrl}/home`)
// .do(res => console.log('getFeatured res', res.json()))
.map(res => res.json())
.catch(this.handleError);
}
I did not need or could even use a type for my larger Categories array, what I needed was a smaller type for the exact Objects that exist in that larger Array:
export class FeaturedCategory {
id?: number;
type: string;
title: string;
graphic?: string;
video?: string;
categorycards: Array<{}>;
}
So now with the correct Type of Objects inside my Array I added it to the service:
getFeatured(): Observable<[FeaturedCategory]> {
return this.http.get(`${this.getFeaturedUrl}/home`)
.map(res => res.json())
.catch(this.handleError);
}
Now back in my Component I imported the single Typed Object
import { FeaturedCategory } from '../shared/models/home/featuredcategory';
Then typed the variable:
featuredCategories: Array<FeaturedCategory>;
And finally in ngOnInit
ngOnInit() {
this.api.getFeatured()
.subscribe(categories => {
console.log('categories', categories);
this.featuredCategories = categories;
});
}
No more errors :)

Categories

Resources