过滤数组并继续获取数据
我正在尝试使用 Angular 构建一个空闲角色扮演游戏。 我有一个控制台日志,显示诸如造成的伤害/获得的经验等事件。
我有一个服务调用 MessageService,其中我有一个 Message 类型(文本、日期、类型)的数组属性。
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() {}
}
我的控制台日志上有按钮,允许用户“过滤”所有消息以仅获取特定类型(战斗/常规/系统)。
我可以使用以下方式进行过滤:messages.filter(message => message.type == 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
);
}
}
}
有什么想法吗?我尝试使用 observable 但没有成功,我想我可能没有很好地实现它。
编辑:我的消息组件看起来像这样:
<div class="log">
<app-message-button-menu
(filter)="filterByType($event)"
></app-message-button-menu>
<app-message-chat [messages]="messages"></app-message-chat>
</div>
我的应用程序消息按钮菜单是这样的:
<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);
}
}
我的应用程序消息按钮是这样的:
<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);
}
}
这是我的应用程序消息聊天:
<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;
}
}
编辑 Ling Vu Answer 工作:
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() {}
}
我的消息组件:
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
);
}
}
}
遗憾的是我没有找到他告诉我的如何在我的组件中实现 Observable 属性。我会找一些教训
谢谢 Ling Vu
应用如下可观察对象:
MessageService
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[] = [];
filteredMessages: ReplaySubject<Message[]>;
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 (!filteredMessages) filteredMessages = new ReplaySubject(1);
this.filteredMessages.next(this.messageService.messages.filter(
item => item.type === this.filter
));
}
constructor() {}
}
并在组件中订阅它。组件中的属性需要是
Observable<Message[]>
。之后,您可以将它与 *ngIf 和
async
管道一起使用
因此,首先,我最近必须专注于
react
,并且无法提供遵循最佳实践的示例。
也就是说,下面的示例应该为您提供一个可以运行的应用程序。哦,还有一件事,使用类型
string
而不是
String
Typescript:String 和 string 之间的区别
。
消息服务:
import {Injectable} from '@angular/core';
import {Message} from './message';
import {MESSAGE_TYPE} from './messageType'
@Injectable()
export class MessageService {
public messages: Message[] = [
{message: 'general', type: MESSAGE_TYPE.GENERAL},
{message: 'general1', type: MESSAGE_TYPE.GENERAL},
{message: 'general2', type: MESSAGE_TYPE.GENERAL},
{message: 'general3', type: MESSAGE_TYPE.GENERAL},
{message: 'combat', type: MESSAGE_TYPE.COMBAT},
{message: 'combat1', type: MESSAGE_TYPE.COMBAT},
{message: 'combat2', type: MESSAGE_TYPE.COMBAT},
{message: 'combat3', type: MESSAGE_TYPE.COMBAT},
];
private add(message: string, type: MESSAGE_TYPE): void {
this.messages.push({message, type});
}
public addGeneralMessage(message: string): void {
this.add(message, MESSAGE_TYPE.GENERAL);
}
public addCombatMessage(message: string): void {
this.add(message, MESSAGE_TYPE.COMBAT);
}
public clear(): void {
this.messages = [];
}
constructor() {}
}
应用组件 ts
import {Component, OnInit} from '@angular/core';
import {MessageService} from './message.service';
import {Message} from './message';
import {MESSAGE_TYPE} from './messageType';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
name = 'Angular';
// "Exporting" enum so it's available in the HTML.
// This is probably not the right way if your way is working, keep it that way.
// I haven't written angular in a long time (had to do react) and so I can't tell you
// best practices.
public MESSAGE_TYPE = MESSAGE_TYPE;
public messages: Message[] = [];
public selectedType: MESSAGE_TYPE = MESSAGE_TYPE.GENERAL;
public constructor(public messageService: MessageService) {}
public ngOnInit() {
this.goAndFilter();
}
public filterMessages(type: MESSAGE_TYPE) {
this.selectedType = type;
this.goAndFilter();
}
// really creative I know
private goAndFilter(): void {
this.messages = this.messageService.messages.filter(_ => _.type === this.selectedType);
}
}
应用组件 html
<p>Selected type: {{selectedType}}<P>
<Button
(click)="filterMessages(MESSAGE_TYPE.COMBAT)"
>Show Combat</Button>
<Button
(click)="filterMessages(MESSAGE_TYPE.GENERAL)"
>Show General</Button>
<div *ngFor="let message of messages">
<p>{{message.message}} - {{message.type}}</p>
</div>
这是一个 StackBlitz,但我不知道这是否是持久的: https://stackblitz.com/edit/angular-vwpi6n?file=src%2Fapp%2Fapp.component.html .