How to subscribe Input property to call out function Angular 2+ - javascript

Hi I have an issue like in subject:
My Parent component passes the value to his child component like this:
<em-event-list [sumListEvents]="sumListEvents"></em-event-list>
So when when value sumListEvents will change I want to subscribe that value to call out the function from Service. This is how my children component looks:
#Input() sumListEvents: Observable<number>;
private events: Event[] = [];
constructor(private dataService: EventListService) {
let loadSubscription = this.sumListEvents.subscribe(
value => {
this.events = this.dataService.showEvents(value)
}
)
}
But I receive error on subscribe Cannot read property 'subscribe' of undefined in the sumListEvents. Any ideas?

sumListEvents is not available at the moment you need it. You can use *ngIf to subscribe only when sumListEvents available.
<em-event-list *ngIf [sumListEvents]="sumListEvents"></em-event-list>
This will work but it might not be a best practise to pass event to the child and subscribe it in a child.
You may refer to https://scotch.io/tutorials/3-ways-to-pass-async-data-to-angular-2-child-components for other ways to pass async data to child component.

A better approach will be to use the async pipe and a setter.
<em-event-list *ngIf [sumList]="sumListEvents$ | async"></em-event-list>
#Input() set sumList(value) {
if(value) {
this.events = this.dataService.showEvents(value);
}
}
private events: Event[] = [];
constructor(private dataService: EventListService) { }

Related

Cannot read property 'charAt' of undefined Get #input

