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
}
Related
I want to send message from ComponentA to ComponentB. And I am using service for that. Here is the code-
My problem is if I call sendInfoToB() from componentA's html file (event hooked to a button click), then I'm recieving the message in componentB /getting
console.log(this.check); and console.log(message); 's values in the console.. but when I call this.sendInfoToB(true); from componentA's ngOnInit() then I get no message in console for componentB. I want the message when I call from componentA's ngOnInit. How can I achieve that? Please help. Thank you in advance.
ComponentA.ts
constructor(private siblingService: SiblingService,) {}
public dataX: boolean = false;
someFunc(){
//some calculation returning true
this.dataX = true;
}
ngOnInit() {
this.someFunc();
this.sendInfoToB();
}
sendInfoToB() {
this.siblingService.communicateMessage(dataX);
}
message.service.ts
export class SiblingService {
sendMessage = new Subject();
constructor() {}
communicateMessage(msg) {
this.sendMessage.next(msg);
}
}
ComponentB.ts
constructor(private sibService: SiblingService,) {}
ngOnInit() {
this.sibService.sendMessage.subscribe((message) => {
this.check = message;
console.log(this.check);
console.log(message);
});
}
More than likely this has to do with order of operations. Component A is created and sends the message, then Component B gets created and subscribes to new messages. Since you are using a Subject it only gives you messages sent after you subscribe to it.
The easiest strategy to this is to use a BehaviorSubject.
export class SiblingService {
sendMessage = new BehaviorSubject();
constructor() {}
communicateMessage(msg) {
this.sendMessage.next(msg);
}
}
The other way is to send the message after the init happens, like on a click event.
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'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.
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 have an issue with ngonchanges not firing.
I have this component:
#Component({
selector: 'conversation-detail',
templateUrl: './conversation-detail.component.html'
})
export class ConversationDetailComponent implements OnChanges{
#Input()
selectedConversation: Conversation;
title = 'Conversation Detail';
convoMessages: Array<Message> = [];
constructor(
private _messageService: MessageService
){};
ngOnChanges(changes: SimpleChanges){
this.getMessages();
}
ngOnInit(): void{
}
private getMessages(){
if(this.selectedConversation != undefined){
this._messageService.getMessagesForConversation(this.selectedConversation.ConversationId).then(
data => {
if (data) {
this.convoMessages = data.serviceResult as Array<Message>;
} else {
//This really should never happen
console.error('Error retrieving users data: Data object is empty');
}
},
error => {
//loader.dismiss();
console.error('Error retrieving users data');
console.dir(error);
}
);
}
}
}
The ngonchanges will fire the first time selectedConversation changes. GetMessages is called and it loads in the message data. After that ngonchanges no longer fires when selectedConversation changes.
If I comment out the function call to getMessages then ngonchanges will fire every time selectedConversation changes.
so, I'm not sure what is going on in getMessages that cuts off ngonchanges from firing off. The request returns data and the call ends. Anyone have any ideas?
ngOnChanges only fire if #Input change reference, for example Input type of string, boolean, number, immutable object.
assume [selectedConversation] bind to an array arrSelected, if you do something like arrSelected.push, the array does not change ref so ngOnChanges won't fire.
so you need using immutable array, using concat, slice, etc to change the array reference
Please change your component as :
#Component({
selector: 'conversation-detail',
templateUrl: './conversation-detail.component.html',
changeDetection: ChangeDetectionStrategy.Default
})
This works fine for me.