Creating a running total from an array of data

Hello, please dont link other topics to this one as i have spent days trying to find anything but none of them work for me and there are many differant methods.
I am trying to make a running total of cost for a series of receipts that are added to an array. for now, i have dummy data but will soon be using firebase to store the data.

Any help would be massively appreciated, i cant seem to figure it out.

This is the HTML

<ion-header>
  <ion-toolbar>
    <ion-title class="fontB">Expenses Home</ion-title>
  </ion-toolbar>
</ion-header>
<div>
  <ion-searchbar animated placeholder="Search Expenses"></ion-searchbar>
</div>

<ion-content>
  <ion-list>
    <ion-item-sliding *ngFor="let receipt of receipts" [routerLink]="['./receipts', receipt.id]">
      <ion-item>

      <ion-avatar>
        <ion-img [src]="receipt.imageURL"></ion-img>
      </ion-avatar>

        <ion-label>

      <ion-text>
        &nbsp; {{receipt.name}} &nbsp;  £{{receipt.amount.toFixed(2)}}
      </ion-text>


      <ion-text>
        <p> &nbsp; <b>ON </b>{{receipt.date}} &nbsp; <b>AT </b> {{receipt.time}} &nbsp; <b>#</b> {{receipt.id}}</p>
      </ion-text>

      </ion-label>
      </ion-item>
<!--      <ion-item-options side="end" color="danger">-->
<!--        <ion-item-option (click)="onDeleteReceipt()">Delete</ion-item-option>-->
<!--      </ion-item-options>-->

    </ion-item-sliding>
  </ion-list>
</ion-content>

<ion-footer>
  <ion-item>

    <span class="receipt-titles">Total Amount:</span>  &nbsp; &nbsp; £ {{totalPrice}}

  </ion-item>
</ion-footer>

This is the TS

import { Component } from '@angular/core';
import { ReceiptService } from '../create/receipt.service';
import { Receipt } from '../create/receipts.model';

@Component({
  selector: 'app-home',
  templateUrl: './home.page.html',
  styleUrls: ['./home.page.scss'],
})
export class HomePage {
  receipts: Receipt[];

  constructor(private receiptService: ReceiptService
  ) {
  }

  ionViewWillEnter() {
    this.receipts = this.receiptService.getAllReceipts();
  }
}

And this is the service TS with the data.

import { Injectable } from '@angular/core';
import {Receipt} from './receipts.model';

@Injectable({
  providedIn: 'root'
})
export class ReceiptService {

  private receipts: Receipt[] = [{
    id: 1,
    name: 'Pancakes',
    address: 'Home',
    description: 'Some pancakes for Georgia',
    amount: 1.50,
    date: '06-10-2020',
    time: '12:20',
    imageURL: 'https://upload.wikimedia.org/wikipedia/commons/thumb/0/0b/ReceiptSwiss.jpg/170px-ReceiptSwiss.jpg'
  },
    {
      id: 2,
      name: 'Coke',
      address: 'Work',
      description: 'Some coca-cola for Mills',
      amount: 24.95,
      date: '12-08-2020',
      time: '19:20',
      imageURL: 'https://i.imgur.com/9C5Q9Rt.jpg'
    },
    {
      id: 3,
      name: 'Petrol in Tesco Extra',
      address: 'Home',
      description: 'Some pancakes for Dummy tesxtasiuch Georgia',
      amount: 1.50,
      date: '06-10-2020',
      time: '12:20',
      imageURL: 'https://i.imgur.com/w1iF3yD.jpg'
    },
    {
      id: 4,
      name: 'Krispy Kremes',
      address: 'Home',
      description: 'Somekiusadlffh sadliuhdlfg saiguh  pancakes for Georgia',
      amount: 1.50,
      date: '06-10-2020',
      time: '12:20',
      imageURL: 'https://i.redd.it/xvbmnif3fnt21.jpg'
    },
    {
      id: 5,
      name: 'Nuttella',
      address: 'Home',
      description: 'Some pancakes zsldivuhiuh sdliuhdf vlijh for Georgia',
      amount: 1.50,
      date: '06-10-2020',
      time: '12:20',
      imageURL: 'https://i.ebayimg.com/images/g/0kMAAOSw-VtbCgcF/s-l300.jpg'
    },
    {
      id: 6,
      name: 'Cherries',
      address: 'Home',
      description: 'Some pancakes kuihsdf sdfiuh dyh oids dsfhjefiju ds fciisidfoi sd lidvhoi  for Georgia',
      amount: 1.50,
      date: '06-10-2020',
      time: '12:20',
      imageURL: 'https://i.redd.it/go8r4r6opqe11.jpg'
    }];

  constructor() {

  }
  getAllReceipts() {
    return [...this.receipts];
  }

  getReceipt(id: number) {
    return {...this.receipts.find(receipt => {
      return receipt.id === id;
    })};
  }

    deleteReceipt(id) {
    this.receipts = this.receipts.filter(receipt => {
      return receipt.id !== id;
    });
  }
}

Finally this is some images of file structure etc.