I'm trying to pass my Get method from my parent component to my child component to display the data but I get an error I don't understand.
the error comes from my service
here is the error Cannot read property 'charAt' of undefined
thanks in advance.
parent.component.html
<app-child [arrayList]="getDetails()"></app-child>
parents.component.ts
public array: any[] = [];
getRoute() {
this.route.paramMap.subscribe((params: Param) => {
this.id = params.get('id');
this.material = params.get('matos');
}
}
getDetails() {
this.service.get(this.id, this.material).subscribe((data:any[]) => {
this.array.push(data);
}
}
parent.service
get(id: string, material:string) {
let material = this.str(material:string);*
return this.get<any>(url+id+material);
str(material) {
return material.charAt(0).toUpperCase()+material.substr(1);
}
child.component.ts
#Input() arrayList: any[] = [];
There are several glitches in your code, first in child component add input decorator, so that it accepts the input i.e
child.component.ts
#Input() arrayList: any;
parents.component.html
In parents component you are passing getDetails() method to input which will always be undefined since its not returning anything, try to pass array list in which you are pushing the data
<app-child [arrayList]="array"></app-child>

Get value from Observable before re-execution

Im currently getting the new updated user value this way:
this.Service.user$.subscribe(data => {
this.userData = data;
this.userId = data._id;
});
but the updateUser is only executed every 5 secs.
So before its loaded the userData and UserId is empty.
is there a way i can get the stored user data from whats already in the service, instead of waiting 5 secs to it beeing executed again?
something like:
this.Service.user$().GET((data:any) => { // gets the value already stored
});
How would i accomplish this?
Service code:
user$: Observable<any>;
constructor(private http: HttpClient, private router: Router) {
this.user$ = this.userChangeSet.asObservable();
}
updateUser(object) {
this.userChangeSet.next(object);
}
Edit:
Also, how would i destory all subscribes on ngOnDestroy event?
What you can do in your service is internally use a BehaviourSubject to
store the values but expose this as an Observable.
Here is a quote from the docs detailing what a BehaviourSubject is
One of the variants of Subjects is the BehaviorSubject, which has a notion of "the current value".
It stores the latest value emitted to its consumers, and
whenever a new Observer subscribes, it will immediately receive the "current value" from the BehaviorSubject
See here for more.
Service code:
private _user$ = new BehaviourSubject<any>(null); // initially null
constructor(private http: HttpClient, private router: Router) {
this.userChangeSet.subscribe(val => this._user$.next(val))
}
get user$ () {
return this._user$.asObservable();
}
Then you can use it like normal in your component.
this.service.user$.subscribe(v => {
// do stuff here
})
Note that the first value
that the component will get will be null since this is the inital value of
the BehaviourSubject.
EDIT:
In the component
private _destroyed$ = new Subject();
public ngOnDestroy (): void {
this._destroyed$.next();
this._destroyed$.complete();
}
And then for the subscription
this.service.user$.pipe(
takeUntil(this._destroyed$)
).subscribe(v => {
// do stuff here
})
The way this works is that when the destroyed$ subject emits, the observables that have piped takeUntil(this._destroyed$) will unsubscribe from their respective sources.
Use BehaviorSubject for userChangeSet. It emits value immediately upon subscription.
Example:
userChangeSet = new BehaviorSubject<any>(this.currentData);

Storing ag-grid rowclicked event data Angular

I am trying to store the event data from the onRowClicked event in a Component member. So that when the user hits a button it will be deleted. However when I try accessing it from the delete callback the member variable is undefined.
export class OilTypesComponent implements OnInit {
...
selectedOil : any;
gridOptions: GridOptions = <GridOptions>{};
ngOnInit() {
this.gridOptions = {
...
onCellEditingStopped: this.cellEdited,
onRowClicked: this.rowClicked
}
}
...
rowClicked(event){
this.selectedOil = event.data;
}
delete(){
console.log(`Deleting ${this.selectedOil.manufacturer} //this.selectedOil is undefined
}
Turns out it was a scoping issue when passing in the callbacks as shown here:
Angular2 component's "this" is undefined when executing callback function
What I ended up doing for both callbacks
this.gridOptions = {
...
onCellEditingStopped: this.cellEdited.bind(this),
onRowClicked: this.rowClicked.bind(this)
}

Data set in observable not updating in template

I'm trying to learn Angular 2 and am rebuilding an Angular 1 app I've made with Angular 2 using the Angular CLI. I've setup a HTTP GET request, which fires successfully, and setup a subscriber to interpret the result, and console logging in the subscriber function shows the data I expect. However, no data is being updated on the template.
I tried setting the data to an initial value, to a value in the ngOnInit, and in the subscriber function, and the initial and ngOnInit update the template accordingly. For the life of me, I can't figure out why the template won't update on the subscribe.
events: any[] = ['asdf'];
constructor(private http: Http) {
}
ngOnInit() {
this.events = ['house'];
this.getEvents().subscribe(this.processEvents);
}
getEvents(): Observable<Event[]> {
let params: URLSearchParams = new URLSearchParams();
params.set('types', this.filters.types.join(','));
params.set('dates', this.filters.dates.join(','));
return this.http
.get('//api.dexcon.local/getEvents.php', { search: params })
.map((response: Response) => {
return response.json().events;
});
}
processEvents(data: Event[]) {
this.events = ['car','bike'];
console.log(this.events);
}
The data is being displayed via an ngFor, but car and bike never show. Where have I gone wrong?
You have gone wrong with not respecting the this context of TypeScript, if you do stuff like this:
.subscribe(this.processEvents);
the context get lost onto the processEvents function.
You have to either bind it:
.subscribe(this.processEvents.bind(this));
Use an anonymous function:
.subscribe((data: Events) => {this.processEvents(data)});
Or set your method to a class property:
processEvents: Function = (data: Event[]) => {
this.events = ['car','bike'];
console.log(this.events);
}
Pick your favourite, but I like the last option, because when you use eventListeners you can easily detach them with this method.
Not really sure with what's going on with that processEvents. If you want to subscribe to your response just do:
this.getEvents()
.subscribe(data => {
this.events = data;
});

Angular2 - pass ASYNC data to child component

I have issue, with passing async data to child component. I trying to write dynamic form generator. Issue starts when I try to call json via Observable and pass it into child component.
service:
generateSearchFields2(): Observable<any> {
return this.http
.get(this.API + 'searchFields')
.map((res:Response) => {
res.json().data as any;
for (var i = 0; i < res.json().data.length; i++) {
var searchField = res.json().data[i];
switch (searchField.component) {
case "TextboxQuestion":
let TXQ: TextboxQuestion = new TextboxQuestion({
key: searchField.key,
label: searchField.label,
value: searchField.value,
required: searchField.required,
order: searchField.order
});
this.searchFieldModels.push(TXQ);
console.log("TXQ: ", TXQ, this.searchFieldModels);
break;
case "DropdownQuestion":
let DDQ: DropdownQuestion = new DropdownQuestion({
key: searchField.key,
label: searchField.label,
required: searchField.required,
options: searchField.options,
order: searchField.order
});
this.searchFieldModels.push(DDQ);
console.log("TXQ: ", DDQ, this.searchFieldModels);
break;
default:
alert("DEFAULT");
break;
}
}
return this.searchFieldModels.sort((a, b) => a.order - b.order);
})
.catch(this.handleError);
}
Component Parent:
generateSearchFields2() {
this.service.generateSearchFields2()
.subscribe(res => this.searchFields = res)
}
Iam passing variable via INPUT directive in parent template to child: [searchFields]="searchFields"
Issue is in child component, where searchField has undefined value. In this child I pass value to another service, to create formContros, but I got undefined there also. Data missing starts here, in child:
#Input() searchFields: SearchBase<any>[] = [];
ngOnInit() {
this.form = this.qcs.toFormGroup(this.searchFields);
console.log("ONINIT DYNAMIC FORM COMPONENT: ", this.searchFields);
}
Please for hint how I can pass async variable, to not loose data meantime
You can make #Input() searchFields a setter
private _searchFields: SearchBase<any>[] = [];
#Input() set searchFields(value SearchBase<any>[]) {
if(value != null) {
this.form = this.qcs.toFormGroup(this.searchFields);
console.log("ONINIT DYNAMIC FORM COMPONENT: ", this.searchFields);
}
}
get searchFields() : SearchBase<any>[] {
return this.searchFields;
}
You can also use ngOnChanges() which is called every time an input is updated, but a setter is usually more convenient except perhaps when the executed code depends on multiple inputs being set.
In the ngOnInit event the data which comes from the parent is not bound yet. So your searchFields is undefined yet. You can use it in NgAfterViewInit component lifecycle event.
#Input() searchFields: SearchBase<any>[] = [];
ngAfterViewInit() {
this.form = this.qcs.toFormGroup(this.searchFields);
console.log("ONINIT DYNAMIC FORM COMPONENT: ", this.searchFields);
}
For other cases you can see Angular2 Component Lifecycle events

Categories