1

I'm new to Typescript, i'm working on creating Cost calculator application. I have 8 question as separate components and totalCost component. Each question have 3 different options to select. upon selecting option I want Total cost to add up as user progress in questions. Once user answer final question I was summary to present him with Total Cost.

I did research and came across different answers but non of them seems to be working for me. Most of the answers given are in JS.

I tried using rxjs/BehaviorSubject library but got error. Here is my code.

totalCost.component.ts

import { Component, Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';

 @Component({
   selector: 'totalcost',
   template: `<div>
    <p>Total: {{totalCost}}
   </div>`

 })

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


  private totalCost = new BehaviorSubject<number>(0);
  currentCost = this.totalCost.asObservable();

  constructor(){

  }

     addCost(add: number){
       this.totalCost.next(add);
       console.log('addCost');
        return this.totalCost;
     }



 }

Here is the first question component:

import { Component, OnInit, Injectable } from '@angular/core';
import { TotalCost } from '../totalCost/totalCost.component';

@Injectable({
  providedIn: 'root'
})
 @Component({
   selector: 'first',
   templateUrl: './first.component.html',
   styleUrls: ['./first.component.scss']
 })



export class First {

    totalCost: number;


    constructor(private cost: TotalCost){

    }

   ngOnIt(){
      this.cost.currentCost.subscribe(totalCost => this.totalCost = totalCost )
    }

    addToCost(value: number){
      this.cost.addCost(value);
      console.log('addToCost()');
    }

 } 

here is HTML for First Question:

<div class="container-fluid text-center">
  <div>
    <h3>Choose The Type Of Website</h3>
    <input type="range" value="1000" id="budget">
    <p>
      <span>
        <a [routerLink]="'/second'" (click)="addToCost(1000)"><img src="" alt="static"></a>
      </span>
      <span>
        <a [routerLink]="'/second'" ><img src="" alt="Dynamic"></a>
      </span>
      <span>
        <a [routerLink]="'/second'" ><img src="" alt="E-Commerce"></a>
      </span>
    </p>
  </div>
</div>

When user select one of the option I want Total cost to add up until user reach last question.

This is what my application look like

enter image description here

Sabith
  • 1,628
  • 2
  • 20
  • 38

3 Answers3

2

You can try like this.
Create one common service which one contains your subject.

common.service.ts

@Injectable({
  providedIn: 'root'
})
export class CommonService {
  private totalCost = new BehaviorSubject<number>(0);

  sendMessage(data: any) {
     this.totalCost.next(data);
  }
  getMessage(): Observable<any> {
     return this.totalCost.asObservable()
  }
}

ComponentA // component which you want to send the data

export class ComponentA {
// here we are adding our common service which contain our subject
    constructor(private brodcaster: CommonService){}

    sendData(data) {
     this.brodcaster.sendMessage(data);
    }

ComponentB // component which you want to access the data

export class ComponentA {
    // here we are adding our common service which contain our subject
    constructor(private brodcaster: CommonService){
     this.brodcaster.getMessage().subscribe(response => {
        console.log(response);
     })
    }
Celsiuss
  • 895
  • 7
  • 19
Yash Rami
  • 2,276
  • 1
  • 10
  • 16
  • Thank you for providing help, how can I add total from previous question? right now it's removing previous amount and displaying next question selection value. Should I add totalCost in service send message? sendMessage(data: any) { this.totalCost.next(data + this.totalCost); } – user12515297 Dec 11 '19 at 04:46
  • Yes, You can do that if and only if `totalcost` is static other wise you have to pass the `totalcost` from the component. other wise you can also add the `totalcost` on your component itself – Yash Rami Dec 11 '19 at 05:20
0

You are declaring your your behavioural subject inside TotalCost component private totalCost = new BehaviorSubject<number>(0); ideally it should be declared in a shared service. Another thing I noticed is that. You are declaring it in TotalCost component as a private variable which makes that variable cannot be accessed from another components from the way you are doing it. Ideal way to accomplish this is shared service which can be injected to both the components.

Share service.ts

@Injectable({
  providedIn: 'root'
})
export class SharedService{
  private totalCost = new BehaviorSubject<number>(0);

  addCost(data: any) {
     this.totalCost.next(data);
  }
  getCost(): Observable<any> {
     return this.totalCost.asObservable()
  }
}

TotalCost.component.ts:

import { Component, Injectable } from '@angular/core';
import { SharedService} from '<Import the SharedService from here>';

 @Component({
   selector: 'totalcost',
   template: `<div>
    <p>Total: {{totalCost}}
   </div>`

 })

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

  constructor(pubic sharedService:SharedService){

  }

     addCost(add: number){
       this.sharedService.addCost(add);
     }



 }

First Question.Component.ts:

import { Component, OnInit, Injectable } from '@angular/core';
import { SharedService} from '<Import the SharedService from here>';

@Injectable({
  providedIn: 'root'
})
 @Component({
   selector: 'first',
   templateUrl: './first.component.html',
   styleUrls: ['./first.component.scss']
 })



export class First {

    totalCost: number;


 constructor(public sharedService: SharedService){
      // Fetch values from the service here
      this.sharedService.getCost().subscribe((cost)=>{
       console.log(cost);
        alert(cost);
     });
    }

   ngOnIt(){

    }



 } 
Selaka Nanayakkara
  • 3,296
  • 1
  • 22
  • 42
  • Thank you, in which component and method should I save previous question totalCost? Right now when I go to next question and make selection than totalCost change to that value rather then adding old value with new. – user12515297 Dec 11 '19 at 04:56
  • I was able to get value first in second question and add it with data from SendData. `// here we are adding our common service which contain our subject constructor(private brodcaster: CommonService){ this.brodcaster.getMessage().subscribe(response => { this.totalCost = response; }) } sendData(data) { this.brodcaster.sendMessage(data + this.totalCost); } }` – user12515297 Dec 11 '19 at 05:03
  • Sorry i didn't get you?? – Selaka Nanayakkara Dec 11 '19 at 05:31
0

Check this Stackblitz example I made: https://stackblitz.com/edit/angular-bqxex9

There you have a common service to track the values of each component and the total

import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';

@Injectable()
export class CommonService {

  valueFirst = 0;
  valueSecond = 0;

  total = new BehaviorSubject<number>(0);

  setFirstValue(value: number) {
    this.valueFirst = +value;
    this.total.next(this.valueFirst + this.valueSecond);
  }

  setSecondValue(value: number) {
    this.valueSecond = +value;
    this.total.next(this.valueFirst + this.valueSecond);
  }

}

The main component subscribes to the BehaviorSubject in the service:

export class AppComponent  {
  total = 0;

  constructor(private commonService: CommonService) {
    this.commonService.total.subscribe(
      tot => this.total = tot
    );
  }
}

And the First, Second, etc. components update their values in the service:

export class FirstComponent {

  constructor(private commonService: CommonService) { }

  sendValue(value: number) {
    this.commonService.setFirstValue(value);
  }
}
Kari F.
  • 1,214
  • 10
  • 16