TransWikia.com

Chart.jsのラベルと値が対応していない問題を解決したい

スタック・オーバーフロー Asked by maiyanhahumetu on December 28, 2021

解決したいこと

Chart.js上で表示される値が、実際にDBに格納されているラベル・値共に対応していないため、正しいラベル・値を表示させたい

イメージ

画像の説明をここに入力

画像の説明をここに入力

環境

・Ruby:2.6.5
・Rails:5.2.4.3
・DB:PostgreSQL

試したこと

logger.infoでどんな値が格納されたのか確認したのですが、Controllerの意図した順番ではありませんが、正しい内容(=ラベル・値が対応している)が格納できています。にもかかわらず、実際に表示される円グラフは、全く対応できておりません。

Started GET "/records/3/aggregate_result" for ::1 at 2020-06-30 08:09:17 +0900
Processing by RecordsController#aggregate_result as HTML
  Parameters: {"id"=>"3"}
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 3], ["LIMIT", 1]]
  ↳ /Users/user/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-5.2.4.3/lib/active_record/log_subscriber.rb:98
   (0.5ms)  SELECT SUM(practice_time) AS sum_practice_time, "practice_item" AS practice_item FROM "records" LEFT OUTER JOIN "practices" ON "practices"."record_id" = "records"."id" WHERE "records"."user_id" = $1 GROUP BY "practice_item"  [["user_id", "3"]]
  ↳ app/controllers/records_controller.rb:59
test {"多球練習"=>10, "フットワーク"=>30, "サーブ練習"=>50, "オール"=>20}

※本来の順番は、上の一枚目の画像の通りです。

コード

Controller

class RecordsController < ApplicationController
before_action :authenticate_user!

  def index
    @q = current_user.records.ransack(params[:q])
    @search_records = @q.result(distinct: true).includes(:practices).page(params[:page]).per(8)
  end

  def show
    @record = Record.find(params[:id])
  end

  def new
    @record = Record.new
    output = @record.outputs.build
    practice = @record.practices.build
    task = @record.tasks.build
    @item_array = ["サーブ練習","フットワーク", "3球目攻撃","台上処理","多球練習","オール"]
  end

  def create
    @record = Record.new(record_params)
    logger.info "###### #{@record.inspect}"
    if @record.save
      flash[:success] = "練習内容の登録が完了しました。"
      redirect_to records_url
    else
      flash[:alert] = "登録に失敗しました。"
      render :new
    end
  end

  def edit
    @record = Record.find_by(id: params[:id])
    @item_array = ["サーブ練習","フットワーク", "3球目攻撃","台上処理","多球練習","オール"]
  end

  def update
    @record = Record.find_by(id: params[:id])
    if @record.update(record_params)
        flash[:success] = "練習内容の更新が完了しました。"
        redirect_to records_url
      else
        flash[:alert] = "更新に失敗しました。"
        render :edit
      end
  end

  def destroy
    record = Record.find_by(id:params[:id])
    record.destroy

    redirect_to root_path, notice: "練習記録を削除しました。"
  end

  def aggregate_result
  @label = ["3球目攻撃", "多球練習", "サーブ練習", "フットワーク","オール","台上処理"]
  gon.label = @label
  @record = current_user.records.includes(:practices).select(:practice_time).group(:practice_item).sum(:practice_time)
  logger.info "test #{@record.inspect}"
  gon.data = @record.values
  end

  private

  def set_user
    @user = current_user || User.new
  end

  def record_params
    params.require(:record).permit(:record_id, :training_date, :learning_point, outputs_attributes:[:output_name, :id, :_destroy], practices_attributes:[:practice_item, :practice_time, :id, :_destroy], tasks_attributes:[:task_name, :id, :_destroy]).merge(user_id: current_user.id)
  end
end

slim

= render 'records/flash_messages'
h1 レポート

canvas#myPieChart
script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.js"
javascript:
  var ctx = document.getElementById("myPieChart");
  var myPieChart = new Chart(ctx, {
    type: 'pie',
    data: {
      labels: gon.label,
      datasets: [{
          backgroundColor: [
            'rgba(255, 99, 132, 0.2)',
            'rgba(54, 162, 235, 0.2)',
            'rgba(255, 206, 86, 0.2)',
            'rgba(192,240,0,0.2)',
            'rgba(75, 192, 192, 0.2)',
            'rgba(242,68,172,0.2)'
          ],
          data:#{@record.values}
      }]
    },
    options: {
      title: {
        display: true,
        text: '練習内容の内訳'
      }
    }
  });

.d-flex
  .col-6.text-align: center
    = link_to '練習記録一覧に戻る', root_path, class: 'btn btn-primary'
  .col-6.text-align: center
    = link_to '練習記録を登録する', new_record_path, class: 'btn btn-primary'

db.schema

ActiveRecord::Schema.define(version: 2020_06_29_125236) do

  # These are extensions that must be enabled in order to support this database
  enable_extension "plpgsql"

  create_table "outputs", force: :cascade do |t|
    t.text "output_name", null: false
    t.bigint "record_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["record_id"], name: "index_outputs_on_record_id"
  end

  create_table "practices", force: :cascade do |t|
    t.string "practice_item", null: false
    t.integer "practice_time", null: false
    t.bigint "record_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["record_id"], name: "index_practices_on_record_id"
  end

  create_table "records", force: :cascade do |t|
    t.string "user_id"
    t.text "learning_point", null: false
    t.date "training_date", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "tasks", force: :cascade do |t|
    t.text "task_name", null: false
    t.bigint "record_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["record_id"], name: "index_tasks_on_record_id"
  end

  create_table "users", force: :cascade do |t|
    t.string "email", default: "", null: false
    t.string "encrypted_password", default: "", null: false
    t.string "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["email"], name: "index_users_on_email", unique: true
    t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
  end

  add_foreign_key "outputs", "records"
  add_foreign_key "practices", "records"
  add_foreign_key "tasks", "records"
end

One Answer

aggregate_result メソッドを次のように書き換えれば、指定ラベルの順序どおりにグラフが描画されるようになるのではないでしょうか?

def aggregate_result
  # 1. ログインユーザーの親関係にあるRecordテーブルと、
  #   子関係にあるPracticeテーブルの全てのデータを取得する。
  # 2. 取得したデータから、Practiceテーブルのpractice_timeカラムにある
  #   データを選択し、practice_itemごとにデータを分類し、
  #   practice_timeカラムの合計値を計算する。
  # 3. その合計値を、@recordオブジェクトとして表示する。

  # MEMO: item_order の順序を参考に Hash を再構築し、その後ひとつの Hash として値を纏める
  item_order = %w[サーブ練習 フットワーク  3球目攻撃 台上処理 多球練習 オール]
  @record =
    current_user.records
                .includes(:practices)
                .select(:practice_time)
                .group(:practice_item)
                .sum(:practice_time)
                .then { |hash| item_order.map { |key| {key => hash[key]} } }
                .reduce { |hash, key_value| hash.update(key_value) }

  logger.info "test #{@record.inspect}"

  gon.label = @record.keys
  gon.data = @record.values
end

メソッドチェーンを2つ以上使っての数珠繋ぎをやりすぎたパターンだとおもっているので、もしかしたら適切に別名のメソッドを定義して処理を渡してあげる必要があるかもしれません

なにか参考になれば幸いです

Answered by gouf on December 28, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP