Chart is not defined in a Rails 6 view page with webpacker - ruby-on-rails

I'm trying to figure out the right way to use a Chart from chart.js in a Rails 6 app using webpacker. I would like my chart generation javascript code to remain in the page since I'll be dynamically generating it from the view.
I started with
yum add chartjs
then added to the application pack (and javascript_pack_tag is in my application.html.erb)
// app/javascript/packs/application.js
import Chart from "chartjs";
And then, in a view I try and use that library:
<div class="row">
<div class="col">
<canvas id="backlog-chart" width="100%" height="400px">
<script>
var ctx = document.getElementById('backlog-chart').getContext('2d');
new Chart(ctx, { ...
This fails with an Chart is not defined javascript error.
Things I've tried:
the require("chartjs") form of including the library in application.js seems to have no impact one way or the other.
I tried adding to config/webpack/environment.js to the ProvidePlugin object with Chart: "chartjs/chart.js" but no changes.
And I've tried adding import Chart from "chart.js" directly to the <script> section in the view but this fails with a "import only allowed in module" error on the browser.
My javascript skills have definitely not kept up with the last couple years of new technologies so any suggestions on the right way to do this would be appreciated.

You can't use Chart in a script in your HTML body because Chart hasn't been globably defined. You could import the chart.js javascript by embedding the script tag, and then Chart would be available. But you said you wanted to use Webpacker, so...
Include your chart definition in your application.js, rather than in the script tag.
// app/javascript/packs/application.js
import Chart from "chartjs";
var ctx = document.getElementById('backlog-chart').getContext('2d');
var myChart = new Chart(ctx, {...});
This will target that canvas in your HTML:
<canvas id="backlog-chart" ... />

Related

How to call a javascript function inside a rails view?

I did just a upgrade from RAILS 5 to RAILS 6 and I see that all rails views are not able to call a javascript function as before in RAILS 5.
I have an external javascript file located under
app/javascript/packs/station.js
This is is embeded in in app/views/layouts/application.html.erb as
<%= javascript_pack_tag 'station' %>
This is the code how I call the javascrpt function from html.erb file :
<%= text_field_tag(:station_text_field, ... ,
onkeyup: "javascript: request_stations(); ") %>
When I try to call a function thats is part of the station.js then I get an error in the browser developmer view: ReferenceError: request_stations is not defined
But I can also see in the brwoser view, under Debugger :
Webpack / app/javascript / packs / station.js
and the javascript function I want to call.
So it seems that this script was loaded by the browser.
In contrast, when I just copy and paste these few lines that represent this javascript function direct into the template view file (...html.erb), something like :
<script>
function request_stations ()
{
alert("calling request_stations");
};
</script>
then - it works as expected !
By default, variables/functions defined inside JavaScript files that are packed by Webpacker will not be available globally.
This is a good thing, because it prevents global naming conflicts. Generally speaking, you don't want to reference javascript functions/variables from your view. You instead want to write JavaScript in a way that attaches functionality to DOM nodes using their id or other attributes.
Here is a basic example based on the code you provided:
# in your rails view
<%= text_field_tag(:station_text_field, ..., id: 'station-text-field') %>
// in your javascript
function request_stations() {
alert("calling request_stations");
};
const stationTextField = document.querySelector("#station-text-field");
stationTextField.addEventListener('keyup', (event) => {
request_stations();
});
Agree with mhunter's answer.
This post helped me get a grounding on this difference in Rails 6: https://blog.capsens.eu/how-to-write-javascript-in-rails-6-webpacker-yarn-and-sprockets-cdf990387463
What I don't see in your question is whether or not you did this in app/javascript/packs/application.js:
require("#rails/ujs").start()
require("turbolinks").start()
require("#rails/activestorage").start()
require("channels")
require("station")
The big difference in Rails 6 is that you have to deliberately:
require a JS file
deliberately export something from that file
deliberately import that something, in the file where you want to use it.
So if there is a function in station.js that you want to use, connect the steps above. Start with a simple function in station.js that fires upon DOMContentLoaded, and add a console.log("hey, station.js is alive and well"). If you don't see it, then something in those 3 steps is not right.
In pre-Rails6, you had a "garden" of JavaScript, just by virtue of being in the asset pipeline. In Rails 6, you have to be more deliberate.

Gmaps with Rails 6/Webpack

I'm trying to get my Google Maps setup that previously worked with earlier versions of Rails to display using Rails 6. Obviously Rails 6 is now using webpack to handle javascript assets and I can't get my app to recognie the Gmaps function used to render the map.
Some of the basics:
Gemfile
gem 'geocoder'
gem 'gmaps4rails'
gem 'underscore-rails'
# maybe don't need ^ this underscore gem anymore
I installed underscore with yarn add underscore and also added the google maps script gmaps_google.js currently under vendor/javascripts folder which has been added to my resolved paths in webpacker.yml `app/javascript/packs/application.js file looks like:
require("#rails/ujs").start()
require("#rails/activestorage").start()
require("channels")
require("gmaps_google")
import "bootstrap";
import 'underscore'
import "../stylesheets/application";
window.jQuery = $;
window.$ = $;
Here is my actual show.html.erb view and attempted load of the map
<div style='width: 800px;'>
<div id="map" style='width: 800px; height: 400px;'></div>
</div>
<script>
const handler = Gmaps.build('Google');
handler.buildMap({ provider: {}, internal: {id: 'map'}}, function(){
markers = handler.addMarkers([
{
"lat": <%= #user.latitude %>,
"lng": <%= #user.longitude %>,
"infowindow": "<%= #user.full_name %>'s location"
}
]);
handler.bounds.extendWith(markers);
handler.fitMapToBounds();
handler.getMap().setZoom(17);
});
</script>
Yet I still get no map loading on the page an error that says
Uncaught ReferenceError: Gmaps is not defined
It does load, and the error goes away when I load the gmaps script from an external CDN source in my HTML head, as this question suggests: Gmaps is not defined
So it is clearly just the loading/availability of that google_maps.js script. It's too massive of a file to show here, but here is the link to the working CDN version: cdn.jsdelivr.net/gmaps4rails/2.1.2/gmaps4rails.js
Copy-pasting that into my google_maps.js file doesn't help though. And I'm trying to figure out how I can get it to work with the Google Maps script residing within my Rails application and the webpack Rails 6 world is still very new to me. Any suggestions would be greatly appreciated.
The quick-and-dirty way is just load the gmaps4rails code (and underscore) from CDN instead of Webpack. Since you're just using a <script> tag to build the map, Webpack is not helping you there.
If you'd like to try using Webpack anyways, read on.
Understand that Webpack isolates itself from the global scope; unlike Sprockets, objects or values you import in Webpack will not be available in <script> tags or the browser console. If you want this behavior, you have to configure it by hand.
Looking at the gmaps4rails script, it appears not to be module-aware and assumes it's evaluated in the global scope (where underscore is available). In other words, it's not module-friendly. But we can make it play nice.
We need to tell Webpack to treat this in the gmaps4rails script as window, i.e., the global scope, and to "provide" underscore (or nearly interchangeable lodash, as below) since it assumes the _ lib is available in its scope.
In your shell:
$ yarn add imports-loader lodash
In config/webpack/environment.js:
const { environment } = require('#rails/webpacker')
const webpack = require('webpack')
environment.loaders.append('gmap4rails', {
test: /gmaps_google/,
use: [
{
loader: 'imports-loader',
options: 'this=>window',
},
],
})
environment.plugins.append(
'lodash',
new webpack.ProvidePlugin({
_: 'lodash',
})
)
module.exports = environment
Assuming you have set up your google scripts correctly per the gmaps4rails README, this should do the trick.
I've created a demo app with a working example in the example/gmap4rails branch (bring your own Google Maps API key): https://github.com/rossta/rails6-webpacker-demo/tree/example/gmaps4rails

Calling chart.update() with navigator options let the diagram shrink

For my current project, I need to fetch data from the server and update the chart with a seperate navigator series data. When I do this, the diagram itself is shrinking, each time I call
chart.update({
navigator: {
//actually it doesn't matter what is written here
}
});
It looks like it shrinks exactly as much space as would needed for another navigator.
See this jsFiddle
It's already fixed on a master branch of Highcharts repository. In order to use the newest version of library please change links from:
<script src="https://code.highcharts.com/stock/highstock.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
to:
<script src="https://github.highcharts.com/stock/highstock.js"></script>
<script src="https://github.highcharts.com/modules/exporting.js"></script>
You can use those links until the new release comes out (what should happen this week), but we don't recommend using GitHub version on production enviroment.
Live example: http://jsfiddle.net/pavnLexd/

Need some help setting up Typescript and Highcharts

I am quite New to typescript and I have tried to set up a typescript Project in Visual studio 2015 as HTML Application With Typescript. My problem is how to import or Reference highcharts without errors. I need a short description on how to set up the easist highchart in my typescript file. The description should explain from the start
1) creating the Project, and adding highchart (npm, tsd...) to the Project,
2) setting up the index.html file and app.ts (or the name of Your typescript file),
3) adding References in the html file and app.ts, especially in app.ts where you set import or References, like using import statement or
< reference path="typings/tsd.d.ts" />
< reference path="typings/highcharts/highcharts.d.ts" />
4) Set require statements and put into a variable
(I can't get this code to work ... Please use Your way to do this
var Highcharts = require('highcharts');
// Load module after Highcharts is loaded
require('highcharts/modules/exporting')(Highcharts);
// Create the chart
Highcharts.chart('container', { /*Highcharts options*/ });
5) Finally you render the highchart either in app.ts or by using the id in the index.html file
<div id="container" style="width:100%; height:400px;"></div>
I hope somebody can help me out here. I could of course have done all this in index.html but I want to make use of the debugging possibilities in typescript.

ckeditor inline edit removes script tag

We are using ckeditor inline edit in our asp.net mvc application. It removes the script tag when rendering the content. Following is the script we are trying to add in the source mode and save it to the database:
<script type="text/javascript" language="javascript">
$(function() {
document.oncontextmenu = function() { alert('Right click disabled for security reasons!!') ;return false;}
});
</script>
It adds fine to the database and when rendering the content back to the webpage, it removes the script tag.
Below is the image where I was able to add the script and save:
Below is the image where it removed the script when rendering back the page after save:
Following is the code i am using in my page to render the saved content:
<div id="content_editable" contenteditable="true">#Html.Raw(Model.PageContent.ToString())</div>
When i remove Html.Raw, it displays the script as the text in the page, but I need to have the script only in the source mode. I also have allowedContent = true in my config.js of ckeditor.
Please suggest how to handle this?

Resources