Mark Robinson
Mark Robinson

Reputation: 1619

Get Chart.js to work with ImportMap in Rails 7

I tried:

./bin/importmap pin chart.js

which added this to config/importmap.rb

pin "chart.js", to: "https://ga.jspm.io/npm:[email protected]/dist/chart.mjs"

In application.js I added:

import 'chart.js'

When I try to create a new Chart I get "Chart is not defined".

If I try

import Chart from 'chart.js'

I get "The requested module 'chart.js' does not provide an export named 'default'"

import Chart from 'chart.js/auto' 

says "Failed to resolve module specifier 'chart.js/auto'"

Upvotes: 4

Views: 3764

Answers (3)

Rails Dev
Rails Dev

Reputation: 11

The case with JSPM links works for me but i was able to make it work for Rails 7.1.3 from vendor/javascript folder. May be somebody needs it.

Pin chart.js:

Run the following command to pin the chart.js library:

bin/importmap pin chart.js

In my case it was added like that:

Pinning "chart.js" to vendor/javascript/chart.js.js via download from https://ga.jspm.io/npm:[email protected]/dist/chart.js
Pinning "@kurkle/color" to vendor/javascript/@kurkle/color.js via download from https://ga.jspm.io/npm:@kurkle/[email protected]/dist/color.esm.js

This will automatically add the library to your config/importmap.rb and download the chart.js library and its dependency @kurkle/color to your vendor/javascript directory:

pin "chart.js" # @4.4.3
pin "@kurkle/color", to: "@kurkle--color.js" # @0.3.2

Create the Necessary Directory and File:

The chart.js library imports a file from a relative path that needs to be present in your application. To handle this:

Create a _ directory inside the vendor folder:

mkdir -p vendor/_

Download the required file from the CDN and place it in the vendor/_ directory. The file URL can be found by following the import path in the chart.js library:

curl https://ga.jspm.io/npm:[email protected]/_/6La3kzg5.js -o vendor/_/6La3kzg5.js

Add the Route:

Add a route to serve the JavaScript file from the vendor/_ directory in config/routes.rb:

get "/_/:filename", to: "assets#serve_js", constraints: { filename: /.*\.js/ }

Create the Assets Controller

Create a controller to handle serving the JavaScript file. Create a file app/controllers/assets_controller.rb with the following content:

# frozen_string_literal: true

class AssetsController < ApplicationController
  skip_before_action :verify_authenticity_token, only: %i[serve_js]

  def serve_js
    file_name = params[:filename]
    file_path = Rails.root.join("vendor", "_", file_name)

    if File.exist?(file_path)
      send_file(file_path, type: "application/javascript", disposition: "inline")
    else
      render plain: "File not found", status: :not_found
    end
  end
end

Import chart.js in Your Stimulus Controller:

there was no need to import "chart.js" in "application.js"

Directly import chart.js in your Stimulus controller without needing to include it in application.js. For example, in app/javascript/controllers/dashboard_controller.js:

import { Controller } from "@hotwired/stimulus"
import { Chart, registerables } from "chart.js"

Chart.register(...registerables);

export default class extends Controller {
  connect() {}
}

There is no need to precompile assets or something. just run bin/dev

Upvotes: 1

user22332935
user22332935

Reputation: 1

This is how I got it to work on a rails 7 app with importmaps.

const { Chart, registerables } = await import('chart.js');
Chart.register(...registerables);

Upvotes: 0

Mitch
Mitch

Reputation: 179

I had the same struggle this weekend and kept happening upon this article, so posting my solution here.

A couple things of note:

  1. Chart.js seems to have some hardcoded accessory files that aren't registered as dependencies in the CDN. For example, mine kept 404ing on c1719c72.js and chunks/helpers.segment.js. For this reason, it works better if you don't use the --download flag when running bin/importmap pin. Credit to this article: Using chart.js with importmaps in rail 7.
  2. Importing Chart from chart.js requires a little magic from here: Chart.js - Where do I find which components should be registered?. Specifically, lines 3-4 of barchart_controller.js below.

Here's a summary of the file contents that worked for me on a fairly plain Rails 7 build with importmaps:

config/importmap.rb - Note: Running bin/importmap pin chart.js should pull in one dependency (@kurkle/color). Be sure to skip --download, or you're in for a world of pain.

# Pin npm packages by running ./bin/importmap

pin "application", preload: true
pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
pin_all_from "app/javascript/controllers", under: "controllers"

pin "chart.js", to: "https://ga.jspm.io/npm:[email protected]/dist/chart.js"
pin "@kurkle/color", to: "https://ga.jspm.io/npm:@kurkle/[email protected]/dist/color.esm.js"

app/javascript/application.js

// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
import "@hotwired/turbo-rails"
import "controllers"

import "chart.js"

app/javascript/controllers/barchart_controller.js

import {Controller} from "@hotwired/stimulus"

import { Chart, registerables } from "chart.js";
Chart.register(...registerables);

export default class extends Controller {
    static targets = ['myChart'];

    canvasContext() {
        return this.myChartTarget.getContext('2d');
    }

    connect() {
        new Chart(this.canvasContext(), {
            type: 'bar',
            data: {
                labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
                datasets: [{
                    label: '# of Votes',
                    data: [12, 19, 3, 5, 2, 3],
                    backgroundColor: [
                        'rgba(255, 99, 132, 0.2)',
                        'rgba(54, 162, 235, 0.2)',
                        'rgba(255, 206, 86, 0.2)',
                        'rgba(75, 192, 192, 0.2)',
                        'rgba(153, 102, 255, 0.2)',
                        'rgba(255, 159, 64, 0.2)'
                    ],
                    borderColor: [
                        'rgba(255, 99, 132, 1)',
                        'rgba(54, 162, 235, 1)',
                        'rgba(255, 206, 86, 1)',
                        'rgba(75, 192, 192, 1)',
                        'rgba(153, 102, 255, 1)',
                        'rgba(255, 159, 64, 1)'
                    ],
                    borderWidth: 1
                }]
            },
            options: {
                scales: {
                    y: {
                        beginAtZero: true
                    }
                }
            }
        });
    }
}

Content from the div in my model's app/views/widgets/show.html.erb

<div data-controller="barchart">
  <canvas id="bar-chart" data-barchart-target="myChart" width="800" height="450"></canvas>
</div>

Don't forget to rebuild assets (bin/rails assets:precompile).

Upvotes: 11

Related Questions