Edit: I have uploaded the project to StackBlitz https://stackblitz.com/edit/angular-ivy-kfyhcp?file=src/app/chat-view/chat-view.component.ts
Through more testing I found out that the issue is being caused by this.router.navigate(['view']); on line 54 in file-upload.component.ts. If I remove this and just embed the ChatViewComponent on my page normally instead of within a router-outlet, the app works as expected. I will update with a solution if I figure out how to get the functionality working from within a router-outlet.
import { Component, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MessageParsingService } from '../services/message-parsing.service';
import { Router } from '@angular/router';
@Component({
selector: 'file-upload',
templateUrl: './file-upload.component.html',
styleUrls: ['./file-upload.component.scss'],
})
export class FileUploadComponent implements OnInit {
fileName = '';
constructor(
private messageParsingService: MessageParsingService,
private router: Router,
public dialog: MatDialog
) {}
ngOnInit(): void {}
readFileContent(file: File): Promise<string> {
return new Promise<string>((resolve) => {
if (!file) {
resolve('');
}
const reader = new FileReader();
reader.onload = (e) => {
const text = reader.result!.toString();
resolve(text);
};
reader.readAsText(file);
});
}
async onFileSelected(event: any) {
const file: File = event.target.files[0];
const fileReader = new FileReader();
if (file) {
console.log(file.type + ' uploaded');
this.fileName = file.name;
const formData = new FormData();
formData.append('thumbnail', file);
const fileContent = await this.readFileContent(file);
console.log(this.fileName);
if (file.type == 'application/json') {
this.messageParsingService.parseJson(fileContent);
}
this.router.navigate(['view']);
}
}
}
I have the following Angular service to parse a JSON file of messages, which works as expected, and is using an EventEmitter to send the parsed messages to a display component, which is not working as it's supposed to:
import { EventEmitter, Injectable } from '@angular/core';
import { Message } from '../models/message';
import { ParseEvent } from './parse-event';
@Injectable({
providedIn: 'root'
})
export class MessageParsingService {
public onParseComplete: EventEmitter<ParseEvent> = new EventEmitter<ParseEvent>();
public messages: Message[];
constructor() {
this.messages = [];
}
public parseJson(jsonString: string) {
let jsonObj = JSON.parse(jsonString);
this.messages = jsonObj.chats[0].messages.map((item: { timestamp: any; fromMe: any; text: any; }) => {
var msg = {
timestamp: item.timestamp,
fromMe: item.fromMe,
text: item.text,
}
this.onParseComplete.emit(new ParseEvent(msg));
return msg;
});
console.log("Messaged parsed by service: " , this.messages);
}
}
Sample JSON file I'm using for testing:
{
"chats": [
{
"contactName": "Test",
"messages": [
{
"timestamp": "2022-12-11T17:00:12Z",
"id": "35A8984427489ACE786A9F5DFA81E7",
"fromMe": true,
"type": "text",
"text": "Hello!"
},
{
"timestamp": "2022-12-11T21:34:04Z",
"id": "009715FBED647B52CD4EA6AE9A7CA4",
"fromMe": false,
"type": "text",
"text": "How are you?"
},
{
"timestamp": "2022-12-11T21:34:23Z",
"id": "479AFD522FEC7444991CCA578EDF9F",
"fromMe": false,
"type": "text",
"text": "What are you doing today?"
}
]
}
]
}
Code for the ParseEvent:
import { Message } from "../models/message";
export class ParseEvent {
message: Message;
constructor(m: Message){
this.message = m;
}
}
Component to view messages:
import { Component, OnInit } from '@angular/core';
import { Message } from '../models/message';
import { MessageParsingService } from '../services/message-parsing.service';
import { ParseEvent } from '../services/parse-event';
@Component({
selector: 'app-chat-view',
templateUrl: './chat-view.component.html',
styleUrls: ['./chat-view.component.scss']
})
export class ChatViewComponent implements OnInit {
private _serviceSubscription;
messages: Message[];
constructor(private messageParsingService: MessageParsingService) {
this.messages = [];
this._serviceSubscription = this.messageParsingService.onParseComplete.subscribe({
next: (event: ParseEvent) => {
console.log('Received message from parseEvent', event.message);
this.messages.push(event.message);
}
})
}
public logMessages(){
console.log("Log current messages: ", this.messages);
}
ngOnInit(): void {
console.log('chat view component msgs: ', this.messages);
}
}
HTML for display component:
<app-message
*ngFor="let m of messages"
[timestampInput]="m.timestamp"
[fromMeInput]="m.fromMe"
[textInput]="m.text"
>
</app-message>
<button mat-raised-button (click)="logMessages()">Log current messages</button>
In my view component, I have subscribed to the above event emitter from the parsing service. Each time an event is triggered, I am pushing the message attached to the event onto the list in ChatViewComponent.ts - I can see the messages passing through via the console.log statements, as shown in the screenshot below. However when I try and access the list from outside of the subscribe block, for example in the logMessages() button, or within ChatViewComponent.html, it returns an empty list. I tried refactoring my code to use a BehaviorSubject and Observable instead of EventEmitters, but faced the same issue where the list stays empty. I also tried changing the list in ChatViewComponent to type 'any' just in case the objects were being formatted incorrectly, but that didn't help either.
After checking your code on stackblitz, I recoment you to refactor the code a little
so, your refactored code is below https://stackblitz.com/edit/angular-ivy-exyyf6?file=src%2Fapp%2Fservices%2Fmessage.service.ts