开发者问题收集

过滤数组并继续获取数据

2020-03-03
75

我正在尝试使用 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

2个回答

应用如下可观察对象:

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 管道一起使用

Ling Vu
2020-03-03

因此,首先,我最近必须专注于 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 .

Elias
2020-03-03