Rest assured, the gnomes are hard at work
completing this video!
What if we want a color to be automatically selected on page load? Like maybe there's a most popular color that we want to suggest.
The best approach would probably be to pre-select an option in the select
element. Then we could read that from inside our Stimulus controller - probably
in connect()
- and set that color square as "selected".
But for the purposes of learning, we're going to do something different to show a solution to a really common problem: the problem of how to pass options and server-side data into your Stimulus controller. In this case, our Symfony code will decide which color should be selected by default and we need to pass that info into Stimulus.
We already know one approach to do this: data attributes! We invented a data attribute on the button to add more information to it. So... if we want to pass something to our controller, why not add a data attribute to the top level controller element?
A fantastic suggestion! Break the element onto multiple lines and then add
data-color-id=
. To keep things simple, let's pre-select whatever the second
color is. To do that, copy this whole addToCartForm.vars
thing from down here,
paste, and at the very end, add [1].id
to use the id of the second color.
Done! Inside the controller... inside connect()
, let's see if we can log
this: console.log()
, get the top-level element - this.element
- and read
its data: .dataset.colorId
.
Let's give it a go! When we refresh... we can see the data-color-id
in the
HTML and... over in the log... there's the id!
So this works great. But this is such a common thing to do - passing info from your server into your Stimulus controller - that stimulus gives us a special system for handling this... with a couple of really nice advantages. It's called the values API.
Here's how it works. Step 1, add a static values
property set to an object. Each
"value" that we want to allow to be passed into our controller will be a key in
this object. So we want a colorId
value. Set this to the type that this will
be. In our case, colorId
will be a Number
.
As soon as we define this, we can magically access a colorIdValue
property.
Let's log it: console.log(this.colorIdValue)
.
Finally, to pass this value into the controller, we will use a data attribute,
but with a special syntax. It's data-
the name of the controller -
color-square
- the name of our value - color-id
- the name is colorId
in our
controller, but it will be color-id
in HTML - then -value=
the actual value.
I know what you're thinking: that is ugly! Don't worry, we're going to learn an awesome shortcut in a minute.
But let's try it. Move over and refresh. Woohoo! It works! And something subtle
just changed. If you dug deeper, you'd find out that the 2
in the log is a Number
type. But before we started using the values API, the 2
was a string!
That second part makes sense. Technically, in HTML, data attributes are strings!
If you read something from the dataset
property, it will always be a string.
But the values API allows us to set the type for each value. It then handles
converting the string into that type. We can use things like Object
, Array
,
Boolean
, or any other JavaScript type.
I love the values API and it has one sweet trick up its sleeve that we'll learn about in the next video. But the syntax, woof.
So far, we've created all of our data-controller
elements by hand... because...
it's pretty simple to write data-controller="color-square"
.
But WebpackEncoreBundle gives us a shortcut method. Check it out: inside
the element where we want the data-controller
to be, add
{{ stimulus_controller() }}
and pass the name of the controller: color-square
.
Then, for the second optional argument, we can pass an array of values. We
have one: color-id
set to the long addToCartForm.vars
line.
Celebrate by deleting data-controller
and the value.
This will give us the exact same result as before. We can see it: inspect
element, find data-controller
and... refresh! Sweet! It does look the same
as before and we still get the log.
I love this feature because the values API is incredibly powerful and this removes all the pain of using it.
Oh, and this function also automatically escapes each value so it's safe to use in an HTML attribute. So if a value contains a double-quote, it won't break your page. We'll see this later when we use the values API to pass props to a React app.
The function also normalizes the value names automatically. If you want, you
can use colorId
here... and we don't even need the quotes anymore. This is
nice because it now exactly matches the name of the value inside the controller.
When this renders - I'll refresh and go back to Elements - it still outputs the same attribute name... and it all still works.
Next: let's use this new colorIdValue
property to pre-select the color. Thanks
to the organization of our controller, that's going to be pretty easy. And thanks
to a feature of the values API that we have not seen yet, we're going to end
up with less code after adding the new feature. Cool.
// composer.json
{
"require": {
"php": ">=7.2.5",
"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.12.1
"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.1.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.28.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
"@symfony/stimulus-bridge": "^2.0.0", // 2.0.0
"@symfony/ux-chartjs": "file:vendor/symfony/ux-chartjs/Resources/assets", // 1.1.0
"@symfony/webpack-encore": "^1.0.0", // 1.0.4
"bootstrap": "^4.6.0", // 4.6.0
"core-js": "^3.0.0", // 3.8.3
"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-use": "^0.22.1", // 0.22.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
}
}