If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeWe now know Stimulus pretty well, which is really just a nice JavaScript library that has nothing to do with Symfony. So then... what exactly is Symfony UX?
To answer that, go to https://github.com/symfony/ux. At its most basic, Symfony UX is a growing list of pre-built Stimulus controllers that you can install to get free functionality. For example, one of them is a controller that integrates Chart.js: a completely independent, lovely JavaScript-powered chart library.
This is perfect... because our sales are really starting to take off. People love our junk, uh, minimalist products! So let's install and use this UX package to build a sales graph on an admin page. I've already created that page at "/admin"... but it's not very interesting yet... and none of these links go anywhere.
Ok: let's check out the docs for Symfony UX chartjs. Scroll down... Whoa. Stimulus and Chart.js are both pure JavaScript libraries. So it's a little interesting that the first step is to install a PHP package. Why are we doing that? Let's find out! Copy the composer require
line and head over to your terminal.
I committed all my changes before hitting record so I can easily see any changes that the Flex recipe for this package might make. Paste and go:
composer require symfony/ux-chartjs:1.2.0
While this loads, your Encore build may start failing: don't worry about that for now. When it finishes, run:
git status
Interesting! This modified composer.json
and composer.lock
, which is totally normal. And the PHP package we just installed is actually a Symfony bundle, so its Flex recipe auto-enabled that bundle in config/bundles.php
and updated the symfony.lock
file.
But here's where things get interesting, really interesting. The recipe also updated our package.json
file as well as some other file called assets/controllers.json
.
Let's see what changed in package.json
:
git diff package.json
Woh! It added a new package! But instead of a version number on the right, it's pointing to a directory inside of vendor/
. Let's go check out that path. It was vendor/symfony/ux-chartjs/
then Resources/assets
. Cool! This directory is a JavaScript package! How can I tell? It has a package.json
file like all JavaScript libraries and, in the src/
directory, a Stimulus controller!
Well, in reality, when we use this package, the compiled controller in the dist/
directory is what will truly be used... but reading the one in src/
is easier.
So: what does this mean for us? It means that each Symfony UX library - like ux-chartjs - is actually two things: a PHP bundle and a JavaScript package. There is also some magic related to how this controller is registered in our app... but we'll talk about that in a few minutes.
Anyways, we have a new Symfony bundle in our vendor/
directory and a new JavaScript package registered in package.json
- let me open that - which points to a directory in that bundle.
Ok, back to the docs! The next step is to run yarn install --force
. Copy that.
Normally, if you add a package to package.json
, you need to run yarn install
to actually download that into the node_modules/
directory. And actually, that's exactly what just happened to us! The Flex recipe added a new package to our package.json
file.
Move over to the terminal that's running Encore, hit Ctrl+C to quit, then paste this command:
yarn install --force
This command forces yarn to reinstall our dependencies. The important part is that it copies the directory from the bundle into node_modules/
so that it looks and acts like any normal package.
Let's go find that: let me close a couple of things, then open node_modules/
, and @symfony/
. Cool! We of course have a webpack-encore/
directory but we also have a package called ux-chartjs
.
Let's go re-build our assets:
yarn watch
Ok: so how do we use the new Stimulus controller that lives in the package?
To answer that... let's keep following the docs! Scroll down. To build the JavaScript-powered chart, we're... woh! We're going to write PHP code!
Copy the top half of this code then head over and open up our admin controller, which is src/Controller/AdminController.php
. Paste that code on top. And... let's see, we also need this ChartBuilderInterface
argument. Add ChartBuilderInterface $chartBuilder
. And... we need a use
statement for this Chart
class. I'll delete the "t", re-type, and hit tab to get it on top.
... lines 1 - 6 | |
use Symfony\UX\Chartjs\Builder\ChartBuilderInterface; | |
use Symfony\UX\Chartjs\Model\Chart; | |
... line 9 | |
class AdminController extends AbstractController | |
{ | |
... lines 12 - 14 | |
public function dashboard(ChartBuilderInterface $chartBuilder) | |
{ | |
$chart = $chartBuilder->createChart(Chart::TYPE_LINE); | |
$chart->setData([ | |
'labels' => ['January', 'February', 'March', 'April', 'May', 'June', 'July'], | |
'datasets' => [ | |
[ | |
'label' => 'Sales!', | |
'backgroundColor' => 'rgb(255, 99, 132)', | |
'borderColor' => 'rgb(255, 99, 132)', | |
'data' => [522, 1500, 2250, 2197, 2345, 3122, 3099], | |
], | |
], | |
]); | |
... lines 29 - 32 | |
} | |
} |
But wait: where did these classes come from? Remember: we did just install a new bundle... and bundles give us new classes and services. Autowiring ChartBuilderInterface
will give us a new service that's really good at building chart data in PHP. Pass the $chart
variable into the template: chart
set to $chart
.
... lines 1 - 14 | |
public function dashboard(ChartBuilderInterface $chartBuilder) | |
{ | |
... lines 17 - 29 | |
return $this->render('admin/dashboard.html.twig', [ | |
'chart' => $chart | |
]); | |
} | |
... lines 34 - 35 |
Okay. How do we render the chart? Go back to the docs and scroll down one more time. Ah ha! The bundle also gave us a new Twig helper. Copy that and go find the template for this page: templates/admin/dashboard.html.twig
.
Then... all the way at the bottom, paste.
... lines 1 - 2 | |
{% block body %} | |
<div class="container-fluid"> | |
<div class="row"> | |
... lines 6 - 101 | |
<div class="col-10 mt-4"> | |
<h1>Admin Dashboard</h1> | |
{{ render_chart(chart) }} | |
</div> | |
</div> | |
</div> | |
{% endblock %} |
This uses the new function render_chart()
from the bundle to render our chart
variable.
I... guess we're done? Head back to our site and refresh the admin page. Whoa! We are done! We have a JavaScript-powered graph! That's extra amazing since all we did was composer require
a PHP package, run yarn install
, then write some PHP code!
That is the power of Symfony UX.
But... how exactly does this all work and connect together? What does render_chart()
actually do? How is that Stimulus controller from the bundle being used? When will I find my car keys?
Let's answer... most of these questions, next.
Hey Richard!
Nah, it's a pretty reasonable questions ;).
First, nice job setting up the JavaScript to hook into the chart!
How do I listen to an input tag in the twig and update the chart when the value changes?
Here is what I would do:
A) Add a stimulus_action()
to the input element - make it call, for example, some updateChart()
method on your controller.
B) Inside of _onConnect(event)
(for reference for others, you correctly got that from the documentation - https://symfony.com/bundles/ux-chartjs/current/index.html#extend-the-default-behavior - set the Chart
object on a property on your Stimulus controller:
_onConnect(event) {
// set the Chart object on a property so you can use it later
this.chart = event.detail.chart;
}
C) Finally, inside of the updateChart()
method, you can use the this.chart
object and any other info you need to update the chart:
updateChart(event) {
const input = event.target;
const inputValue = input.value;
// to update the chart, follow the chart.js docs
// e.g. https://www.chartjs.org/docs/latest/developers/updates.html
this.chart.data.datasets...
}
I'm assuming that you want to update the Chart object in JavaScript by hand. Another thing you could do is make an Ajax call to some Symfony controller that returns a new, fully-rendered Chart, which you could then put onto the page to render a brand new chart.
Cheers!
Hi,
Despite installing version 1.2.0 I can't get rid of this error:<br />"./node_modules/@symfony/ux-chartjs/dist/controller.js" contains a reference to the file "stimulus".<br />This file can not be found, please check it for typos or update it if the file got moved.<br />
It is obvious that it originates on this line var _stimulus = require("stimulus");
from the dist version or import { Controller } from 'stimulus';
from the src one.
Maybe I need to updated to @hotwired/stimulus
but no sure how to get it rebuild
<b>Edit</b>
Changed dist version to var _stimulus = require("@hotwired/stimulus");
and the error is gone... too hacky for my taste
Hey danresmejia!
Hmm, well that's no fun! So, if you're using Version 3 of Stimulus (the one where the package name is "@hotwired/stimulus" and not "stimulus"), then you need version 2.0 (or greater) of symfony/ux-chartjs. Really, the only difference between the 1.x and 2.x of the UX packages is that 2.x starts importing @hotwired/stimulus (instead of stimulus).
Hope that clarifies!
Cheers!
If you have a problem with "has unmet peer dependency" or undefined package stimulus, it's because you are using stimulus v3 but by installing "composer require symfony/ux-chartjs:1.2.0" you get bundle with package reference to 'stimulus' (v2) inside vendors
So you need to install "symfony/ux-chartjs" v2+, and before it you must update symfony/flex package (composer update symfony/flex) otherwise composer will throw an error
Hey @Hainovsky,
Thanks for sharing detailed explanation. BTW exact this installation string is mentioned in course code and also there is video note ;)
Cheers!
Hey Emilio,
Sure, it should work! With EasyAdminBundle v3 you can add Webpack Encore entries, that's exactly what you need instead of static addJsFile() method calls. You just need to import the ChartJS code in the Webpack Encore entry and write the implementation of it.
Cheers!
Sorry, I was referring to symfony / ux-chartjs. Can it be integrated into the EasyAdmin 3v dashboard?
Yes, i was integrate it yesterday
1) need rewrite layout.html.twig
1.1) create folder templates/bundles/Easyadmin/
1.2) create in this folder layout.html.twig
1.3) copy intire code from vendor/...../views/layout.html.twig yo your 1.2)
1.4) add stymulus to css and to js like a encore_entry_link_tags('app')
1.5) yarn watch
1.6) create own controller in admin/Dashboard with php code from symfony/ux and render 1.7
1.7) add your page with chart to template like in body {{ render_chart(chart) }}
1.8) enjoy your work
I do not know if it is allowed by symfonycasts rules, but i can create tutorial and make repository and put here link.
RYAN should allow me to do it.
Thanks,
What I did was add a new JS (called dashboard.js inside import './bootstrap';) in webpack.config.js and then in DashboardController.php (EasyAdmin) add the assets with -> addWebpackEncoreEntry ('dashboard'). [in configureAssets() method]
It worked!.
Not override easyadmin's layout.html, but I tried that method and it worked too.
Thanks! Also worked for me!
1) I've created <b>dashboard.js</b> in <b>/assets/scripts/</b> with content:import '../bootstrap';
Used ".." cause of my folder structure.
2) Injected <b>dashboard.js</b> into <b>webpack.config.js</b> file:.addEntry('dashboard', './assets/scripts/dashboard.js')
3) Overrided <b>configureAssets</b> method in DashboardController class:
public function configureAssets(): Assets {
return parent::configureAssets()->addWebpackEncoreEntry('dashboard');
}
Hey Emilio,
Great! I'm happy it worked for you. Thanks for sharing your solution with others!
Cheers!
How to create transparent background on chart Chart::TYPE_LINE? Removed this backgroundColor, nothing happens, light grey background exist....
Solved - 'backgroundColor' => 'rgba(255, 255, 255, 0)', Last parameter is transparency. Thanks to all
// composer.json
{
"require": {
"php": ">=8.1",
"ext-ctype": "*",
"ext-iconv": "*",
"composer/package-versions-deprecated": "1.11.99.1", // 1.11.99.1
"doctrine/annotations": "^1.0", // 1.11.1
"doctrine/doctrine-bundle": "^2.2", // 2.2.3
"doctrine/doctrine-migrations-bundle": "^3.0", // 3.0.2
"doctrine/orm": "^2.8", // 2.8.1
"phpdocumentor/reflection-docblock": "^5.2", // 5.2.2
"sensio/framework-extra-bundle": "^5.6", // v5.6.1
"symfony/asset": "5.2.*", // v5.2.3
"symfony/console": "5.2.*", // v5.2.3
"symfony/dotenv": "5.2.*", // v5.2.3
"symfony/flex": "^1.3.1", // v1.18.5
"symfony/form": "5.2.*", // v5.2.3
"symfony/framework-bundle": "5.2.*", // v5.2.3
"symfony/property-access": "5.2.*", // v5.2.3
"symfony/property-info": "5.2.*", // v5.2.3
"symfony/proxy-manager-bridge": "5.2.*", // v5.2.3
"symfony/security-bundle": "5.2.*", // v5.2.3
"symfony/serializer": "5.2.*", // v5.2.3
"symfony/twig-bundle": "5.2.*", // v5.2.3
"symfony/ux-chartjs": "^1.1", // v1.2.0
"symfony/validator": "5.2.*", // v5.2.3
"symfony/webpack-encore-bundle": "^1.9", // v1.11.1
"symfony/yaml": "5.2.*", // v5.2.3
"twig/extra-bundle": "^2.12|^3.0", // v3.2.1
"twig/intl-extra": "^3.2", // v3.2.1
"twig/twig": "^2.12|^3.0" // v3.2.1
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.0
"symfony/debug-bundle": "^5.2", // v5.2.3
"symfony/maker-bundle": "^1.27", // v1.30.0
"symfony/monolog-bundle": "^3.0", // v3.6.0
"symfony/stopwatch": "^5.2", // v5.2.3
"symfony/var-dumper": "^5.2", // v5.2.3
"symfony/web-profiler-bundle": "^5.2" // v5.2.3
}
}
// package.json
{
"devDependencies": {
"@babel/preset-react": "^7.0.0", // 7.12.13
"@popperjs/core": "^2.9.1", // 2.9.1
"@symfony/stimulus-bridge": "^2.0.0", // 2.1.0
"@symfony/ux-chartjs": "file:vendor/symfony/ux-chartjs/Resources/assets", // 1.1.0
"@symfony/webpack-encore": "^1.0.0", // 1.0.4
"bootstrap": "^5.0.0-beta2", // 5.0.0-beta2
"core-js": "^3.0.0", // 3.8.3
"jquery": "^3.6.0", // 3.6.0
"react": "^17.0.1", // 17.0.1
"react-dom": "^17.0.1", // 17.0.1
"regenerator-runtime": "^0.13.2", // 0.13.7
"stimulus": "^2.0.0", // 2.0.0
"stimulus-autocomplete": "^2.0.1-phylor-6095f2a9", // 2.0.1-phylor-6095f2a9
"stimulus-use": "^0.24.0-1", // 0.24.0-1
"sweetalert2": "^10.13.0", // 10.14.0
"webpack-bundle-analyzer": "^4.4.0", // 4.4.0
"webpack-notifier": "^1.6.0" // 1.13.0
}
}
Nube Question:
Symfony 6
Charts working
_onConnect(event)
worksevent.detail.chart.options.onClick = (mouseEvent) => {}
works when you click the chart areaHow do I listen to an input tag in the twig and update the chart when the value changes?