Struggling with RxJs Observables and need some assistance. I'm a beginner in working with them.
I am attempting to create an Observable logging stream that serves two purposes:
- Push any new line/value written to the log file to the stream.
- Begin with pre-populated values from the log file.
I have successfully met both requirements. The current challenge lies in using it with *ngFor.
*ngFor necessitates an array from an Observable for comparison to facilitate adding/removing elements (my assumption). However, my observable only returns an array of the most recently pushed item.
//logviewer.page.ts constructor()
this.logs = Subject.create();
this.logs$ = this.logs.asObservable()
.startWith("logs\\error.log")
.flatMap((fileName: string) => {
//begin by reading the existing log files as a string
return this.$localStorageService.readAsStringAsync(fileName);
})
.map((contents: string) => {
//splitting the log file line-by-line into a log entry
let logs = contents.split(/\r\n|\r|\n/).filter(n => n.length > 0);
logs.forEach((s, ix, parent) => {
let x = JSON.parse(s);
parent[ix] = { timestamp: new Date(parseFloat(x[0])), message: x[1] };
})
return logs; //an array of objects { timestamp, message }
})
//merge the existing application log stream
//throughout the application we log errors, info, etc
//if a new entry is made it will appear here
.merge(this.$loggerService.applicationLog$.map((x) => {
//return an array with one object { timestamp, message }
return [{ timestamp: new Date(parseFloat(x[0])), message: x[1] }];
}))
The template at present is straightforward.
//logviewer.template.ts
<div *ngFor="let entry of logs$ | async">
{{entry|json}}
</div>
To test it, I have added a button to insert an entry.
//logviewer.page.ts
addEntry() {
this.$loggerService.error("this is a test");
}
//LoggerService.service.ts
private applicationLog: ReplaySubject<any[]>;
get applicationLog$(): Observable<any[]> {
return this.applicationLog.asObservable();
}
error(...args) {
let data = [Date.now().toString()].concat(args.map<string>((n, ix) => { return toString(n); }));
//...write to file
//send through the subject
this.applicationLog.next(data);
}
Upon clicking addEntry
, all components function correctly, and the value navigates through the observable sequence properly. However, *ngFor updates solely with a single value instead of maintaining history of previous log entries, displaying only the last returned array which is reasonable.
How can I ensure my observable sequence always presents an array containing all values? It currently delivers one entry at a time but I need the entire history for *ngFor.
I had misconceptions about *ngFor and the async pipe. Believed that it would automatically subscribe to the observable and add new entries to *ngFor but apparently not.