Summarized using AI

Stimulating Events

Jesse Spevack • April 12, 2021 • online • Talk

In this RailsConf 2021 talk, Jesse Spevack introduces Stimulus JS, a lightweight JavaScript framework designed to enhance Rails applications with minimal effort. Jesse emphasizes that the framework is not suited for building complete applications independently but rather serves as a helpful tool for integrating modern interactions into Rails apps. The session involves live coding three examples, focusing particularly on developing a dynamic dropdown menu for selecting RailsConf keynote speakers.

Key Points Discussed:
- Introduction to Stimulus JS:
- Positioned as an 'omakase' framework, emphasizing simplicity and integration with Rails.
- Contrasted with more complex frameworks like React and Angular, highlighting its Rails-centric nature and focus on server-side logic.

  • Live Coding Session:

    • Jesse walks through building a sample dropdown menu, adding stylish elements using Tailwind CSS, which showcases modern design accessibility for developers with limited design backgrounds.
    • Demonstrated how to create Stimulus controllers, manage lifecycle callbacks, and interact with the DOM using actions, targets, and values.
  • Creating a Dropdown Menu:

    • The development process includes setting up a simple Rails application, generating a new controller, and implementing HTML that interacts dynamically with the Stimulus framework.
    • Discussed using data attributes to connect HTML elements to JavaScript functions effectively.
    • Highlighted methods for handling events such as 'mouseenter' and 'click' to improve user interaction, such as highlighting items and updating button text based on selections.
  • Refactoring for Better Encapsulation:

    • Jesse advocates for breaking down the dropdown controller into more manageable, smaller controllers, thereby following best practices in encapsulation and separation of concerns.
    • Introduced the concept of custom events to allow better communication between sibling controllers, enhancing the application’s modularity.
  • Conclusion and Takeaways:

    • The session concluded with an encouragement for developers to explore Stimulus JS as a facilitating tool that makes integrating interactive features into Rails applications seamless and less overwhelming.
    • Jesse invites attendees to continue discussions and share feedback, reflecting on the supportive community integral to these developer conferences.

Stimulating Events
Jesse Spevack • April 12, 2021 • online • Talk

Stimulus JS bills itself as a framework with modest ambitions that fits nicely into the Rails as "omakase" paradigm. At its core, Stimulus gives Rails developers a new set of low effort, high impact tools that can add much more than the sheen of modernization to an everyday Rails application. Join me as I live code three Stimulus JS examples that will help you save time, impress your friends, and win new clients and opportunities. This will be great for JS newbies and experts alike along with anyone interested in the potential schadenfreude that watching me live code will likely elicit.

RailsConf 2021

