I have a parent component that receives data from an api using a service. I need to pass this data to the child component, as I don't want to make another call to the api (strict rule). I can access the data in my parent component, but I don't get it in my child component. I've tried many different ways.
import { Component, OnInit, ViewChild } from '@angular/core';
import {ActivatedRoute} from "@angular/router";
import {SwapiService} from "../../models/swapi.service";
import {Starship} from "../../models/starship";
// import {MatTableDataSource} from '@angular/material/table';
// import {MatPaginator} from '@angular/material/paginator';
@Component({
selector: 'app-starships',
templateUrl: './starships.component.html',
styleUrls: ['./starships.component.css']
})
export class StarshipsComponent implements OnInit {
public starship: Starship[]
url: string;
constructor(private swapi: SwapiService, private route: ActivatedRoute) {
}
ngOnInit(): void {
this.loadStarshipList();
}
loadStarshipList(): void {
this.swapi.getStarshipList().subscribe(
res => {
this.starship = res;
console.log(res);
}
)
}
}
parent template
span *ngIf="starship">
<p *ngFor="let res of starship?.results" >{{res?.name}}
<app-starship *ngFor="let res of starship.results" [data]="res?.results"></app-starship>
<!-- <span [res1]="res1"></span>-->
<button routerLink="/starships/{{res?.url}}">Starship Details</button>
</p>
</span>
child component
import { Component, Input, OnInit } from '@angular/core';
// import {Starships} from "../../models/starship";
import {SwapiService} from "../../models/swapi.service";
import {ActivatedRoute} from "@angular/router";
import {StarshipsComponent} from "../starships/starships.component";
@Component({
selector: 'app-starship',
templateUrl: './starship.component.html',
styleUrls: ['./starship.component.css']
})
export class StarshipComponent implements OnInit {
// public starship: Starship[]
private url: string;
@Input() data;
constructor(private swapi: SwapiService,
private route: ActivatedRoute,
private activatedRoute: ActivatedRoute,
) {
}
ngOnInit(): void {
this.url = this.route.snapshot.paramMap.get('url');
console.log(this.url);
}
}
child template
<span *ngIf="data"></span>
<div *ngFor="let res of data">
<h1>{{data?.name}}</h1>
<p>{{data?.url}}</p>
</div>
hi
there is no response.
I am trying to use BehaviorSubject so I can share the data among several components. My problem is, I don't know how to transfer the data from my function to a variable so that I can use it in the other components.
this is my service file:
import { Injectable } from '@angular/core';
import {HttpClient} from "@angular/common/http";
import {BehaviorSubject, Observable} from "rxjs";
import {map, skipWhile, tap} from 'rxjs/operators';
import {Starship} from "./starship";
@Injectable({
providedIn: 'root'
})
export class SwapiService {
private apiData = new BehaviorSubject<Starship[]>(null)
public currentData = this.apiData.asObservable()
baseURL = 'https://swapi.dev/api/';
constructor(private http: HttpClient) { }
getStarshipList(page?: number): Observable<Starship[]> {
return this.http.get<Starship[]>(`${this.baseURL}starships?format=json${this.getByPage(page)}`)
.pipe(
map(resp => resp['results']),)
;
}
getByPage(page: number): string {
if (page) { return '&page=' + page; } else { return ''; }
}
setData(data) {
this.apiData.next(data)
}
}
how do I transfer the data received by getStarshipList to a variable, so I can then pass it using the setData function. I know it seems like a stupid questions, but I've tried many different ways and can't get it right.
@Abhinav Aggarwal
I figured out how to use it in both components without declaring the variable in the service, but I would still like to know
Note import required library, ex: Subject
Child component
@Input() data: BehaviorSubject<any>;
ngUnsubscribe: any = new Subject();
ngOnInit(){
this.listenParentDataChanges();
}
listenParentDataChanges() {
if (this.data) {
this.data.asObservable().takeUntil(this.ngUnsubscribe)
.subscribe(responseData => {
console.log("-data--",data);
});
}
}
Parent Component
shareData: BehaviorSubject<any> = new BehaviorSubject({});
below code write where you are receiving data from backend.
this.shareData.next({ data: responseData});
html
<app-starship *ngFor="let res of starship.results" [data]="shareData"></app-starship>
You can also use BehaviorSubject
service.ts
private sList = new BehaviorSubject<any>(null);
getStarshipList() {
return this.http.get(`url`).pipe(
tap((data: any) => this.sList.next(data);
);
}
getSlist() {
return this.sList.asObservable().pipe(skipWhile(val => val === null));
}
parent.component.ts
ngOnInit(){
this.loadStarshipList();
}
loadStarshipList(){
this.swapi.getStarshipList().subscribe(
res => {
this.starship = res;
console.log(res);
}
)
}
child.component.ts
data:any;
ngOnInit()
this.swapi.getSList().subscribe(
res => {
this.data = res;
console.log(res);
}
)
}
You don't need multiple ngFor
on parent template. Pass res
directly to the child which has a result
array into it.
<p *ngFor="let res of starship?.results" >{{res?.name}}
<app-starship [data]="res"></app-starship>
Child component:
ngOnInit(): void {
console.log(this.data); //check you have data or not here
}
Advice:- First understand how the lifecycle hooks works. And why angular has introduces ngOnChanges. Study lifecycle hooks and Change Detection.
You problem :- You are passing variable from parent to child. which on init stage of parent does not have data and is undefined. Which you have already passed to the child component hence you don't have data. Api gives data after both views are already created. and angular component does not know that data is changed.
Solution :-
Option 1 : You should create child Component when parent component receives the data.
or
Option 2 : Subscribe to the parent component for change detection
or
Option 3 : Do a change detection on both parent and child component.
In your child component ... after the ngOnInIt method use above code and import & implements ngOnChanges from angular/core, I hope it will work.
Imports : import { OnChanges } from '@angular/core';
Implements: export class StarshipComponent implements OnInit, OnChanges { }
ngOnChanges(changes: SimpleChanges): void {
if (changes.data.currentValue) {
this.data = changes.data.currentValue;
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.