1

I created two components one for listing all loans and another is for viewing each loan details on click of each card in the list. When I tried to console the data in subscribe, I can view the information, but I am not getting any data on the HTML page.

loan.ts

export class Loan
{
    id :number;
    title: string;
    description: string;
    amount: number;
}

list-loan-component.ts

import { Component, OnInit } from '@angular/core';
import * as EventEmitter from 'events';
import { Loan } from '../loan';
import { LoanService } from '../loan.service';

@Component({
  selector: 'app-list-loans',
  templateUrl: './list-loans.component.html',
  styleUrls: ['./list-loans.component.css']
})
export class ListLoansComponent implements OnInit {
  
  loans:Loan[];

  constructor(private loanService: LoanService) { }

  ngOnInit(): void {
    this.loans = this.loanService.getLoans();
  }

  openLoan(loan: Loan)
  {
    this.loanService.loan.emit(loan);   
  }
}

list-loans-component.html

<div class="container">
    <div class="card-deck">
       <div *ngFor="let loan of loans">
           <div class="card" routerLink="/loan/view" routerLinkActive="active"
           (click)="openLoan(loan)">
               <div class="card-header"> {{loan.title}} - {{loan.amount}}</div>
               <div class="card-body">
                   <p class="card-text">{{loan.description}}</p>
               </div>
           </div>
       </div>
    </div>
</div>

view-loan.component.ts

import { ChangeDetectorRef, Component, Input, OnChanges, OnInit } from '@angular/core';
import { ActivatedRoute, ActivatedRouteSnapshot, Params } from '@angular/router';
import { Loan } from '../loan';
import { LoanService } from '../loan.service';

@Component({
  selector: 'app-view-loan',
  templateUrl: './view-loan.component.html',
  styleUrls: ['./view-loan.component.css']
})
export class ViewLoanComponent implements  OnInit {

  selectedLoan: Loan ;

  constructor(private loanService: LoanService, private router:ActivatedRoute) { }

  ngOnInit() {
    this.loanService.loan.subscribe(loan =>
      {
        this.selectedLoan = loan;     
      }
    );
  }
}

view-loan.component.html

<div class="card text-center">
  <div class="card-header">
    {{selectedLoan['title']}}
  </div>
  <div class="card-body">
    <h5 class="card-title">Loan From :  {{selectedLoan['title']}}</h5>
    <h3 style="text-align: left; text-decoration: underline;">Details:</h3>
    <p class="card-text">{{selectedLoan['description']}}</p>
    <p class="card-text">{{selectedLoan['amount']}}</p>
    <a href="#" class="btn btn-primary">Go somewhere</a>
  </div>
  <div class="card-footer text-muted">
    2 days ago
  </div>
</div>

loan.service.ts

import { Injectable, Output, EventEmitter } from "@angular/core";
import { Loan } from "./loan";
    
@Injectable({
    providedIn: 'root'
  })
export class LoanService
{
    loan = new EventEmitter<Loan>();

    private loans:Loan[] = [
        {
            "id":1,
            "title" : "HDFC Credit Loan",
            "description" :"Loan to clear all credit card payments",
            "amount" : 24958.23
        },
        {
            "id":2,
            "title" : "Aditya birla personal Loan",
            "description" :"Loan to personal expenses",
            "amount" : 12000.00
        }
    ]

    constructor(){}

    getLoans(): Loan[]{
        return this.loans.slice()
    }

    getLoan(id:number): Loan{
        this.loans.forEach(loan =>
            {
                if(loan["id"] === id) 
                    return loan;
            }
        );
        return new Loan();
    }
}

Note: I am using routing as well. Kindly let me know if routing can cause any of this issues.

Vineel Pellella
  • 332
  • 2
  • 4
  • 20

3 Answers3

1

It seems you have EventEmitter value passed before your ViewLoanComponent is loaded. Just replace your EventEmitter for ReplaySubject, like this:

export class LoanService
{
    $loan = new ReplaySubject<Loan>(1);
...

And next in code

openLoan(loan: Loan)
  {
    this.loanService.$loan.next(loan);   
  }

Also remove the async pipe, in your case:

<div *ngFor="let loan of loans">
Anton Marinenko
  • 2,749
  • 1
  • 10
  • 16
  • This worked perfectly fine. Can you explain when to use RelaySubject and EventEmitter, also why EventEmitter is not right choice here? – Vineel Pellella May 10 '21 at 09:03
  • EvenEmitter actually is the `Subject`. So when you emit value, it should be listened somewhere. In you case ViewLoanComponent is not created yet when value emitted and can't catch it. After ViewLoanComponent is created, EventEmitter is not emitting new values, and nothing to catch. But if you use ReplaySubject, then on `subscribe` you can get latest emitted values, in that case we need last one. – Anton Marinenko May 10 '21 at 09:20
0

Inside your list-loans-component.html you are iterating over your loans like so:

<div *ngFor="let loan of loans | async"> but your loans are not a async value, as your loan service directly returns an Loan[] (not Observable<Loan[]>). Just remove the async pipe and things will start to work.

p.s. Very well asked question.

Edit:

Here is a working stackblitz with your code line for line.

0

You just can using async pipe on an observable but in your code you are using on loans[] witch is an array.

Remove the pipe.

But I recommend to use a BehaviorSubject or an Observable rather than an EventEmitter. https://angular.io/guide/component-interaction#parent-and-children-communicate-via-a-service

Saghi Shiri
  • 537
  • 5
  • 19