00:00:05.060 Hello RailsConf! I am so glad to connect with my Rails brothers and sisters from all over the world. Thank you all for tuning in. Last year, we came together at what would be the start of this pandemic, and the connections I made with folks helped sustain me. I am hopeful that next year we will get to see each other in person. Until then, I hope you all are healthy and safe. A big thank you to the RailsConf program committee and planning team! I can only imagine how challenging it is to put a conference like this together, but it's so important to all of us who use Rails. This conference has been a much-needed treat for me, so thank you for your dedication to this incredible community.
00:00:40.860 My name is Jesse Spevack, and I use he/him pronouns. I'm a staff engineer at Ibotta, a cashback shopping app that aims to make every purchase rewarding. I've been at Ibotta for the past four years, and it is the best job I’ve ever had. I've learned a lot during my time at Ibotta, and they've always encouraged my growth. We have a great team; everyone is not only sharp but also, more importantly, kind and giving. We're growing, so please reach out if you're interested in learning more. I love working with folks from this community. Before working at Ibotta, I had a completely different career—I spent over 10 years in K-12 public education and made my way into coding through the Turing School of Software and Design. I'm a huge believer in that program and its mission to open high-fulfillment technical careers to a diverse group of people.
00:01:26.640 There are several ways you can help support Turing. First, you can consider hiring one of the school's recent graduates, who will undoubtedly bring immediate value to your team. Ibotta has hired over a dozen Turing grads, and we are a far stronger company for it. You can also get involved by sharing your wisdom with current students as a mentor or by hosting job shadowing days. Finally, you can donate to Turing, which, unlike many other code schools, is entirely not-for-profit. I am so proud of my developer origin story, which started at Turing.
00:02:10.619 I am very excited to make my second speaking appearance at RailsConf. Last year, I talked about some of the big mistakes I made and what I learned from them. This year, I'm going to talk to you about Stimulus. Before we begin, let's get one thing out of the way: Stimulus is a toy JavaScript framework. It's not meant to build commercially viable web applications, so don’t even try. Of course, I'm kidding. Basecamp's hey.com email app is what first got me interested in Stimulus, and the promise of lightweight client-side code has me hooked.
00:02:28.560 With that disclaimer out of the way, let's start with what Stimulus is. Stimulus is a JavaScript framework that can help bring simple order to client-side code in your Rails application. It does not belong in the React or Angular category; I find it much 'railsier' in its focus on server-side code convention and conceptual compression. Some of the syntax may be new, but some of it is definitely Rails-inspired and feels very familiar. We're going to use Stimulus to add a nice, professional modern sheen to a simple dropdown menu.
00:03:05.940 The example is contrived in the sense that I'm not showing you production code. I'm not considering myself with testing, which I'm slightly ashamed about, and in the end, our dropdown isn't really going to solve any real problems. Our goal here is to use the dropdown select HTML and JavaScript as a teaching tool. I hope that this will show you how easy it is to use Stimulus to add modern interaction to our HTML in Rails. I hope that you walk away with an understanding of the core concepts of Stimulus—controller lifecycle, callbacks, actions, targets, values, and CSS classes.
00:03:52.560 I also want to refactor our initial code together using some more advanced JavaScript to better encapsulate the responsibilities of our code. Okay, enough of me talking about Stimulus; I want to write some code with you to show you Stimulus. We're going to start with a brand new Rails application that I've added Tailwind CSS to. Tailwind is a utility-first CSS library that is great for someone with basically no design or CSS chops.
00:04:26.040 The first thing we're going to do is generate a new demo controller. We'll use the generator to define a dropdown method. I know it's not RESTful, but hang with me here. I like to set up my terminal with three panes: one for my commands, one to run the Rails server, and one to run the Webpack dev server. You are really going to want the Webpack dev server running because it allows us to see our changes on localhost immediately, instead of needing to compile after each change.
00:04:50.760 We can view our generated dropdown page and see the template HTML we get from Rails. Let's open our dropdown view in the demo directory and remove the template code. We'll replace it with some HTML I put together ahead of time using Tailwind UI, which is a premium HTML component library by the folks who created Tailwind CSS. I told you before that design is definitely not my area of expertise, nor is it the subject of this talk. If anything, I hope this shows how accessible Stimulus makes modern-looking and feeling web apps to folks who spend most of their time, like me, behind asynchronous boundaries in a back-end system.
00:05:37.260 Also, I have this code available on GitHub, which I invite you to check out. Now let's open the demo controller we just generated in the app/controllers directory. We’re going to be building a dropdown menu where a user can choose a RailsConf keynote speaker. We will add some data to our dropdown method in our controller. Typically, in a real application, this might be accomplished by a database query or some other service request.
00:06:02.520 Spelling here is always a challenge for me, so I appreciate everybody's patience. Also, I look forward to the feedback I undoubtedly get about my Vim skills. Listen, maybe I like matching lots of keys all the time; have you ever considered that? Now you can view our page with our speaker by opening the browser to localhost:3000/demo/dropdown. This route was auto-generated for us in our generated controller step.
00:06:36.720 We now have our static HTML page that is generated on the server side of our app. We'd like to use Stimulus to make this HTML actually do some stuff. Let's copy the boilerplate Stimulus controller code we get in hello_controller.js; this is automatically generated when I ran Rails new and set my Webpack option to Stimulus. We'll call our new controller dropdown_controller. I like naming my controllers after components because it helps push me towards making reusable controllers.
00:07:22.919 So this isn't, for example, the 'Select a Keynote Speaker' controller. A controller in Stimulus is the basic organizational unit of the framework. Controllers are instances of JavaScript classes that we define and that get attached to chunks of our HTML. What I like about this is that we're getting some mandatory organizational structure for the JavaScript in a Rails application.
00:07:38.520 To me, this is a big step up from writing jQuery in a bunch of random places, which I've definitely done. It's also not quite the lift needed to use React or Vue in our Rails app, which in a lot of cases is just overkill. The controller is meant to govern the behavior of a chunk of HTML, so we need to wire our controller to our HTML. This is important because where we tie our controller into our HTML will define our controller's scope.
00:08:31.799 If we put our controller in our application layout, the controller would have every view we create in its scope because every view, or every HTML element, is nested within our application layout. In this case, the dropdown button and the list of select items are the HTML we care about. We will take the div that wraps these HTML tags and add a data attribute called 'data-controller.' We will set the value of the attribute to be 'dropdown,' the name of our new controller.
00:09:01.680 And this is the first pattern that we’ll see repeated again and again. We define a data attribute in HTML, assign it a value, and we gain some functionality as a result. Let's add a console log in the dropdown_controller.js to show that our controller is properly connected to our HTML. In lieu of test-driven development, I'll be self-consciously modeling console-driven development.
00:09:44.480 The important piece here is to be systematic and test all assumptions. We can add our console log statement to one of the Stimulus lifecycle methods, 'connect,' which is called anytime the controller is connected to the DOM. Open the developer console in our web browser, and we can see our message. At this point, our controller does not yet let users pick items.
00:10:10.920 We can add a modern feel to this by styling the dropdown. We want the item that a user is mousing over to be highlighted. We'll also want the text in our button to change when the user selects an item. Finally, clicking the button or clicking on a speaker from the list should open or close the list of selectable items.
00:10:44.640 To achieve this, we have some Tailwind CSS classes that define styles we would like to apply to the various HTML elements on our page. We'll be liberally using JavaScript to add and remove classes throughout this code in Stimulus. Let's just refer to CSS classes by logical naming. The goal here is to make our Stimulus controllers more reusable by making the styling we apply dynamic and something that can be passed into the controller as a set of parameters.
00:11:47.279 We do this by adding new data attributes to the HTML div with our data-controller attribute. These attributes will be the 'data-' prefix, followed by the name of the controller, the styles we want to apply. This is another pattern in Stimulus; we will be defining more and more data attributes that are prefixed with 'data-' and our controller name as we continue. After naming the controller, we come up with a logical name for our class. I'm not doing a great job of this here; 'highlighted' versus 'unhighlighted' might be a better choice, but sometimes I also leap towards abstraction too quickly.
00:12:37.860 We end our data attribute with the word 'class' and then assign a value that corresponds with the actual CSS class we would like to access in our controller by name. Now we'll add these values to our static classes array. So again, abstraction might be really nice here. Instead of 'text-pink,' we might say 'highlighted-text,' and then the color we choose could change on a controller-by-controller basis. I'm not doing that here, though, so do as I say, not as I do! Now we are ready to use a Stimulus action to define some behavior we'd like to see on our page.
00:13:34.920 An action is a connection between a controller method, an element on the page, and a DOM event listener. We'd like to change the style of the list items as the user's cursor hovers over them. In this case, the action will specify that the 'mouse-enter' DOM event should be routed to the dropdown controller and invoke the 'highlightListItem' function. I like the syntax a lot because it feels familiar to me from writing routes in Rails.
00:14:01.800 We have to define our 'highlightListItem' function. We'll add some console logging because this demo is nothing if not a demonstration of console-driven development. Every action in Stimulus sends the triggering event to the function specified in our data attribute, so we'll send the mouse-enter event into our 'highlightListItem' function. This event object includes a target, which is the HTML element that dispatched the event—in this case, the target is our list item that just had the cursor enter its boundaries.
00:14:56.100 Let's console log the inner text of the first child element of our list item; this should print out the name of the speaker mouse over in our console. Since we see this message as we hover over our list of speakers, let's now write JavaScript to apply our desired style. We want the background of the speaker to turn pink. Let's use the 'this.background-pink' class, which we get from our static classes array, which points back to the background-pink data attribute in our HTML.
00:15:54.360 We'll add this class to the list item element, which is the target that dispatched the mouse enter event. Now, as our mouse enters the list item, the list item passes that mouse enter event to our Stimulus controller, and typically, the 'highlightListItem' function. This function adds a CSS class to the list item that was just moused over, and the class is defined by a data attribute in our HTML. Friends, we are writing Stimulus, and this is great since it looks like it's working.
00:16:36.720 Let's do the same thing again for the font color and style when we highlight—we'll want the font to go from gray to white and from normal weight to semi-bold. The text of the speaker's name is actually in a span nested within the list item, so to add some CSS classes to this span, we can first call 'firstElementChild' on our list item. As we move our mouse over our list, each list item will get a pink background, and the inner span text will turn white and bold.
00:17:19.560 If you aren't thrilled by this, then you are probably a lot better at JavaScript than I am. At this point, you're probably thinking, 'Wow, Jesse, you aren't that good at JavaScript; you've just made a perfectly good list of keynote speakers pink!' Okay, well, in response, I'd say Rome was not built in a day. What if we turn our attention to unhighlighting our list items when the mouse leaves their boundaries? We can go back to our data-action attribute in our list item and add a second action definition.
00:18:15.960 We'll add that a mouse-leave event should be routed to our dropdown controller and invoke our 'unhighlightListItem' function. Our 'unhighlightListItem' function is going to be the inverse of our 'highlightListItem' function. Instead of adding these classes, we will remove them, and giving this a spin on our localhost seems like our list items are highlighting and unhighlighting appropriately. I'm feeling more and more like a complete wizard; let's see if I can retain that confidence for the remainder of this talk.
00:18:58.560 Now let's handle the actual selection of a speaker in our list. Ideally, when the user clicks on a list item to select a speaker, the list should vanish and the button text should change from 'Select Speaker' to the name of the speaker just selected. We'll define another action using the data-action attribute on our list item. The DOM event that triggers this action will be a click event, and we want the user’s click on the list item to invoke the 'selectItem' function in our dropdown controller.
00:19:43.140 We'll add some console logging in our new selectItem function to make sure things are wired up correctly; the console shows that we are logging the name of the speaker we are clicking on. Now let’s work on taking the name we are logging and putting it in the select button. To do this, we'll introduce another piece of Stimulus—the target. The target allows us to reference important HTML elements in our controller.
00:20:25.920 Just like an action, we'll create a target by adding a data-target attribute to the HTML element we’d like to name. We’ll use 'data-dropdown-target' to tell Stimulus that this target should be accessible in our dropdown controller. We use the value 'speaker' to indicate that when we call the speaker target in our controller, we really mean this particular span in our HTML. Now we will add the speaker target to our static targets array; this will allow us to reference this span by invoking 'this.speakerTarget' in our selectItem function.
00:21:24.480 We can call 'textContent' and set that equal to the inner text of the target that dispatched the click event. Here, the event is the click event, the target is the list item containing the name of the speaker just clicked, and the inner text is the speaker's name. When we click on a list item, the text of the button will update to the name of the list item that was clicked. Now that we have a selection, we need to hide the dropdown list.
00:22:08.640 To do so, we'll add another target in our HTML to help us more easily reference elements from our HTML in our controller. Let's add a data-dropdown-target attribute with the value 'list' to our unordered list; this will let us call 'this.listTarget' in our dropdown controller and get the entire unordered list. We have to remember to add this list to our static targets array in our controller. Now, in our selectItem function, we can hide our list target by adding 'this.hidden' class to it.
00:22:58.320 Now when we select a speaker, the dropdown disappears. But what if the user wants to change their selection? With our current implementation, we can get rid of the list, but we can't get it back. We can fix this by adding a click action to our button, so that clicking the button will either show or hide the list of options. Stimulus gives certain elements, like buttons, their own default event shorthands. Because we're dealing with an HTML button, Stimulus assumes we are going to want a click event; therefore, we do not have to specify 'click' in our data-action attribute.
00:23:54.240 Someone in Stimulus headquarters is really looking out for us. Instead, our data-action attribute needs the controller name and the function to call. The click DOM event is assumed. Now we’ll add a toggleList function that can toggle the hidden class on our list target, so each time the button is clicked, we'll either add or remove the hidden class from our dropdown list, which will make it look like it has expanded or collapsed. I noticed that the functionality of our toggleList function is repeated in our selectItem function, so we can refactor and 'dry' our code.
00:25:20.940 We can also add a call to 'this.toggleList' in our connect function so that when the controller class is first instantiated upon our page loading, the dropdown will be hidden. We want the user to click to open the dropdown. Now our list mostly works, but maybe you are wondering what to do about the check marks next to the text in our list items. Ideally, when the user opens the options dropdown by clicking the button, and a speaker is already selected, we should display a check mark next to the speaker's name because we’re fancy like that.
00:26:12.240 We'll repeat the pattern we've been using. We'll add a data target attribute, and then we'll toggle our hidden class on the check SVG icon. First, our data target attribute will go on the span that wraps our SVG check icon; the target name will be 'check' because honesty is a good policy when naming data targets in Stimulus. Now we need a way to hide all the check marks because when a page first loads, the user will not have had the chance to select a speaker.
00:27:03.720 To do so, we're going to do something a little different, but it builds off our past experience with the target concept. In the past, we used a singular data target. Now, our page has many check targets, and with Stimulus, all we have to do is pluralize targets, and we get an array of our targets. So instead of 'this.checkTarget', we'll do 'this.checkTargets' (plural). We’ll iterate over the list of check targets. We’re trying to hide these checks, so let's console log the spans containing the checks to make sure we are grabbing the right elements.
00:27:57.240 Now we can call 'hideChecks' in our connect function. That looks right to me, so for each check target, we add 'this.hidden' class. Our goal here is to show check marks when a user selects a speaker, so we need to go into our selectItem function, which is called when a user clicks on a list item. We have to loop over all the check targets. For each check target, we need to inspect the check target's sibling, which is the span containing the speaker's name.
00:28:36.480 If the check target's sibling text equals the text of the selected item, then we know we should show the check because this is the selected item. If the check target's sibling text is not equal to the text of the selected item, then we know we should hide the check, because this is one of the other speaker options, and to clarify, we know the text of the selected item because we get it from the click DOM event.
00:29:30.840 Because we love our work, we will also have to turn our check marks white when we mouse over them, because we are nothing if not craftspeople. Let's go into our 'highlightListItem' function. Here we’ll add a check that says, if the check is not hidden, meaning if this is the selected speaker, then replace our pink check with a white check, so it will show up nicely in our pink highlight when our user hovers over the list item, and we’ll do the exact opposite in our 'unhighlight' function.
00:30:15.300 So let’s take stock of where we’re at. We accomplished our goal of adding some nice-looking styles to our dropdown. We introduced a few of the main concepts in Stimulus: the controller, the action, the target, lifecycle events, and CSS classes. We can imagine our selectItem method including a post request that sends the selected speaker to a database, but we are not going to do that in this talk. Instead, let's look at our code; does it bring us joy? It's kind of confusing, if I'm being honest. At least it has been kind of confusing to try to explain it out loud to you all.
00:31:19.920 And to me, that's a code smell. Specifically, we loop over our checks in a few places, which I don't love, and there are probably ways we could refactor our code a bit, but I think all of this points to a larger refactor that I'd like to do with you all. I think our dropdown controller knows too much and has too many different responsibilities; it’s just not well encapsulated. I propose we break our dropdown controller into two parts—keeping the logic for opening and closing our list and setting the selected speaker in the dropdown controller.
00:32:09.960 We'll add a new controller called the 'listItemController' that will handle anything specific to a list item in our dropdown menu. This would include highlighting and understanding whether or not the list item is a selected list item. Let's copy our hello controller to get our Stimulus controller boilerplate. Now let's find our list item element. The div that wraps our list item is going to be where I declare my list item controller. We'll use the data-controller attribute and set it equal to 'listItem.' That will tell Stimulus that everything within this div tag should be within the scope of our new list item controller, including the list item, the text of the list item, and the span holding the check SVG icon.
00:33:08.880 Let's add a console log to our connect function to ensure we've connected a controller correctly. Now let's add some targets to make it easy to reference the important pieces of our list item in our list item controller. First, we will add a data-list-item-target attribute to the list item; we'll set that equal to 'item.' We'll add a data-list-item-target attribute equal to 'check' on our span holding our SVG check icon, and we'll also add a data-list-item-target to our span that contains our speaker name and set that equal to 'text.' We'll add each of these targets to our static targets array.
00:34:00.960 Now we will move the classes that we defined on the dropdown controller to our list item controller. The dropdown controller will still need the hidden class because it will be concerned with toggling our entire dropdown list. We can now add these classes to the static classes array. Currently, our list items have three defined data actions: a click action that will call the selectedItem function, and then mouse-enter and mouse-leave actions that will call the highlight and unhighlight functions. All these functions are defined in the dropdown controller.
00:34:51.540 We no longer want our list items to report to the dropdown controller; instead, we'd like a click event to trigger a select function in our list item controller. We'd like the mouse-enter and leave events to trigger the highlight and unhighlight functions in our list item controller. Now let's add some console logging to make sure all of this is working. The next step is to move our highlighting functionality over from the dropdown controller to our new list item controller. I’m really excited for the difference I think you’re going to see because we defined the elements we care about as targets; we can reference them more directly instead of needing to get at them by way of first or lastElementChild.
00:35:42.879 First, we'll add a pink background to our list item, next we'll add the white bold font to our span that contains the speaker name, and finally, we'll apply the same white color to our check. This reads a lot nicer to me—do this to the item, this to the text, and this to the check. We'll do the opposite for our 'unhighlight.' For select, we're going to reach for the last piece of Stimulus that we have yet to cover: the value. Our goal here is for each list item to govern itself rather than rely on the dropdown controller to orchestrate them together.
00:36:35.760 This would be nice, because iterating over a list is annoying; dealing with one thing is simpler. We want each list item to have state—either selected or not selected. We are going to use the Stimulus value to store this state. To do so, we'll follow the pattern of adding data attributes to our HTML. In this case, we're going to add the attribute to the div where we defined our list item controller. We'll add a data-list-item-in-selected-value attribute and set it equal to false, so the list item controller now has a value or state called 'isSelected.' It is initially set to false.
00:37:20.940 Stimulus supports all sorts of data value types; we just need to specify the data type of our new 'isSelected' value in our static values object in our list item controller. We'll specify that this is selected value as a Boolean, because the list item can either be 'isSelected' true or 'isSelected' false—can be selected or not. Now our select function becomes a lot simpler to write. Instead of messing around with list item text, we’ll just update our 'isSelected' value when a list item is clicked on, setting it to true.
00:38:13.440 Using values in Stimulus also comes with some additional powers: we can use callback functions that get triggered whenever any of our values get changed. In this case, we get a free 'isSelectedValueChanged' function call whenever we change the 'isSelected' value. Let's add some console logging here to see how that works. Well, when a list item is clicked on, it should change from 'isSelected: false' to 'isSelected: true.' When the list item is in an 'isSelected: true' state because it has been clicked on, we’ll want to show the check mark.
00:39:05.640 We just need to remove the hidden class from our check target. Since we are only dealing with one list item, there is only one check target to worry about; we don't have to worry ourselves with a for loop anymore. Also, importantly, the change callbacks are called when the controller is first instantiated. This is saying that the value of 'isSelected' has been initially set, so it's changed. This works in our favor, because the callback function we just wrote will automatically hide the checks on every list item that is not selected.
00:39:51.840 This is way better than what we were doing in our dropdown controller, where we had to write a special 'hideAllChecks' function. So, pretty quickly, we got our list item styles working on a list item-by-list item basis, but now we need to somehow create a line of communication between our sibling list item controllers. When our page loads, we get one list item controller per list item on the page. It would be really nice if one list item controller could announce to all of its siblings that it has been selected.
00:40:47.640 As far as I know, there isn't a 'not-clicked' event, but we can make our own custom events. I really like this pattern because it creates a barrier between caller and callee. We are going to try to get one list item, upon being clicked, to shout 'Hey, I’ve been selected,' and the rest of the list items need to hear that event and make sure that they set their own isSelected value to false.
00:41:41.280 To define a custom event, we'll create a detail object with a value key. We'll assign the inner text of the text target on our list item to be that value. In other words, the detail of our event is the name of the speaker that was just clicked. Then, we'll make our custom event and call that event a 'listItemSelectedEvent.' We’ll use 'window.dispatchEvent' and pass in the event we just created to get published in our HTML.
00:42:38.640 Now we want all our other list items to listen for this 'listItemSelectedEvent.' To do so, we'll add a new data-action; we'll say that when we register a 'listItemSelectedEvent,' we should call the 'deselect' method in the list item controller, and then we can define this 'deselect' function with a console log to make sure we haven't gotten too lost. We will log the value of this selected item that has just dispatched the event along with the value of the item that received the event.
00:43:35.640 So, we want to log both the list item that called out 'Hey, I was selected,' as well as the sibling list item that now needs to understand that it is not selected. Also, we have to remove the event from the parameters in the select event because we will go on to define an event constant a few lines down, and that would be a whoopsie! These items appear to be logging correctly; when these two values are the same, it means the list item that announced 'Hey, I was selected' is also the list item that received the event—it heard its own announcement.
00:44:31.200 In this case, we can just drop the event. But when the selected speaker on the event is different from the speaker name on the target item itself, we know we need to update our isSelected state to be false, because one of the sibling list items is the one that just told us it was selected. Whenever we update a value, we trigger the change callback, so here, setting isSelected to be false will call the isSelectedChanged function. This will also cause the uncheck functionality to activate, which is exactly what we want.
00:45:28.760 At this point, our list items are more or less autonomous. They are much more encapsulated than in our initial implementation, and we no longer have to loop over any lists—this is an improvement. Now all that is left to do is clean up our dropdown controller. We still need the dropdown controller to hide and show our list, but we can delete the selectItem, setCheck, highlight, and unhighlight functions. Before, when we clicked on the list item, we toggled the hidden class on our list. Now we prefer to use the 'listItemSelected' event.
00:46:28.240 Let's add a data action to our button that listens for that custom 'listItemSelected' event. When that event occurs, we'd like to trigger the 'setSelected' function in the dropdown controller. That 'setSelected' function now needs to take the name of the speaker from the 'listItemSelected' event and put that into our speaker target. This will change the text of our button from 'Select Speaker' to whichever speaker is actually clicked on.
00:47:40.920 In our connect function, we want to make sure we are hiding the list when the page loads. Now we have a refactored RailsConf speaker dropdown! This was quite the journey, and I appreciate you all for sticking with me. We covered quite a bit. We looked at how to create a Stimulus controller, we used actions to call functions in our controllers when specified DOM events get fired, we utilized targets to find important elements in our HTML, we used values to store state, and we applied styles with CSS classes.
00:48:20.040 We also used our own custom events to communicate between our Stimulus controllers, which enabled us to better encapsulate our code. I hope you’re walking away with increased interest in Stimulus, maybe a few new tools added to your repertoire, and perhaps not total disgust with my VS Code proficiency. I’ll be answering questions in the stimulating events Discord channel. I’m happy to talk about Stimulus, Ibotta, and Turing, so come by and say hi.
00:49:12.300 I’m sending love to all my RailsConf brothers and sisters, and I hope all of us will get to see each other in person at next year's RailsConf. Thank you again for joining me, and enjoy the rest of this amazing conference!
Explore all talks recorded at RailsConf 2021
+65