#Barchart is not loading and giving undefined back.

154 messages · Page 1 of 1 (latest)

sonic ocean
mild sinew
#

it just keeps returning undefined
what do you mean by "it" here exactly

#

btw, use bigint instead of BigInt for your types.

sonic ocean
#

in the two console logs i do:

console.log(this.totalDebitStatsByCategory);
    console.log(this.totalDebitStatsByCategory.mapToArray());

The first one is giving the object back, but the second is giving undefined back

#

and i need a array to use for the chart

mild sinew
#

pretty sure that function just can't return undefined

sonic ocean
mild sinew
#

you sure it's up to date and not running a stale version of your code?

sonic ocean
#

i did reload the server: ng serve

#

stopped it and started it

mild sinew
#

and everything's saved?

sonic ocean
#

yess

mild sinew
#

wait, that looks like it'd error

#

wait, what?

#

im getting very confused looking at that log lol

#

what browser are you using?

#

i think i might be reading the log incorrectly

sonic ocean
#

chrome

mild sinew
#

ok yeah no you're calling the wrong thing then im pretty sure

sonic ocean
#

where?

mild sinew
#

not sure but a lot of things aren't lining up in the log

sonic ocean
#

like?

#

vsCode doenst have pair programming? mayby its easier te read then pastebin

mild sinew
#

your class name is TotaldebitStatsByCategory, but the log has the D in Debit capitalized
the total property is missing, and there are a lot of other properties
it's nested 2 times, which shouldn't be happening given the structure you have in code

mild sinew
#

you know the structure of your project, i don't

sonic ocean
#

both the class and the assignment is with lower d

mild sinew
#

but the log has an uppercase d, that's gotta be coming from somewhere

sonic ocean
#

now i capitalized it, still the same

mild sinew
#

can you check in the [[Prototype]] fields in the log, what does it say on constructor

sonic ocean
mild sinew
#

