Currently, I am in the process of developing an idle RPG game using Angular. One of the features I have implemented is a console log that displays events such as Damage Dealt and Experience Earned.
In order to manage messages efficiently, I have created a service called MessageService which contains an array property consisting of Messages with attributes like text, date, and type.
import { Message } from "@core/models/message";
import { Injectable } from "@angular/core";
import { MESSAGE } from "@core/constant/constant";
@Injectable({
providedIn: "root"
})
export class MessageService {
messages: Message[] = [];
private add(message: String, type: string) {
this.messages.push(new Message(message, type));
}
addGeneralMessage(message: String) {
this.add(message, MESSAGE.GENERAL);
}
addCombatMessage(message: String) {
this.add(message, MESSAGE.COMBAT);
}
clear() {
this.messages = [];
}
constructor() {}
}
To enhance user experience, I have incorporated buttons above the console log that allow users to filter messages based on specific types (Combat / General / System).
Although I can successfully filter messages using
messages.filter(message => message.type == type)
, I am facing challenges in dynamically receiving new messages of the selected type.
import { Message } from "@core/models";
import { MESSAGE } from "@core/constant/constant";
import { MessageService } from "@core/services";
import { Component, OnInit } from "@angular/core";
@Component({
selector: "app-message",
templateUrl: "./message.component.html",
styleUrls: ["./message.component.scss"]
})
export class MessageComponent implements OnInit {
messages: Message[];
constructor(public messageService: MessageService) {}
ngOnInit() {
this.messages = this.messageService.messages;
}
filterByType(type: String) {
if (type == MESSAGE.ALL) {
this.messages = this.messageService.messages;
} else {
this.messages = this.messageService.messages.filter(
item => item.type == type
);
}
}
}
Any suggestions or solutions would be greatly appreciated. I attempted to use observables without success, indicating a possible implementation error on my part.
EDIT : Here is how my message component looks:
<div class="log">
<app-message-button-menu
(filter)="filterByType($event)"
></app-message-button-menu>
<app-message-chat [messages]="messages"></app-message-chat>
</div>
Details about my app-message-button-menu:
<div class="menuLog">
<app-message-button
[text]="'All'"
[type]="MESSAGE.ALL"
[active]="activeButton == MESSAGE.ALL"
(messageType)="onFilter($event)"
></app-message-button>
<app-message-button
[text]="'General'"
[type]="MESSAGE.GENERAL"
[active]="activeButton == MESSAGE.GENERAL"
(messageType)="onFilter($event)"
></app-message-button>
<app-message-button
[text]="'Fight'"
[type]="MESSAGE.COMBAT"
[active]="activeButton == MESSAGE.COMBAT"
(messageType)="onFilter($event)"
></app-message-button>
<app-message-button
[text]="'System'"
[type]="MESSAGE.SYSTEM"
[active]="activeButton == MESSAGE.SYSTEM"
(messageType)="onFilter($event)"
></app-message-button>
</div>
import { Component, OnInit, Output, EventEmitter, Input } from "@angular/core";
@Component({
selector: "app-message-button",
templateUrl: "./message-button.component.html",
styleUrls: ["./message-button.component.scss"]
})
export class MessageButtonComponent implements OnInit {
@Input() type: String;
@Input() text: String;
@Input() active: boolean;
@Output() messageType = new EventEmitter<String>();
constructor() {}
ngOnInit() {}
filter() {
this.messageType.emit(this.type);
}
}
import { Component, OnInit, Output, EventEmitter, Input } from "@angular/core";
@Component({
selector: "app-message-button",
templateUrl: "./message-button.component.html",
styleUrls: ["./message-button.component.scss"]
})
export class MessageButtonComponent implements OnInit {
@Input() type: String;
@Input() text: String;
@Input() active: boolean;
@Output() messageType = new EventEmitter<String>();
constructor() {}
ngOnInit() {}
filter() {
this.messageType.emit(this.type);
}
}
Information regarding app-message-button:
<button [ngClass]="{ active: active == true }" (click)="filter()" type="button">
{{ text }}
</button>
import { Component, OnInit, Output, EventEmitter, Input } from "@angular/core";
import { MESSAGE } from "@core/constant/constant";
@Component({
selector: "app-message-button-menu",
templateUrl: "./message-button-menu.component.html",
styleUrls: ["./message-button-menu.component.scss"]
})
export class MessageButtonMenuComponent implements OnInit {
MESSAGE;
activeButton: String;
@Output() filter = new EventEmitter<String>();
constructor() {}
ngOnInit(): void {
this.MESSAGE = MESSAGE;
this.activeButton = MESSAGE.ALL;
}
onFilter(type: String) {
this.activeButton = type;
this.filter.emit(type);
}
}
Lastly, here is information regarding app-message-chat:
<ul>
<app-message-item
*ngFor="let message of messages; trackBy: trackBy"
[message]="message"
></app-message-item>
</ul>
import { Component, OnInit, Input } from "@angular/core";
import { Message } from "@core/models/message";
@Component({
selector: "app-message-chat",
templateUrl: "./message-chat.component.html",
styleUrls: ["./message-chat.component.scss"]
})
export class MessageChatComponent implements OnInit {
@Input("messages") messages: Message[];
constructor() {}
ngOnInit(): void {}
trackBy(index: number, item: Message): Message {
return item;
}
}
UPDATE: Ling Vu's solution worked for me:
import { Message } from "@core/models/message";
import { Injectable } from "@angular/core";
import { MESSAGE } from "@core/constant/constant";
import { ReplaySubject } from "rxjs";
@Injectable({
providedIn: "root"
})
export class MessageService {
messages: Message[] = [];
filteredMessages: ReplaySubject<Message[]> = new ReplaySubject(1);
filter: String;
private add(message: String, type: string) {
this.messages.push(new Message(message, type));
this.filterMessages();
}
addGeneralMessage(message: String) {
this.add(message, MESSAGE.GENERAL);
}
addCombatMessage(message: String) {
this.add(message, MESSAGE.COMBAT);
}
clear() {
this.messages = [];
}
setFilter(filter: String) {
this.filter = filter;
}
filterMessages() {
if (!this.filteredMessages)
this.filteredMessages = new ReplaySubject(1);
if (this.filter === MESSAGE.ALL) {
this.filteredMessages.next(this.messages);
} else {
this.filteredMessages.next(
this.messages.filter(item => item.type === this.filter)
);
}
}
constructor() {}
}
And here's the updated message component:
export class MessageComponent implements OnInit {
messages: Message[];
constructor(public messageService: MessageService) {}
ngOnInit() {
this.messageService.setFilter(MESSAGE.ALL);
this.messageService.filteredMessages.subscribe(
messages => (this.messages = messages)
);
}
filterByType(type: String) {
this.messageService.setFilter(type);
if (type === MESSAGE.ALL) {
this.messages = this.messageService.messages;
} else {
this.messages = this.messageService.messages.filter(
messages => messages.type === type
);
}
}
}
Unfortunately, I wasn't able to implement the Observable property in my component as suggested. I will continue researching for further insight.
A special thanks to Ling Vu for the assistance!