I'm fetching data from my service in the app component, and then passing it down to a child component via #Input. when I console.log the data in ngOnInit, it DOES show up in the child component, but when I try to pass it to a variable and use it in the child template, it's coming back undefined, and I'm not sure why.
Here's where I call my service in app.component.ts, the data that is having issues is this.colorcounts. Console logging here inside the ngOnInit DOES show the correct data. colorCounts has 3 properties: red, yellow & green:
ngOnInit() {
this.pciService.getPciInfo()
.subscribe((pciInfo) => {
this.spinner.hide();
this.pciData = pciInfo.sorted,
this.colorCounts = pciInfo.counts;
});
Here's the app.component.html template where I'm passing the data down:
<app-navbar *ngIf="colorCounts" [colorCounts] = "colorCounts"></app-navbar>
<app-system-status [pciData]="pciData"></app-system-status>
Here's the child component where I'm grabbing the data, doing a console.log in ngOnInit does work, but trying to use "red" in the template or save it in the class comes back undefined:
constructor(private pciService: PciService,
public dialog: MatDialog,
private decimalPipe: DecimalPipe) { }
AMVersion: any;
#Input() colorCounts: Colors;
openDialog(): void {
let dialogRef = this.dialog.open(AmVersionDialogComponent, {
panelClass: 'custom-dialog-container',
data: {}
});
(<AmVersionDialogComponent>dialogRef.componentInstance).AMVersion = this.AMVersion;
dialogRef.afterClosed().subscribe(result => {
console.log('The dialog was closed');
});
}
ngOnInit() {
this.pciService.getAMVersion()
.subscribe((AMInfo) => {
return this.AMVersion = AMInfo;
});
var red = this.colorCounts.red;
console.log(red)
console.log(this.colorCounts);
}
}
I know I'm probably missing something simple regarding the life cycle. Can someone point me in the right direction here?
All Observables are async so in template *ngIf condition will check variable and if it is null will return null but if You pipe variable as | async it will be checking this variable all time and when variable will apear not null it will show content ngIf.
*ngIf works only once !!! He not waiting for anny http calls and Observables are making one usualy. If You want to *ngIf wait for calls You need to use | async pipe inside.
Same as You subscribe to it in ngOnInit() and assign to variables in template. Subscription will assign those values later after template is allredy on screen. Read about what async means.
You need to know that this is a boilercode:
ngOnInit() {
this.pciService.getPciInfo()
.subscribe((pciInfo) => {
this.spinner.hide();
this.pciData = pciInfo.sorted,
this.colorCounts = pciInfo.counts;
});
It is better to do it like this:
ngOnInit() {
this.pciInfo$ = this.pciService.getPciInfo()
}
in template:
<ng-container *ngIf="pciInfo$ | async as pciInfo">
<app-navbar [colorCounts]="picInfo.counts"></app-navbar>
</ng-container>
Pipe Async will subscribe for you to an Observable.
Related
I don't really know how to extract the values from the subscribe() method.
getMessages(): any {
this.gatewayMessagesState$.subscribe(data => data.gatewayMessages
.get(this.gatewayId)
?.list
.map(Message => Message.message));
}
gatewayMessagesState is an initial state that contains some data. gatewayMessages is a map with gatewayIds as keys and arrays of Message objects as values. Message has message field that's just a string. I would like to extract an array of messages for a given id. How can I do that?
What you probably want to do is to populate another Observable with the data so that you can access it elsewhere in your project without the need for calling the API more than once.
To do this, you create what is known as a Subject (in this case a BehaviorSubject) and you can populate that with data when your API call returns a response.
Then, in order to access this data elsewhere, you can create a "get" function to return the Subject (which is itself an Observable) whenever you need the data.
Here is an example:
my - data.service.ts
myData: BehaviorSubject < number > = new BehaviorSubject < number > (0);
callApi() {
this.dbService.get('apiUrl').subscribe(
(data) = > this.myData.next(data) // Assuming data is a 'number'
);
}
getMyData() {
return this.myData.asObservable();
}
Now to use this in a component:
this.myService.getMyData().subscribe(
(data) = > {
/* Use the value from myData observable freely */
}
);
Or you could rely on the Angular async pipe (which is a very convenient method for dealing with observables in your code).
You are not specifying if getMessages is in a service, component... in any case, I suggest returning the Observable without subscribing to it in the getMessages function
// this function could be anywhere
getMessages(): Observable<string[]> {
return this.gatewayMessagesState$.pipe(
map((data) => data.gatewayMessages.get(this.gatewayId)),
map((yourMap) => yourMap?.list.map((theMessage) => theMessage.message))
);
}
Now, if you need to extract this value, either from a component, a service, etc... then, just call this function and then subscribe to get the result
Let's say getMessages is in a service file for example
Your component
constructor(private theService: YourService) {}
anotherFunction() {
this.theService.getMessages().subscribe((myMessages) => console.log(myMessages));
}
Or let the async pipe subscribe to this observable
Your component
messages$!: Observable<string[]>
constructor(private theService: YourService) {}
anotherFunction() {
this.messages$ = this.theService.getMessages()
}
Your component html
<ng-container *ngIf="messages$ | async as messages">
<div *ngFor="let message of messages">
<p>{{ message }}</p>
</div>
</ng-container>
I this you want to retrieve the data as an observable of messages as string, you can define the function return as this and using pipe and map operatoes from rxjs,this is code below is my proposition
getMessages(): observable<string[]>{
return this.gatewayMessagesState$.pipe(map((data) =>
data.filter((f) => f.gatewayMessages.id ===this.gatewayId)),
map(item => item.message));
}
I have an Angular Scroller component
<app-scroller></app-scroller>
that provides a skeleton for displaying an array of images
Random Component
<app-scroller [init]="getImages('cats')"></app-scroller>
<app-scroller [init]="getImages('dogs')"></app-scroller>
getImages(src: string) {
//THIS FUNCTION GETS CALLED AGAIN AND AGAIN
return {
aspect: '16/9',
res: 'min',
sources: this.imageService.getImagesFromAPI(src)
};
}
Scroller Component
public movies: string[] = [];
#Input() init: {aspect: string, res: string, sources: Promise<string[]>};
ngOnInit() {
this.init.sources.then(images => this.movies = movies);
}
but this results in the the getImages and therefore the sources Promise to be executed over and over
Is there a way I can send data to the Scroller component only once (therefore without using #Input() )
I believe you need to call your service once to get the array of images and save it inside your component as a property,
something like this
myService.getData.subscribe(data=> this.catImages = data)
If I understand your question and setup correctly, you are asking about preventing an #Input from listening, what if you instead prevent data from emitting to this input?
You could deliver an observable stream that emits just once, eg.
catImages$ = this.catDataFromService$.pipe(
take(1),
)
<app-scroller [source]="catImages$ | async"></app-scroller>
Alternatively, you could construct your own Observable and complete it when necessary.
Use property binding only to send the category id (dogs/cats) to the component and call getImages(cateogryID) only once in the child component.
Parent component
<app-scroller [categoryId]="catIdProperty"></app-scroller>
Child component:
#input()
categoryId: string;
images: [type here] = [initialization here];
ngOnInit(): void {
this.images = this.getImages(categoryId); // Btw, could getImages() reside in the imageService?
}
I am new in angular working on a project.My problem is that i want to transfer data from one component to other. Actually i want to show data in text field from database and then have to update it. I have one component name ricerca.component.ts in which data in table is showing. now when i click on specific line(row) then data for that specific record i have to show in my other component name as generatecontract.comonent.ts. I don't know how to perform this.
I made a model name ContractDblist assign all these value to that model but unfortunatelly not solved the problem in other component
this is my ricercacomponnet code
if(tag === 'Item1'){
this.router.navigate(['/workflow/rigester' ]);
}
}
public lstContractRecordDbValue :any[];
getContractRecordbyParameter(selecteditem: any, index: number) { this.workFlowService.getContractRecordbyParameter(selecteditem).subscribe(data => {
this.lstContractRecordDbValue = data;
this.contractdblist.cnt_num=this.lstContractRecordDbValue[0].CNT_NUM;
this.contractdblist.contract=this.lstContractRecordDbValue[0].CONTRACT; this.contractdblist.contacttype=this.lstContractRecordDbValue[0].CONTRACT_TYPE; this.contractdblist.contractno=this.lstContractRecordDbValue[0].CONTRACT_NO;
this.loading = false;
}, error => {
console.error('getAllTickets', error);
this.loading = false;
})
}
You can use Subject to do that
import { Injectable } from "#angular/core";
import { Subject } from "rxjs";
#Injectable()
export class MessageService {
private subject = new Subject<any>();
constructor() {}
sendMessage(message: any) {
this.subject.next(message);
}
getData() {
return this.subject.asObservable();
}
}
So you can call MessageService class method sendMessage() to send data
I defined 2 method here. The first method using next() to send message to the next subcriber. So in your component you just need to simply subscribe like this to get the data
private subscription$: Subscription;
public ngOnInit(): void {
this.subscription$ = this.messageervice
.getData()
.subscribe(data => { console.log(data); })
}
public ngOnDestroy(): void {
this.subscription$.unsubscribe();
}
There are many ways to do so some of them are
using input decorator when passing data from parent to child component.
using output decorator with event emitter when passing data from child to parent component.
using subjects for components not related to each other.
using a service to set and get data to be passed.
you can refer their official website for all the above ways.
I am developing an Angular 4 application. I have a search component, where the user inputs a string. Whenever the user enters a value and submits it, I am emitting the value from the SearchComponent to DisplayComponent.
Emitting value in SearchComponent
#Output() userInput: EventEmitter<string> = new EventEmitter<string>();
onFormSubmit() {
this.userInput.emit(this.searchForm.value.SearchInputValue);
}
DisplayComponent HTML looks like
<app-search (userInput)="valueFromSearch"></app-search>
<div class="some-other-html></div>
DisplayComponent TS file looks like
valueFromSearch: string;
constructor(private someService: SomeService) {}
ngOnInit() {
this.someService.getSomeData(valueFromSearch)
.subscribe((data) => {console.log("Success! data is ",data)},
(error) => {console.log("Something went wrong", error)})
}
But how to run the someService function whenever, the valueFromSearch changes? Should I use BehaviorSubject to keep listening for the changes in valueFromSearch?
Is there any function to know that the value has changed? Please help me resolve this.
you can make valueFromSearch() a method which will invoke everytime you emit from search
DisplayComponent HTML
<app-search (userInput)="valueFromSearch(value)"></app-search>
<div class="some-other-html></div>
DisplayComponent TS file
constructor(private someService: SomeService) {}
valueFromSearch(value: string) {
this.someService.getSomeData(value)
.subscribe((data) => {console.log("Success! data is ",data)},
(error) => {console.log("Something went wrong", error)})
}
ngOnChanges(changes: any) {
let data = changes.valueFromSearch.currentValue;
// This only work with a change not if it has the same value.
}
Not sure if #JayDeeEss's method will work right away but a slight modification to it definitely seems workable.
Like his suggestion, make valueFromSearch a method (remove this declaration: valueFromSearch: string; from your component) that gets invoked every time the userInput EventEmitter emits a new value.
DisplayComponent HTML (notice the change in the argument passed to valueFromSearch(); from (value) to ($event) )
<app-search (userInput)="valueFromSearch($event)"></app-search>
<div class="some-other-html></div>
DisplayComponent TS file (inside the component, the argument name doesn't matter)
valueFromSearch(value: string) {
// handle the changed 'value' based on required logic
}
I have a component that's displaying a value in {{getData}} if I fire the onDo() with a button click. onDo() is initiating a GET request to a server.
The server is giving back the response, and I am subscribing it. I am assigning the subscribed value to this.getData and being able to show it successfully in my component template with {{getData}}.
I can also get a print of the data in the console inside .subscribe().
But I tried to print it outside the .subscribe() and it's not displaying anything.
What I actually want to do is: assign the value of getData to MyVar.
Here is my code:
#Component({
template: `
...
<p>{{getData}}</p>
...
`,
providers: [MyService]
})
export class MyComponent{
getData: string;
MyVar: string;
constructor(private _myService: MyService, private _router: Router){}
onDo(){
this._myService.getSomething()
.subscribe(
data => this.getData = JSON.stringify(data),
error => this._router.navigate(['error']),
() => console.log('Get data inside subscribe: '+ this.getData)
);
console.log('Get data outside subscribe'+this.getData);
this.MyVar = this.getData;
}
}
This code
data => this.getData = JSON.stringify(data),
is a function that is passed to subscribe(...) to be called when the data arrives from the server.
You console.log(...) is called immediately after above function is passed to subscribe, but that doesn't mean the data is already there.
Just move the code inside subscribe(...) and it will work as expected.