网站搜索

typescript - 在 Angular 中将搜索栏的数据调用到 chart.js 图表


我想在 Angular 9 中创建一个 chart.js 的条形图。用户应该在搜索栏中输入一个字符串,这会在一个 .json 文件中查找相应的条目,并将其属性显示在条形图中。例如,你可以输入“甜椒”,它会显示一个图表,其中一个条形名为“甜”,高度为 3,另一个条形名为“酸”,高度为 1。搜索组件可以工作,我可以输入名字并以文本形式返回值。然而,我无法让它返回一个正确的图表。

如果这个问题对你们来说很基础,我很抱歉,我只是一个正在学习编程的业余爱好者。我到处查找这个问题,但找不到这个问题或者任何解决方法。

这是 home-page.component.ts:

import { Component, OnInit } from '@angular/core';
import { DataService } from '../../data.service';
import { Post } from '../../post';
import { Chart } from 'chart.js';
@Component({
  selector: 'app-home-page',
  templateUrl: './home-page.component.html',
  styleUrls: ['./home-page.component.css']
})
export class HomePageComponent implements OnInit {

  post: Post[]
  constructor(
    public dataService: DataService
  ) { }

  ngOnInit() {
    this.dataService.getPosts().subscribe(posts => {
      this.post = posts
      this.dataService.postsData = posts
    });
  }

  onSelectedFilter(e) {
    this.getFilteredExpenseList();

    let postname = this.post.name;
    let postsour = this.post.sour;
    let postsweet = this.post.sweet;

    this.chart = new Chart('canvas', {
      type: 'bar',
      data: {
        labels: postname,
        datasets: [
          {
            data: postsour,
            borderColor: "#3cba9f",
            fill: false
          },
          {
            data: postsweet,
            borderColor: "#ffcc00",
            fill: false
          },
        ]
      },
      options: {
        legend: {
          display: false
        },
        scales: {
          xAxes: [{
            display: true
          }],
          yAxes: [{
            display: true
          }],
        }
      }
    });

  }

  getFilteredExpenseList() {
    if (this.dataService.searchOption.length > 0)
      this.post = this.dataService.filteredListOptions();
    else {
      this.post = this.dataService.filteredListOptions();
    }

  }

}

这是 search-bar.component.ts:

import { Component, OnInit, ViewChild, ElementRef, EventEmitter, Output } from '@angular/core';
import { FormControl } from '@angular/forms';
import { Observable } from 'rxjs';
import { DataService } from '../../data.service';
import { Post } from '../../post';

@Component({
    selector: 'app-search-bar',
    templateUrl: './search-bar.component.html',
    styleUrls: ['./search-bar.component.css']
})
export class SearchBarComponent implements OnInit {

    myControl = new FormControl();
    filteredOptions: Observable<string[]>;
    allPosts: Post[];
    autoCompleteList: any[]

    @ViewChild('autocompleteInput') autocompleteInput: ElementRef;
    @Output() onSelectedOption = new EventEmitter();

    constructor(
        public dataService: DataService
    ) { }

    ngOnInit() {

        // get all the post
        this.dataService.getPosts().subscribe(posts => {
            this.allPosts = posts

        });

        // when user types something in input, the value changes will come through this
        this.myControl.valueChanges.subscribe(userInput => {
            this.autoCompleteExpenseList(userInput);
        })
    }

    private autoCompleteExpenseList(input) {
        let categoryList = this.filterCategoryList(input)
        this.autoCompleteList = categoryList;
    }

    // this is where filtering the data happens according to you typed value
    filterCategoryList(val) {
        var categoryList = []
        if (typeof val != "string") {
            return [];
        }
        if (val === '' || val === null) {
            return [];
        }
        return val ? this.allPosts.filter(s => s.name.toLowerCase().indexOf(val.toLowerCase()) != -1)
            : this.allPosts;
    }

    // after you clicked an autosuggest option, this function will show the field you want to show in input
    displayFn(post: Post) {
        let k = post ? post.name : post;
        return k;
    }

    filterPostList(event) {
        var posts = event.source.value;
        if (!posts) {
            this.dataService.searchOption = []
        }
        else {

            this.dataService.searchOption.push(posts);
            this.onSelectedOption.emit(this.dataService.searchOption)
        }
        this.focusOnPlaceInput();
    }

    removeOption(option) {

        let index = this.dataService.searchOption.indexOf(option);
        if (index >= 0)
            this.dataService.searchOption.splice(index, 1);
        this.focusOnPlaceInput();

        this.onSelectedOption.emit(this.dataService.searchOption)
    }

    // focus the input field and remove any unwanted text.
    focusOnPlaceInput() {
        this.autocompleteInput.nativeElement.focus();
        this.autocompleteInput.nativeElement.value = '';
    }


}

这是所引用的 post.ts:

export interface Post {
    name: string,
    type: string,
    sweet: number,
    sour: number,
    salty: number,
    bitter: number,
    fatty: number,
    umami: number,
    spicy: number,
}

这是 .json 文件的一个示例:

[
  {
    "name": "bell pepper",
    "type": "vegetable",
    "sweet": 3,
    "sour": 1,
    "salty": 1,
    "bitter": 2,
    "fatty": 0,
    "umami": 0,
    "spicy": 2
  },
  {
    "name": "salt",
    "type": "spice",
    "sweet": 0,
    "sour": 0,
    "salty": 10,
    "bitter": 0,
    "fatty": 0,
    "umami": 0,
    "spicy": 0
  },
  {
    "name": "beef",
    "type": "meat",
    "sweet": 1,
    "sour": 0,
    "salty": 2,
    "bitter": 0,
    "fatty": 4,
    "umami": 5,
    "spicy": 0
  },
  {
    "name": "Apple",
    "type": "fruit",
    "sweet": 6,
    "sour": 3,
    "salty": 0,
    "bitter": 0,
    "fatty": 0,
    "umami": 0,
    "spicy": 0
  }
]

这里是数据服务:

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { Post } from './post';


@Injectable({
  providedIn: 'root'
})
export class DataService {

  searchOption = []
  public postsData: Post[]
  postUrl: string = "../assets/posts.json";


  constructor(
    private http: HttpClient
  ) { }


  getPosts(): Observable<Post[]> {
    return this.http.get<Post[]>("../assets/flavors.json");

  }

  filteredListOptions() {
    let posts = this.postsData;
    let filteredPostsList = [];
    for (let post of posts) {
      for (let options of this.searchOption) {
        if (options.name === post.name) {
          filteredPostsList.push(post);
        }
      }
    }
    console.log(filteredPostsList);
    return filteredPostsList;
  }
}

再次感谢你在这里的帮助。这是我第一次使用 Angular,我真的已经快要绝望了。谢谢你!

答案#1

在主页组件中,this.post指的是一个对象数组。要从对象数组生成新数组,请使用map()。尝试以下方法

home-page.component.ts:

let postname = this.post.map(p => p.name);
let postsour = this.post.map(p => p.sour);
let postsweet = this.post.map(p => p.sweet);

工作示例:游乐场

我在这里为你的代码创建了一个简陋的 Stackblitz。当然,由于我无法访问组件模板,我已将图表生成绑定到一个按钮元素上。你可以随意根据你的需求修改它。