Reputation: 361
I'm loading a list of items over http and using a BehaviorSubject to stream the updates from an angular service to a component. The items have a boolean on them which is bound to a checkbox.
I want another component to use the same service and subscribe to the same data source, but only show the items that are checked. This code below works fine when the initial data loads. But if the checkbox in the first component is checked, it doesn't update the second component.
Do I need to manually update the object from the first component on check and push it back into the observer?
Full example on stackblitz here
export class MyService {
private mySubject = new BehaviorSubject<MyModel[]>([]);
allObservable: Observable<MyModel[]> = this.mySubject.asObservable();
selectedObservable = this.allObservable.pipe(map(result => result.filter(f => f.selected)));
constructor() {
this.fundsSubject.next([
{ selected: true },
{ selected: false },
]);
}
}
export class MyModel {
public selected: boolean = false;
}
Upvotes: 5
Views: 17165
Reputation: 39432
Even if you're making changes to the data
in the AppComponent
, your MyService
is not aware of the change. Hence it is not pushing new data to the subscribers.
To make it aware of the change, just make the following changes to the following files:
AppComponent Template:
<ul>
<li *ngFor="let row of data">
<input (change)="onChange()" type="checkbox" [(ngModel)]="row.selected"/> {{row.name}}
</li>
</ul>
<hr/>
Selected:
<hello></hello>
This change will actually listen to the change in data
. Notice the (change)
handler added to the input
tag.
AppComponent Class:
import { Component, OnInit } from '@angular/core';
import { MyService, MyModel } from './service.component';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
data: MyModel[];
constructor(private myService : MyService){}
ngOnInit() {
this.myService.allObservable.subscribe(d => this.data = d);
}
onChange() {
this.myService.setUpdatedData(this.data);
}
}
This change will propagate the change to the service by calling the setUpdatedData
on it.
And add this method to the MyService
:
setUpdatedData(data) {
this.mySubject.next(data);
}
This change will propagate the change to all the subscribe
rs.
Here's a Working StackBlitz Sample For your ref.
Upvotes: 2
Reputation: 1449
When you subscribe to your observable you can just add a pipe filter and take only the checked items. Example:
import { filter } from 'rxjs/operators';
import { BehaviorSubject } from 'rxjs';
let _data: BehaviorSubject<Array<string>> = new BehaviorSubject(['']);
_data.subscribe((data) => console.log(1, data)); // takes all results
_data.pipe(filter((stuff: Array<string>) => stuff.some((item) => item === 'batman'))
).subscribe((result) => console.log(2, result)); // only takes results that pass the check
_data.next(['justice']); // test 1
_data.next(['justice', 'batman']); // test 2
Upvotes: 0
Reputation: 311
There is all lot of things going on in your project that can be improved, but basically you need to notify your observer that a change has taken place, there are several way to do this. I have just added one example "that works", without saying it is a good solution.
https://stackblitz.com/edit/angular-akfqha
// app.component.html
<input ... (change)="notifyChange()"/>
// app.component.ts
public notifyChange(): void {
this.myService.updateData(this.data);
}
// service.component.ts (preferable named something.service.ts)
public updateData(newData: MyModel[]): void {
this.mySubject.next(newData);
}
Hope this helps!
Upvotes: 1
Reputation: 5289
If you are importing the service in both the components, angular gives separate instances of the service for each of the components. Consider adding the service in the provider scope.
You can use the annotation to do that:
@Injectable({
providedIn: 'root',
})
Look for documentation here
Upvotes: 0