Searching for the best way to implement Angular services has led me here.
The Service:
const url = 'http://127.0.0.1:8000/api/brands/'
@Injectable()
export class BrandService {
private brands:Observable<Array<Brand>>;
constructor(private http: Http) { }
list(): Observable<Array<Brand>> {
if(!this.brands){
this.brands = this.http.get(url)
.map(response => response.json())
.publishReplay(1)
.refCount();
}
return this.brands;
}
clearCache() {
this.brands = null;
}
create(brand: Brand): Observable<Brand> {
Object.entries(brand).forEach(([key, value]) => {
formData.append(key, value);
});
return this.http.post(url+'create/', formData)
.map(response => response.json())
.catch(this.handleError);
}
get(id): Observable<Brand> {
return this.http.get(url+id)
.map(response => response.json())
.catch(this.handleError);
}
private handleError(error:any, caught:any): any {
console.log(error, caught);
}
}
I've successfully implemented caching using the publishReplay method with the Observable object. Now, my goal is to automatically update the list every minute and notify subscribers if there are changes in the list. I attempted to use setInterval(this.clearCache, 1000*60) but it only clears the cache without updating the list as desired.
What would be the most effective approach to stay updated on data while limiting server requests?
UPDATE 1 (Validator Issue):
Following martin's suggestion, I made changes to the list method:
list(): Observable<Array<Brand>> {
if(!this.brands){
this.brands = Observable.timer(0, 60 * 1000)
.switchMap(() => {
console.log('REQUESTING DATA....')
return this.http.get(url);
})
.map(response => response.json())
.publishReplay(1)
.refCount();
}
return this.brands;
}
This new implementation works well, except for validators.
The brandNameValidator previously worked fine:
private brandNameValidator(control: FormControl) {
return this.brandService.list().map(res => {
return res.filter(brand =>
brand.name.toLowerCase() === control.value.toLowerCase() && (!this.editMode || brand.id != this.brand.id)
).length>0 ? { nameAlreadyExist: true } : null;
});
}
However, the field now remains in PENDING status.
UPDATE 2 (Validator Solution):
To resolve the issue, I employed the Promise object:
private brandNameValidator(control: FormControl) {
return new Promise(resolve => {
let subscription = this.brandService.list().subscribe(res => {
let brandsFound = res.filter(brand =>
brand.name.toLowerCase() === control.value.toLowerCase() && (!this.editMode || brand.id != this.brand.id)
)
if (brandsFound.length>0) {
resolve({ nameAlreadyExist: true });
} else {
resolve(null);
}
subscription.unsubscribe();
})
});
}
UPDATE 3 (Forcing List Update):
After creating a new brand, I aim to immediately force an update of the list instead of waiting for the next scheduled update. This requires notifying all observers about the addition without utilizing the next() method since the object is an Observable and not a Subject.
create(brand: Brand): Observable<Brand> {
Object.entries(brand).forEach(([key, value]) => {
formData.append(key, value);
});
return this.http.post(url+'create/', formData)
.map(response => {
// TODO - Need to update this.brands, but I cannot use the next() method since it isn't a Subject object, but an Observable.
// All observers need to be updated about the addition
return response.json();
})
.catch(this.handleError);
}