Problems writing to a nested array of objects in Angular - javascript

I have a model component that defines the data structure that I'm using. I then use that model in another component and try to write data to it. I'm having challenges writing to the 'nested' object array in my data structure. I'm pretty sure it's because I haven't declared the nested array correctly (instantiated it) but can't quite figure out how to do it properly. No question it's a limitation in my knowledge but after thrashing on it for several hours I figured I'd reach out for help. Any guidance would be appreciated.
Simplified stackblitz below...
https://stackblitz.com/edit/angular-bgk5fo
export class Adventure {
id ? : string;
companylogo: string;
guideavatar: string;
quizavatar: string;
active: boolean;
treasureimage: string;
poi: [{
completedflag: boolean;
firsthint: string;
secondhint: string;
thirdhint: string;
quizanswer: string;
quizfail: string;
quizoptionone: string;
quizoptiontwo: string;
quizoptionthree: string;
quizquestion: string;
quizsuccess: string;
sortorder: number;
target: string;
}];
}
import {
Component
} from '#angular/core';
import {
DataModel
} from './data.model'
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
name = 'Angular';
testModel = new DataModel();
createArray() {
this.testModel.firstname = 'Jack';
this.testModel.lastname = 'Brown';
this.testModel.pet[0].petalive = true;
this.testModel.pet[0].petname = 'Whiskers';
this.testModel.pet[0].pettype = 'Cat'
}
}

there are some fundamental issues with your code.
I believe you want to make use of DataModel as for structural typing. Therefore, if you wanna define a class/interface for type checking, your data.model.ts should be defined this way:
export interface DataModel {
firstname: string;
lastname: string;
pet: Array<Pet>;
}
interface Pet {
petname: string;
pettype: string;
petalive: boolean;
}
On your component.ts, you will need to initialise and define your testModel (of type DataModel). On your createArray() method, you can simply assign the properties this way:
testModel: DataModel = {
firstname: undefined,
lastname: undefined,
pet: []
};
createArray(){
this.testModel.firstname = 'Jack';
this.testModel.lastname = 'Brown';
this.testModel.pet.push({
petalive: true,
petname: 'Whiskers',
pettype: 'Cat'
});
console.log(this.testModel);
}

This is a solution with array is type any
https://stackblitz.com/edit/angular-shotgr?file=src/app/app.component.ts
Hope that it's help for you
Result

Related

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>
);
}

What is difference between def interface and dto inerface in Angular?

I am working on the project, which was started by someone else. There are two interface files in the model folder def and dto. The difference between def and dto interface files is not clear to me. Could any expereince developer let me know what is the difference and when and how to use dto instead of def and viceversa. Thanks in advance.
vendor-def.interface.ts:
import { SourceType, VendorType } from '../shared/enums/vendor-type.enum';
export interface VendorDef {
vendorId: string;
companyCode: string;
name: string;
acronym: string;
alias: string;
legalId: string;
vendorType: VendorType;
sourceType: SourceType;
fiscalCode: string;
}
export interface VendorFormDef {
sourceType: SourceType;
companyCode?: string;
previousMainCompany?: string;
}
export interface InUsageDef {
acronym: boolean;
legalId: boolean;
fiscalCode: boolean;
}
vendor-dto.interface.ts
import { SourceType, VendorType } from '../shared/enums/vendor-type.enum';
export interface VendorDto {
data: VendorDataDto[] | VendorDataDto;
errors?: VendorErrorsDto;
}
export interface VendorDataDto {
attributes: VendorAttributesDto;
id: string;
}
export interface VendorErrorsDto {
code: string;
title: string;
detail: string;
}
export interface VendorCreateDto {
companyCode: string;
name: string;
acronym: string;
legalId: string;
fiscalCode: string;
vendorType: VendorType;
sourceType: SourceType;
}
Basically, it's used to separate what your API gives you from the objects you will manipulate.
VendorDTO is your API response (hence the presence of the data and errors fields)
VendorDef is the definition of the object you will manipulate in your app.
It is common to have a transformer from VendorDTO to VendorDef for when you request the data and a transformer from VendorDef to VendorDTO for when you want to push an addition/update on your API.
It is not restricted to Typescript or Angular, so you might want to check your question's tags.

Display an array of data from function in Angular