what about in TotalDebitStatsByCategory (you don't have to expand constructor by the way, just the name is enough)

sonic ocean
mild sinew
#

yeah so that's a plain object

#

it should be erroring that you can't call undefined, rather than returning undefined

sonic ocean
#
  public getTotalDebitStatsByCategory(): Observable<TotalDebitStatsByCategory> {
    return this.http.get<TotalDebitStatsByCategory>(this.expenseDebitbycategoryUrl);
  }
#

this is the method from wich i reviece backend

mild sinew
#

unless angular just nukes the class, but that sounds broken

sonic ocean
#

no error, just undefined in console

mild sinew
#

im so confused by this behavior

#

could you try adding console.logs to the TotaldebitStatsByCategory constructor and method to see if they're being called

sonic ocean
mild sinew
#

...so it's constructed but the method isn't called?

sonic ocean
#

the constructor is being called

#

in the method is not

mild sinew
#

could you try logging

  • this.totalDebitStatsByCategory.mapToArray (without the call)
  • this.totalDebitStatsByCategory.constructor
  • this.totalDebitStatsByCategory.constructor.prototype.mapToArray
sonic ocean
#

in the private updateChartOptions?

mild sinew
#

yeah

sonic ocean
#
console.log(this.totalDebitStatsByCategory);
    console.log(this.totalDebitStatsByCategory.mapToArray);
    console.log(this.totalDebitStatsByCategory.constructor);
    console.log(this.totalDebitStatsByCategory.constructor.prototype.mapToArray);
mild sinew
#

....so it's getting reassigned then

#

oh damn it is

sonic ocean
#
export class TotaldebitbarchartComponent implements OnInit {
  chartOptions: any = {};
  totalDebitStatsByCategory = new TotalDebitStatsByCategory();

  constructor(private expenseService: ExpenseService) {}

  ngOnInit() {
    this.expenseService.getTotalDebitStatsByCategory().subscribe(data => {
      this.totalDebitStatsByCategory = data;
      this.updateChartOptions();
    });
  }

  private updateChartOptions() {
    console.log(this.totalDebitStatsByCategory);
    console.log(this.totalDebitStatsByCategory.mapToArray);
    console.log(this.totalDebitStatsByCategory.constructor);
    console.log(this.totalDebitStatsByCategory.constructor.prototype.mapToArray);
    this.chartOptions = {
      title: {
        text: "Uitgave per categorie"
      },
      animationEnabled: true,
      axisY: {
        includeZero: true,
        suffix: "Euro"
      },
      data: [{
        type: "bar",
        indexLabel: "{y}",
        yValueFormatString: "#,### Euro",
        dataPoints: this.totalDebitStatsByCategory.mapToArray
      }]
    };
  }
}
mild sinew
#
    this.expenseService.getTotalDebitStatsByCategory().subscribe(data => {
      this.totalDebitStatsByCategory = data;
      this.updateChartOptions();
    });
sonic ocean
#

yes data is the thing i get back from the backend

mild sinew
#

yeah you're overwriting your TotalDebitStatsByCategory with that plain object

sonic ocean
#

data is the object i get from the backend

mild sinew
#

exactly, it's a plain object

#

you no longer have the class methods, because the class instance is gone

sonic ocean
#

so how can i assign the plain object to the object i need?

mild sinew
#

you don't

#

is your class TotaldebitStatsByCategory just that or are there other properties omitted? just to get an idea of your current structure

sonic ocean
#

its just that

#
export class TotalDebitStatsByCategory {
    total: Map<string, bigint>;

    constructor() {
        console.log("calling from the constructor")
        this.total = new Map<string, bigint>();
    }

    mapToArray(): { label: string, y: number }[] {
        console.log("calling from the MapToArray method")
        return Array.from(this.total.entries()).map(([label, y]) => ({ label, y: Number(y) }));
    }
}
mild sinew
#

aight so you can either take the object and store that in here or the main class, then have this class read from the object, or you can take the data inside and store it here rather than storing the object itself

sonic ocean
#

hmm i dont understand that completely

mild sinew
#

oh also another approach you could store that object and have a method for processing the object in your main class without using this class

mild sinew
# sonic ocean hmm i dont understand that completely

(just showing structure)
first option:

class TotalDebitBarChartComponent {
  rawData // (a)
  statsByCategory

  subscribe(data) {
    rawData = data // (a)
    statsByCategory.rawData = data // (b)
  }

  update() {
    statsByCategory.read(rawData) // (a)
    statsByCategory.getData() // (b)
  }
}

class TotalDebitStatsByCategory {
  rawData // (b)
}
```second option:```ts
class TotalDebitBarChartComponent {
  statsByCategory

  subscribe(data) {
    statsByCategory.parse(data)
  }

  update() {
    statsByCategory.getData()
  }
}

class TotalDebitStatsByCategory {
  data // parsed into map
}
```third option:```ts
class TotalDebitBarChartComponent {
  rawData // or parsed

  subscribe(data) {
    rawData = data
  }

  update() {
    parse(rawData) // or use directly if already parsed
  }
}
sonic ocean
#

And how can i parse the data of the plain object to a array?

mild sinew
#

the keys are the categories and they're unknown, right?

sonic ocean
#

yess

#

some like:

function parseToObjectArray(data: { [key: string]: number }): { label: string, y: number }[] {
        const dataArray: { label: string, y: number }[] = [];
      
        for (const key in data) {
          if (data.hasOwnProperty(key)) {
            const value = data[key];
            dataArray.push({ label: key, y: value });
          }
        }
      
        return dataArray;
      }
```?
mild sinew
#

sure, that could work

sonic ocean
#

it was a older thingy i tries

#

tried

mild sinew
#

you just need to loop through the object, that would be a for..in, or looping through Object.keys/values/entries

sonic ocean
#
@Component({
  selector: 'app-totaldebitbarchart',
  standalone: true,
  imports: [
    CommonModule, RouterOutlet, CanvasJSAngularChartsModule
  ],
  providers: [ ExpenseService],
  templateUrl: './totaldebitbarchart.component.html',
  styleUrl: './totaldebitbarchart.component.css'
})
export class TotaldebitbarchartComponent implements OnInit {
  chartOptions: any = {};
  totalDebitStatsByCategory = new TotalDebitStatsByCategory();

  constructor(private expenseService: ExpenseService) {}

  ngOnInit() {
    this.expenseService.getTotalDebitStatsByCategory().subscribe(data => {
      this.parseToObjectArray(data);
      this.updateChartOptions();
    });
  }

  private updateChartOptions() {
    this.chartOptions = {
      title: {
        text: "Uitgave per categorie"
      },
      animationEnabled: true,
      axisY: {
        includeZero: true,
        suffix: "Euro"
      },
      data: [{
        type: "bar",
        indexLabel: "{y}",
        yValueFormatString: "#,### Euro",
        dataPoints: 
      }]
    };
  }

  parseToObjectArray(data: { [key: string]: number }): { label: string, y: number }[] {
    const dataArray: { label: string, y: number }[] = [];
  
    for (const key in data) {
      if (data.hasOwnProperty(key)) {
        const value = data[key];
        dataArray.push({ label: key, y: value });
      }
    }
  
    return dataArray;
  }
}

hmm seems to get some syntax errors

mild sinew
#

would help if you specify what errors you're getting

#

i suspect you're getting stuff from this instead, though?

#

!:unsafe-keys

silent gorgeBOT
#
retsam19#0
`!retsam19:unsafe-keys`:

Since TS allows objects to have extra properties not specified in the type, it doesn't assume that all the keys on the type are the only keys on the object. This means that Object.keys returns string[] not a specific type, and for(const key in obj), key is string, (not keyof typeof obj).

If you wish to assume otherwise, this utility is often helpful:

// A signature for `Object.keys` that assumes the only keys are the ones indicated by the type
const unsafeKeys = Object.keys as <T>(obj: T) => Array<keyof T>;
mild sinew
#

value being unknown?

sonic ocean
#

WHere do i need to use the Parse function

mild sinew
#

when you convert the raw data to something you can use for the bar chart. you could either store this and parse when you receive the rawData, or you could store the rawData and parse when you update the chart

sonic ocean
#
export class TotaldebitbarchartComponent implements OnInit {
  chartOptions: any = {};
  const dataArray: { label: string, y: number }[] = [];

  constructor(private expenseService: ExpenseService) {}

  ngOnInit() {
    this.expenseService.getTotalDebitStatsByCategory().subscribe(data => {
      this.parseToObjectArray(data);
      this.updateChartOptions();
    });
  }

  private updateChartOptions() {
    this.chartOptions = {
      title: {
        text: "Uitgave per categorie"
      },
      animationEnabled: true,
      axisY: {
        includeZero: true,
        suffix: "Euro"
      },
      data: [{
        type: "bar",
        indexLabel: "{y}",
        yValueFormatString: "#,### Euro",
        dataPoints: 
      }]
    };
  }

  parseToObjectArray(data: { [key: string]: number }) {
    for (const key in data) {
      if (data.hasOwnProperty(key)) {
        const value = data[key];
        this.dataArray.push({ label: key, y: value });
      }
    }
  }
}

I got this so far. then i get the exception:

Argument of type 'Map<string, bigint>' is not assignable to parameter of type '{ [key: string]: number; }'.
Index signature for type 'string' is missing in type 'Map<string, bigint>'.

on

this.parseToObjectArray(data);
mild sinew
#

im not seeing a map anywhere

#

you're missing a value after dataPoints, so maybe it's just not reporting valid errors due to the parsing error?

sonic ocean
#
export class TotaldebitbarchartComponent implements OnInit {
  chartOptions: any = {};
  dataArray: { label: string, y: number }[] = [];

  constructor(private expenseService: ExpenseService) {}

  ngOnInit() {
    this.expenseService.getTotalDebitStatsByCategory().subscribe(data => {
      this.parseToMapArray(data);
      this.updateChartOptions();
    });
  }

  private updateChartOptions() {
    this.chartOptions = {
      title: {
        text: "Uitgave per categorie"
      },
      animationEnabled: true,
      axisY: {
        includeZero: true,
        suffix: "Euro"
      },
      data: [{
        type: "bar",
        indexLabel: "{y}",
        yValueFormatString: "#,### Euro",
        dataPoints: this.dataArray
      }]
    };
  }

  parseToMapArray(data: Map<string, bigint>): void {
    data.forEach((value, key) => {
      this.dataArray.push({ label: key, y: Number(value) });
    });
  }
}

It says data.forEach is not a function

mild sinew
#

right, it isn't

#

data is a record, not a map

sonic ocean
#

hmm

#

i dont understand it

#

this is the data in data:

{
  TotalDebitStatsByCategory: {
    '43534': 34534,
    '456546': 4564,
    hema: 5,
    boom: 388,
    hypotheek: 1500,
    'Abbonementen Huis': 60,
    Worst: 675,
    henk: 345.45,
    Food: 1912.5
  }
}
#

now i wnat to parse it:

parseToMapArray(data: Map<string, bigint>): void {
    Object.entries(data).forEach(([key, value]) => {
      this.dataArray.push({ label: key, y: Number(value) });
    });
  }
#

its parsing it to:

[ { label: 'TotalDebitStatsByCategory', y: NaN } ]
#

so he does reach it

#

but he needs to be a level lower i think

#

it is because the data is coming in from the backend as a Observable

#

sos omething like to do, get the TotalDebitStatsByCategory out of the observerable

#

and then get the map out of the TotalDebitStatsByCategory

mild sinew
#

there's no map here, TotalDebitStatsByCategory is itself the record

#

just loop through data.TotalDebitStatsByCategory instead of data, or destructure that property out

sonic ocean
#

dont have that call

mild sinew
#

what's the type of data here?

sonic ocean
#

Map string bigint

mild sinew
#

well, that's not right.

sonic ocean
#

public getTotalDebitStatsByCategory(): Observable<Map<string, bigint>> {
return this.http.get<Map<string, bigint>>(this.expenseDebitbycategoryUrl);
}

mild sinew
#

ok, so that's lying to ts

#

!:thats-no%

silent gorgeBOT
#
tjjfvi#0
`!tjjfvi:thats-no-generic-its-a-type-cast`:

When you use a type parameter in the function, it should generally be inferrable from the arguments.

For example, this is a misuse of generics:

function getMeA<T>(): T {
   /* magic */
}

because getMeA<string>() and getMeA<number>() compile to the same code at runtime, there's so way to implement this function safely (other than always throwing); this is just a type cast in disguise. Instead of using a generic here, you should return unknown, and cast at the call site if necessary, to be clear it's an unsafe operation:

-function getMeA<T>(): T {
+function getMeA(): unknown {
    /* magic */
 }

-getMeA<number>()
+getMeA() as number

One exception to this rule is if you're returning a possibly-empty container of T. For example, these are all perfectly safe, even though the generic can't be inferred from the parameters:

function emptyArray<T>(): Array<T> {
  return []
}

function useRef<T>(): { current?: T } {
  return {}
}
sonic ocean
#

i did change this from TotalDebit thing to obserable

mild sinew
#

your backend gives a { TotalDebitStatsByCategory: Record<string, number> }, so your types are mismatched with what they actually are

sonic ocean
#

now i got

#
  public getTotalDebitStatsByCategory(): Observable<TotalDebitStatsByCategory> {
    return this.http.get<TotalDebitStatsByCategory>(this.expenseDebitbycategoryUrl);
  }
#
ngOnInit() {
    this.expenseService.getTotalDebitStatsByCategory().subscribe(data => {
      console.log(data);
      this.parseToMapArray(data);
      this.updateChartOptions();
    });
  }
#
parseToMapArray(data: TotalDebitStatsByCategory): void {
    const dataArray: { label: string, y: number }[] = [];
  
    // Gebruik de eigenschappen van TotalDebitStatsByCategory om de gegevens om te zetten naar het gewenste formaat
    Object.entries(data.total).forEach(([key, value]) => {
      dataArray.push({ label: key, y: Number(value) });
    });
  
    console.log(dataArray);
    // Hier kun je de gegevens verder verwerken, bijvoorbeeld toewijzen aan een variabele voor gebruik in de component
    this.dataArray = dataArray;
  }
#

and i get the error:

ERROR TypeError: Cannot convert undefined or null to object

mild sinew
#

TotalDebitStatsByCategory
is this the class

#

yeah it's the class isn't it

#

you're still lying to ts

#

saying that the variable is a TotalDebitStatsByCategory doesn't make it a TotalDebitStatsByCategory

#

it's still a { TotalDebitStatsByCategory: Record<string, number> } at runtime

sonic ocean
#

yes but how do i change that

mild sinew
#

use that in the generic that get has

#

that's the 1 place that ts doesn't know anything about, because it's a system boundary

sonic ocean
#

data.get?

mild sinew
#

if you type that correctly, the rest will fall into place

#

no, the http.get

sonic ocean
#

in the service?

mild sinew
#

yes

sonic ocean
#

public getTotalDebitStatsByCategory(): Observable<TotalDebitStatsByCategory> {
return this.http.get<TotalDebitStatsByCategory>(this.expenseDebitbycategoryUrl);
}

#
public getTotalDebitStatsByCategory(): Observable<Record<string, bigint>> {
    return this.http.get<Record<string, bigint>>(this.expenseDebitbycategoryUrl);
  }
#
parseToMapArray(data: Record<string, bigint>): void {
    const dataArray: { label: string, y: number }[] = [];
  
    // Gebruik de eigenschappen van TotalDebitStatsByCategory om de gegevens om te zetten naar het gewenste formaat
    Object.entries(data).forEach(([key, value]) => {
      dataArray.push({ label: key, y: Number(value) });
    });
  
    console.log(dataArray);
    // Hier kun je de gegevens verder verwerken, bijvoorbeeld toewijzen aan een variabele voor gebruik in de component
    this.dataArray = dataArray;
  }
mild sinew
#

that's still the wrong type

#

it's a { TotalDebitStatsByCategory: Record<string, number> }

sonic ocean
#

in my example?

mild sinew
#

return this.http.get<Record<string, bigint>>(this.expenseDebitbycategoryUrl);
this is where you tell ts what you get from the request

#

you're lying to ts, so ts can't help you

sonic ocean
#

why am i lying

#

just say it

#

what the correct solution is

#

cant figure it out

mild sinew
#

Record<string, bigint>
this is not what your request gives

#

your request gives a { TotalDebitStatsByCategory: Record<string, number> }, so you should be specifying that in the generic.

mild sinew
sonic ocean
#

then i get :

Page reload sent to client(s).
{
  TotalDebitStatsByCategory: {
    '43534': 34534,
    '456546': 4564,
    hema: 5,
    boom: 388,
    hypotheek: 1500,
    'Abbonementen Huis': 60,
    Worst: 675,
    henk: 345.45,
    Food: 1912.5
  }
}
[
  {
    label: 'TotalDebitStatsByCategory',
    y: {
      '43534': 34534,
      '456546': 4564,
      hema: 5,
      boom: 388,
      hypotheek: 1500,
      'Abbonementen Huis': 60,
      Worst: 675,
      henk: 345.45,
      Food: 1912.5
    }
  }
]
{
  TotalDebitStatsByCategory: {
    '43534': 34534,
    '456546': 4564,
    hema: 5,
    boom: 388,
    hypotheek: 1500,
    'Abbonementen Huis': 60,
    Worst: 675,
    henk: 345.45,
    Food: 1912.5
  }
}
[
  {
    label: 'TotalDebitStatsByCategory',
    y: {
      '43534': 34534,
      '456546': 4564,
      hema: 5,
      boom: 388,
      hypotheek: 1500,
      'Abbonementen Huis': 60,
      Worst: 675,
      henk: 345.45,
      Food: 1912.5
    }
  }
]
mild sinew
#

right, because the record is inside the object you receive, it's not the object itself

sonic ocean
#

if i do:

console.log(dataArray.entries[0])

I get the following back:

[
  {
    label: 'TotalDebitStatsByCategory',
    y: {
      '43534': 34534,
      '456546': 4564,
      hema: 5,
      boom: 388,
      hypotheek: 1500,
      'Abbonementen Huis': 60,
      Worst: 675,
      henk: 345.45,
      Food: 1912.5
    }
  }
]

when i do dataArray.entries[0] i get undefined back

mild sinew
#

entries is a function, if you want to access on an array that's just dataArray[0]

mild sinew
sonic ocean
#

Thought i got it. But i do get the correct things back

#

only if i want to show them in the barchart i get

main.ts:5 ERROR TypeError: Cannot read properties of undefined (reading 'x')