A potentially larger issue is how you communicate changes in the array of receipts. There are two huge red flags for me here: the use of lifecycle events to manage data retrieval, and passing of naked data. See this post for more information and a suggested alternate design.

As for the total, I would recalculate it when a new array of receipts presents. This could either be done in the service or in the page. I would make that decision based on whether anybody aside from that page cares about the total. If not, I’d do it in the page. If so, in the service.

Could you expand a little more on the first para? I read your other post you have linked and dont really understand what it is I have done wrong or inefficiently or how it effects my app.
And how would I go about writing the code for that?
Thanks for a speedy reply.

It is frequently the case that somebody outside this page, whether it be another page in this same app or something completely external (something new posted to the backend server, for example), modifies the data that this page would see.

So let’s say this page gets foregrounded at 1137. There are three receipts at that time. At 1140 a fourth receipt gets posted. If we had opened this page at 1141 instead, it would have seen four receipts. No problem; we close the page and renavigate to it, and now it shows four receipts, right? Maybe, maybe not, depending on the whims of framework lifecycle events, and that’s not a place I like to be.

I don’t think I’d say either “wrong” or “inefficient”, but if you come across a case where updates to the receipt list (which will also affect the total calculation) need to be pushed out to the page, the service should expose it as an Observable<Receipt[]> or Observable<ReceiptArrayIncludingTotalAlreadyCalculated>, not as a raw Receipt[]. This will also futureproof you if you’re planning on migrating to something like Firebase, where you’ll be getting Observables from the underlying storage anyway.

At that point, you also have a natural bottleneck for calculating the total: every time the Observable emits, either on the producer or consumer side.

import { Injectable } from '@angular/core';
import {Receipt} from './receipts.model';

@Injectable({
  providedIn: 'root'
})
export class ReceiptService {
  profile$ = new BehaviorSubject<Profile | null>(null);
  watchProfile(): Observable<Profile | null> { return this.profile$; }
  peekProfile(): Profile | null { return this.profile$.value; }
  pokeProfile(profile: Profile): void { this.profile$.next(profile); }

  private receipts: Receipt[] = [{
    id: 1,
    name: 'Pancakes',
    address: 'Home',
    description: 'Some pancakes for Georgia',
    amount: 1.50,
    date: '06-10-2020',
    time: '12:20',
    imageURL: 'https://upload.wikimedia.org/wikipedia/commons/thumb/0/0b/ReceiptSwiss.jpg/170px-ReceiptSwiss.jpg'
  },
    {
      id: 2,
      name: 'Coke',
      address: 'Work',
      description: 'Some coca-cola for Mills',
      amount: 24.95,
      date: '12-08-2020',
      time: '19:20',
      imageURL: 'https://i.imgur.com/9C5Q9Rt.jpg'
    },
    {
      id: 3,
      name: 'Petrol in Tesco Extra',
      address: 'Home',
      description: 'Some pancakes for Dummy tesxtasiuch Georgia',
      amount: 1.50,
      date: '06-10-2020',
      time: '12:20',
      imageURL: 'https://i.imgur.com/w1iF3yD.jpg'
    },
    {
      id: 4,
      name: 'Krispy Kremes',
      address: 'Home',
      description: 'Somekiusadlffh sadliuhdlfg saiguh  pancakes for Georgia',
      amount: 1.50,
      date: '06-10-2020',
      time: '12:20',
      imageURL: 'https://i.redd.it/xvbmnif3fnt21.jpg'
    },
    {
      id: 5,
      name: 'Nuttella',
      address: 'Home',
      description: 'Some pancakes zsldivuhiuh sdliuhdf vlijh for Georgia',
      amount: 1.50,
      date: '06-10-2020',
      time: '12:20',
      imageURL: 'https://i.ebayimg.com/images/g/0kMAAOSw-VtbCgcF/s-l300.jpg'
    },
    {
      id: 6,
      name: 'Cherries',
      address: 'Home',
      description: 'Some pancakes kuihsdf sdfiuh dyh oids dsfhjefiju ds fciisidfoi sd lidvhoi  for Georgia',
      amount: 1.50,
      date: '06-10-2020',
      time: '12:20',
      imageURL: 'https://i.redd.it/go8r4r6opqe11.jpg'
    }];

  constructor() {

  }
  getAllReceipts() {
    return [...this.receipts];
  }

  getReceipt(id: number) {
    return {...this.receipts.find(receipt => {
      return receipt.id === id;
    })};
  }

    deleteReceipt(id) {
    this.receipts = this.receipts.filter(receipt => {
      return receipt.id !== id;
    });
  }
}

Okay, thanks for the info! I am still developing this as one of my first ever apps and the code is closely modeled by someone teaching me on Udemy so thank you for helping me. Can you help me mould it into the code? Here is what I have from yours which is obviously bringing loads of errors.

Do you have a solution for calculating the total?

I think you would benefit from going through the Tour of Heroes. Your business domain doesn’t appear too distant from the stable of heroes, and it will provide you a grounding in idiomatic Angular at absolutely no cost or obligation.

After u got array, you can use Lodash SumBy:

https://lodash.com/docs/4.17.15#sumBy