My goal is to display a cross or a check according to the vote.result data from the polls.
I had to use Angular only few times and I feel pretty lost honestly.
TS file (angular) :
#Component({
selector: 'app-deck-card',
templateUrl: './deck-card.component.html',
styleUrls: ['./deck-card.component.scss'],
})
export class DeckCardComponent implements OnInit {
#Input() isAnim: boolean;
#Input() inGame: boolean;
#Input() editMode: boolean;
#Input() readOnly: boolean;
#Input() deckIsBase: boolean;
#Input() card: CardDto;
#Input() polls: PollDto[];
#Input() isSearch: boolean;
#Input() isImport: boolean;
#Input() idDeck: number;
#Input() editRight: boolean;
#Output() changeVote = new EventEmitter<number>();
#Output() deleteEvent = new EventEmitter<number>();
#Output() duplicateEvent = new EventEmitter<CardDto>();
#Output() importEvent = new EventEmitter<CardDto>();
#Output() sharedToCommunityEvent = new EventEmitter<CardDto>();
safeIcon: SafeUrl | string;
votes: VoteDto[];
constructor(private readonly authState: AuthState,
private sanitizer: DomSanitizer) {
}
ngOnInit(): void {
this.safeIcon = this.sanitizer.bypassSecurityTrustUrl(this.card?.theme?.icon);
this.votes = this.polls?.find(p => p.card.id === this.card?.id)?.votes;
}
/**
* Emit the card ID to delete the card
* #return void
*/
deleteCard(): void {
this.deleteEvent.emit(this.card.id);
}
showTheResult(): string {
console.log(this.polls);
console.log(this.votes);
this.polls?.forEach(vote => {
if (vote.voted && vote.result == false) {
// display a mat-icon cross
console.log(vote)
return '<mat-icon>clear</mat-icon>'
} else if (vote.voted && vote.result == true) {
// display a mat-icon check
console.log(vote)
return '<mat-icon>done</mat-icon>'
}
});
return '';
}
}
My 2 console.log in showTheResult() are always undefined.
So, obviously, the console log in the if condition are never reached.
HTML file :
<div class="card-body" [class.card-body-readOnly]="readOnly">
<p class="main-text" [class.readOnly]="readOnly" [class.short]="inGame && isAnim"
[class.long]="!editMode && !isAnim">{{card?.text}}</p>
<p>{{showTheResult()}}</p>
<p>DISPLAY HERE THE MAT-ICON</p>
<span *ngIf="isAnim || editMode" class="sub-text">#{{card?.id}}</span>
</div>
can someone show me the way ?
The DTOs look like this:
export interface PollDto {
id: number;
result: boolean;
voted: boolean;
priority: number;
card: CardDto;
votes: VoteDto[];
}
export interface VoteDto {
participantId: number;
participantName?: string;
pollId: number;
result: boolean;
}
since your this.polls is an #Input(), you don't know if this variable is actually loaded when you reach ngOnInit lifecycle.
When working with #Input data, if you want to catch the moment data is loaded, you should watch the changes :
https://ultimatecourses.com/blog/detect-input-property-changes-ngonchanges-setters
ngOnChanges(changes: SimpleChanges) {
console.log(changes);
}
This way, you will see if ever your data are loaded, if not, that means the problem is in the parent container component.
Also, a quick note : I don't think you should return HTML in your
method, you probably want to handle this another way, with a directive
or something, this would not be a good practice.
Cheers ! :)

JavaScript - How to show interface suggestions for every typing

Hello I'm newbie at JavaScript and I wan to create a interface but the interface's variable's name is not gonna be a fix name. So the Interface have to return the suggestions for every name that I write.
Inside of my Interface file:
export interface ArchiveData {
id: string;
latest_reel_media: number;
seen?: any;
...
}
export interface Reels {
anyArchiveName: ArchiveData;
}
export interface ArchivedStoryDataResponse {
reels: Reels;
status: string;
}
The result with fixed name:
The result with another name; no suggestions:
Well, it's been a long time but I finally reached my goal 😅
export interface ArchiveData {
id: string;
latest_reel_media: number;
seen?: any;
...
}
export interface Reels {
[reel_id:string]: ArchiveData;// this line
}
export interface ArchivedStoryDataResponse {
reels: Reels;
status: 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