We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

Element Modifiers: Under the Hood

00:00

Formale Metadaten

Titel
Element Modifiers: Under the Hood
Serientitel
Anzahl der Teile
16
Autor
Lizenz
CC-Namensnennung 3.0 Unported:
Sie dürfen das Werk bzw. den Inhalt zu jedem legalen Zweck nutzen, verändern und in unveränderter oder veränderter Form vervielfältigen, verbreiten und öffentlich zugänglich machen, sofern Sie den Namen des Autors/Rechteinhabers in der von ihm festgelegten Weise nennen.
Identifikatoren
Herausgeber
Erscheinungsjahr
Sprache

Inhaltliche Metadaten

Fachgebiet
Genre
Abstract
Modifiers are a basic primitive for interacting with the DOM in Ember. But, how would you describe how element modifiers work to someone new to Ember? Would you be able to? By the end of this talk, you’ll understand how modifiers work under the hood and be able to teach this to your teammates.
Twitter <Softwareplattform>Web-DesignerGeradeCodeAuszeichnungsspracheTypentheorieAutorisierungKlassische PhysikEndliche ModelltheorieMusterspracheZusammenhängender GraphTopologieHilfesystemBasis <Mathematik>SichtenkonzeptVolumenvisualisierungNormalvektorEreignishorizontExpertensystemVersionsverwaltungComputeranimation
Gebäude <Mathematik>Endliche ModelltheorieProdukt <Mathematik>Kartesische KoordinatenApp <Programm>Computeranimation
Coxeter-GruppeEndliche ModelltheorieCASE <Informatik>Konfiguration <Informatik>CodeMusterspracheATMSchreib-Lese-KopfMultiplikationsoperatorProdukt <Mathematik>Rechter WinkelComputeranimation
DatenmodellMenütechnikQuellcodeBridge <Kommunikationstechnik>Produkt <Mathematik>Basis <Mathematik>Physikalisches SystemFokalpunktOpen SourceWeb-DesignerHilfesystemProdukt <Mathematik>TermWort <Informatik>BitSchnittmengeKartesische KoordinatenEndliche ModelltheorieProgrammfehlerBildschirmmaskeBridge <Kommunikationstechnik>SoftwareRandwertShape <Informatik>MeterGebäude <Mathematik>CASE <Informatik>SoftwareentwicklerMixed RealityArithmetische FolgeDifferenteExpertensystemDemoszene <Programmierung>Kontextbezogenes SystemPunktMAPHardwareBrowserSichtenkonzeptBetrag <Mathematik>Computeranimation
Virtuelle MaschineVirtuelle RealitätTemplateVersionsverwaltungBrowserBimodulTabelleCompilerLaufzeitfehlerVektorrechnungElektronische PublikationProgrammierparadigmaVirtuelle MaschineServerClientZwischenspracheTopologieFront-End <Software>Kartesische KoordinatenZeichenketteFramework <Informatik>Einfache GenauigkeitWeb-SeiteInformationBrowserVersionsverwaltungHilfesystemTemplateFlächeninhaltBereichsschätzungMereologieSyntaktische AnalyseVererbungshierarchieNichtlinearer OperatorFormation <Mathematik>CompilerDynamisches SystemTabelleZahlenbereichEndliche ModelltheorieElektronische PublikationAbstrakter SyntaxbaumWeb SiteTouchscreenRechter WinkelLaufzeitfehlerMaßerweiterungMAPTwitter <Softwareplattform>Weg <Topologie>ProgrammfehlerMultiplikationsoperatorGebäude <Mathematik>DebuggingKeller <Informatik>SelbstrepräsentationComputeranimation
Kategorie <Mathematik>TemplateMathematische LogikHilfesystemFunktion <Mathematik>EreignishorizontVektorrechnungEreignishorizontMathematische LogikAttributierte GrammatikKategorie <Mathematik>SoundverarbeitungGanze FunktionZahlenbereichZusammenhängender GraphComputerspielFunktionalOrdnung <Mathematik>RechenschieberDreiecksfreier GraphEin-AusgabeDatensichtgerätTypentheorieTemplateComputeranimation
EreignishorizontInstallation <Informatik>Physikalisches SystemInjektivitätKategorie <Mathematik>MusterspracheDatenverwaltungEreignishorizontKartesische KoordinatenParametersystemObjekt <Kategorie>Algebraisch abgeschlossener KörperWrapper <Programmierung>MAPComputerspielDreiecksfreier GraphComputeranimation
CodeEin-AusgabeFunktionalOrdnung <Mathematik>ParametersystemMixed RealityElektronische UnterschriftEndliche ModelltheorieMultiplikationsoperatorBimodulEreignishorizontMereologieSoundverarbeitungHilfesystemDreiecksfreier GraphAutomatische HandlungsplanungOptimierungsproblemCASE <Informatik>Computerspiel
InformationsverarbeitungVisuelles SystemTropfenMenütechnikDemo <Programm>App <Programm>System FTouchscreenZahlenbereichMenütechnikGemeinsamer SpeicherKartesische KoordinatenCoxeter-GruppeTropfenElektronischer ProgrammführerSoftwareentwicklerInverser LimesVerschlingungZweiTeilmengeInformationsverarbeitungDienst <Informatik>VersionsverwaltungCASE <Informatik>Web-SeiteMinkowski-MetrikBitVererbungshierarchieRechter WinkelComputeranimation
Lokales MinimumCASE <Informatik>Reelle ZahlMultiplikationsoperatorZahlenbereichSystemaufrufInstallation <Informatik>ServerGebäude <Mathematik>Kartesische KoordinatenGenerator <Informatik>Programm/QuellcodeComputeranimation
Lie-GruppeAmenable GruppeMittelwertGEDCOMKreisringGammafunktionRuhmasseKlasse <Mathematik>CASE <Informatik>DickeMathematikMinkowski-MetrikWasserdampftafelFunktionalSoundverarbeitungZweiElektronische PublikationTemplateEreignishorizontMenütechnikKategorie <Mathematik>BitMessage-PassingSchnittmengeMailing-ListeHook <Programmierung>Cross-site scriptingRechter WinkelSkalarproduktComputeranimation
Formale GrammatikHidden-Markov-ModellKontextbezogenes SystemProgrammbibliothekAutorisierungProzess <Informatik>Total <Mathematik>GruppenoperationPhysikalisches SystemComputeranimation
Formale GrammatikRechenschieberTwitter <Softwareplattform>Gebäude <Mathematik>Elektronische PublikationSichtenkonzeptSkalarproduktVererbungshierarchieGüte der AnpassungTypentheorieFreewareMusterspracheGeradeRechter WinkelComputeranimation
Computeranimation
Transkript: Englisch(automatisch erzeugt)
All right, hello and welcome everyone. My name is Jaya Bhaga and I'm a UI engineer at HashiCorp.
I work on the Nomad team. A quick fun fact about me is that I'm really new to web development. I learned how to code about three years ago and I have less than a year of experience with Ember.
And I think that's an important story to mention in the very beginning. Today, I am covering element modifiers under the hood. And what I think the purpose of this talk isn't to make you some expert overnight. Instead, my goal is I wanna show you
as someone who's new to Ember, here's how I think through problems and what mental models I use to get myself unblocked. And I think that's a very important, it's a very important thing because when I first started developing, things started off and they were like a breeze at first.
My tickets would be super well-defined and they'd be like well-scoped. Be like, hey, can you change the copy on this one view that we have? Could you give this component to makeover? Maybe we're kind of reordering the markup that we have.
And I was doing great at that. But then when there was a bigger challenge, like, hey, can you render a template based on authorization or could you implement a bunch of special requests? Like maybe have this component render outside of the normal DOM tree and then make sure that it closes only
when these events happen and not these events. And then I would get stuck. And basically the issue was that if I had documentation or tutorial, I could easily knock it out. But if it was anything beyond that, I would be stuck for a while.
There's a lot of things that would stump me on a pretty regular basis. I've inherited a code base of more than 70,000 lines of code that I didn't write and the majority of it is still in the Ember classic pattern. So if I need to upgrade to Octane or I was up on Ember 3.20
up until maybe about two weeks ago. Now I've gotten to the latest version, but upgrading Ember or learning how to work with certain add-ons, those types of things were very difficult for me because I didn't have an understanding about how Ember was working.
And not having that understanding meant that I would have to go to someone else for help. And I almost view it like there's this very big chasm between where someone like myself, a beginner product engineer,
has to start solving bigger problems. And so I say this thing where in today's world it's never been easier to stand your application up. Building your app and getting something working is super fast today. But maintaining the application itself, that's where a lot of the challenge occurs.
And being able to maintain an application is where I don't think that there's a lot of resources. And this talk is supposed to go through my mental model of how I think through that. And as I think about crossing this chasm or thinking about these situations where I'm starting to be attacked by things
that I don't necessarily understand, I think about it very similar to dragons that are attacking my city here. And when I think about dragons that are attacking my city, I think that I have a few options here. My first option is I can escape,
which is the case of I can have one of my coworkers who are super knowledgeable come bail me out. That's one thing I can do. Another thing I can do is I can defend. I can choose to not upgrade any of these patterns that I'm seeing. I can just stick with the classic model. I can just stick with the pattern I see somewhere else in the code base.
Or my third option is I can learn how to use my tools and master them so that I can attack these dragons. I think usually it makes sense to play defense. And I think a lot of times that's what we wanna do because we know that there's a lot of business value to technical debt and the fact that it's working in production right now.
So why bother breaking it? Well, one of the things is when dragons are attacking and if all you do is immediately go into defense mode and you're not attacking them head on, those dragons just keep getting stronger. Or in my case, technical debt continues to accrue. And now it may get to a size where
I always need to ask someone else to bail me out. Or it might even get to the size where the person that needs to come bail me out and is capable of bailing me out can no longer bail me out because the situation has gotten too bad. So that's why in this presentation I'm really gonna focus on,
how do I think through these things as I'm learning how to tame these dragons? This is my way of thinking about things. By no way is this perfect. By no way is this absolutely accurate. This is my system of thinking and how I solve the problems that I deal with on a regular basis. And some of it may not be absolutely perfect to spec.
So I just wanna keep that in mind because I'm a beginner and I'm a work in progress. But I think about things in three M's. So first I wanna understand the model of what I'm looking at. And so in the case of element modifiers, I think we wanna go all the way up to Glimmer.
Be able to understand Glimmer just enough in context. The reason behind that is, when I'm going through splunking, because I just got through a bug, I'm wondering where should I look? When I understand the boundaries of what something does and doesn't do, that tells me how far I should be looking. That tells me what keywords I should be using.
So that's why we come all the way up to the model. Have a solid understanding of it, but know that you're not supposed to be an expert on this stuff. Next thing is the mystery. The actual element modifier itself. What can it do? What can it not do? What are some things behind the scenes that we don't see?
What's that mystery piece? Once I understand that, again, that's gonna tell me the boundaries of where I should be splunking. And then lastly, there's the actual method itself saying, hey, I have some business requirement. How does that kind of compile down to what I know as a developer? What tools should I be looking out for?
And that's gonna be that last step there. So my intention and outcome for this talk, there's a fourth M word that I really just don't like. And it's the word magic. And when I first started working in Ember or even like in web development in general, if I tried to understand something that was beyond my level,
everyone would just keep telling me that it was some magic. But the more you say the word magic, the more imposters are in my field. Because if I can't understand the magic, but it seems like you're able to, the senior developer, it seems like you're a magician and I'm never gonna be there. Like I'm just gonna be someone in the crowd.
So I do think that this is a word that I want people to stop using and to start explaining things in terms of JavaScript or in terms of what we know as developers so that it can be communicated in some way. Next, I wanna bridge the gap between product and infrastructure engineers. I view product engineers as someone who gets
a set of user stories, they work with some business stakeholders and they build an application. Infrastructure engineers are working with the APIs that we have and they're thinking about, they think a little bit about how browsers work. They might think a little bit about how hardware works and they definitely do think about how software fits into the mix here.
Now, increasingly I do think that crossing that chasm of being able to work with different dependencies, build tools and all those other things that are gonna help you become an intermediate to advanced developer requires you to start thinking like that. And you can't just have like that straight up product had anymore.
You have to think about how to solve some of those other issues, even if you're not incentivized to solve them. Otherwise those dragons are gonna keep getting bigger. And then my third point is hopefully I can inspire and empower you to contribute to open source whether you're gonna contribute to a documentation or whether you're gonna fix some low or high level bugs.
I do think that if we're all using open source software we should try to contribute in some way, shape or form by helping with documentation, teaching or contributing to the APIs themselves. So that's what my intentions and outcomes are. So let's get into it. We have our first dragon here and our first dragon is Glimmer.
So one of my biggest pet peeves when I was first learning Ember was that I kept seeing Glimmer when I was learning Ember. And when I asked about what Glimmer was, people said, don't worry about it. It's just magic. And that didn't help me at all because then I was stuck thinking,
wondering whether my bug was related to Glimmer or not. So understanding Glimmer with first principles is important. I think about Glimmer with these first principles in mind. Number one, it compiles our templates into a JSON blob and its goal is to reduce the over the wire size.
I'll dive more into what that looks like when I cover what happens when we actually go to the browser. Number two, it's architected like a virtual machine and it has two main subsystems that we wanna keep track of. Number one are references. So anything that we're pointing to
that isn't like a static value, it's a dynamic value. Number three, revision tags. These dynamic references, are they up to date or are they not up to date? And if they're not up to date, then we need to make sure that those values get recomputed. And then the third thing that we do is we render some DOM, right?
So where we think about Glimmer is it's helping us go from this stage of we've fetched information from our website, now we need to go paint what's on our screen. So in our browser, we have this client server paradigm. I'll go to twitter.com, I'll ping the Twitter server
by way of just typing in twitter.com, which makes that request. Twitter.com sends us some information back. They can send this information back in a few different ways. One way, the very popular way before single page applications came about was sending back a string of HTML,
which then gets parsed into a DOM tree and then painted onto the DOM. Now, a lot of front end frameworks are sending JavaScript over the wire. But if we're sending JavaScript over some string of HTML, now we have a very expensive parsing operation.
So the beauty of what Glimmer is doing is it's finding that happy medium between being able to parse that string of HTML or interpret what that JS is by way of making sure that we're just getting this huge JSON blob,
which drastically reduces that size and it's super easy to read. So this is where Glimmer is helping us out. I think it's pretty important to know what Glimmer is doing because now you feel confident about one part of the stack. Once you have confidence growing in one area, you start to have confidence
that will build in other areas. So let's continue building out this mental model of what's going on in Glimmer. There's probably about six stages that I think about. Stage one, where all of our handlebar files get compiled down in this pre-compiled stage to this intermediate representation where we're gonna start to form this abstract syntax tree
of what our DOM is going to look like. It'll be validated to make sure that we're sending back what will convert into a valid DOM later on. Then we'll have our build time compiler which will then create that JSON blob that I was referring to earlier. And then we're gonna have this module table
where our template helpers and our element modifiers live that we're gonna reference. And then lastly, we're gonna paint our DOM and whenever we have updates happening, we have this runtime compiler that will be making sure that we're triggering the correct operations. And that's not the extent
that we need to understand about Glimmer with respect to beginner to intermediate level. Now that brings us into our next subject which are element modifiers, right? So our element modifiers are Ember's way
of giving us DOM manipulation rather than us manipulating the DOM on our own which is something we probably don't wanna do. We wanna tap into Ember in order to do that. And using element modifiers, we number one, we attach event handlers to our elements.
Number two, we can fire callback functions that are associated to the life cycle of an element. I'll tie into how we're able to do that in the next slide. Number three, we're gonna be able to manipulate element properties. So imagine I wanna dynamically change the attributes of the elements that I'm working with. Number four, I may wanna communicate
with other elements inside or outside of the component that I'm working in by way of having a reference or being able to just attach an event listener to the entire DOM itself, which is what I think some other Ember add-ons
like Ember on outside click modifier, it's able to do something like that. In plain English, I think the big thing for beginners like myself is that if I wanna do something that's JavaScripty inside of my template, I'll reach for an element modifier if I need to manipulate the attributes
of an element dynamically, or if I need to fire off some side effect after alongside my events on some type of input event, whether it be some mouse events or an input event like the key down or a keystroke. And then I'll use a template helper in the event
I wanna fire off some custom JavaScript logic that's gonna yield out a property. Maybe I wanna display a property in its correct date format. I may be able to just use a quick template helper in order to do that. So that's what an element modifier is
and the types of things that it helps us do. How are they created? So Ember has exposed, so Ember has exposed this API for working with managers that we don't need to touch. This is the modifier manager. But I think the important thing to know is that we have this manager pattern
and the manager pattern is when we have an object that's responsible for coordinating the lifecycle events for its underlying property. And we have a lifecycle with element modifiers that we can tap into. We have its creation, so when it actually is being registered into our application.
Once it's registered into our application, that allows us to tap into Ember's dependency injection system. Then there's when we actually invoke it or install it once the element is painted onto the DOM. There's what we do when any of the properties that we're relying on, so maybe arguments that we've passed
into our element modifier update. And then of course, if that element is being removed off from the DOM, we wanna make sure to destroy any listeners we have and avoid any stale closure that we might run into. If we wanna create our own modifier, Ember has released a high-level wrapper
around that low-level API that I just talked about with the modifier manager and the modifier itself. So why don't we take a look at what that looks like right now? So at a glance, we see that we're importing from another module and there's a function and we have this higher order function of a modifier. It's gonna take a function as its parameter.
And that function itself, this callback function that we see being executed here, that function itself has a function signature where it can take three inputs. The first input that it's guaranteed to take is the element that it's going to be attached to based on whatever reference we currently have,
which I think is very important to make sure that we're working with an element that's actually on the DOM. Next, we have positional arguments. So it's arguments that would be in a particular order that do not have a name. And then it could take a third optional parameter of named arguments that we may wanna use later on.
And then this function itself is also a higher order function in the sense that it returns out a function on its own. That function that it returns out is what will be fired once we hit the destroyed part of the life cycle.
So this is whenever we're planning to do some cleanup effect. And we wanna do a cleanup effect in the event that we have some, something we're subscribing to. So we're subscribing to an event by way of an event listener. When it's time to tear down, we wanna make sure to remove that so that we don't have those listeners
just hanging around everywhere. All right, so that's like the quick way to understand that. So now we've just gotten through two of our big models here. First, we understand the model of where Glimmer fits into the mix. Now we understand when we wanna reach for an element modifier,
what API is gonna help us use it, and the signature of the function that we're gonna use to go ahead and create that API. So that's awesome. So now we have our final dragon. This is dealing with business requirements. This is usually the place where I normally wanna give up
because I can understand how things work, but then I have all this code that I need to deal with. And in this case, this is why you wanna have a very strong understanding of the model and the mystery of the APIs that you're using so you know what your best solution's gonna be and how you can get to that optimal solution.
So in our situation, we're gonna try to create our own accessibility modifier. And there's a quick note that I wanna make is that I am probably only one to two Google searches ahead of you on understanding accessibility. Up until this talk, I had only thought about accessibility
as enabling screen readers, and I was totally wrong. So there's two quick pieces that I wanna share. So number one, sometimes the apps that we're making are gonna end up turning into more of an obstacle for folks than a convenience for other people. And I do think that later on,
you're gonna see this amazing presentation from Zoe about space-jamming accessibility, and you're gonna be able to say, wow, this new website doesn't seem as if it's as convenient as the old version of this other website. And they'll walk you through why that's the case.
So I think that's one important note to have. The other important note is that when we talk about accessibility, we're not just talking about visual accessibility. Sometimes it can appear in motor and cognitive limitations as well. If you wanna learn more, there's tons of resources at W3C.
So you can go to w3.org forward slash W-A-I, or you can click that full link right there. You'll see a full guide for developers, and you'll even see tutorials on how we create certain very common UI elements that all come together.
So the example we're gonna work with today is gonna be creating a more accessible dropdown menu. That's what we're going to try to create. So the way that I'm doing that as someone that's super new to development is I'm gonna turn this into a user story. So as a user with motor limitations,
I'd like the dropdown menu to stay visible long enough so that I can read and select the dropdown menu item. And an acceptance criteria that we have for this is that our dropdown menu should stay open for about 0.8 seconds. I don't know where I got that number from.
Maybe I just picked it because it was a memorable number to be honest, rather than one. So now that is our user story. So let's get to our demo application now, all right? I'm gonna exit out of full screen, and here we have our demo app. And this demo app is made to be just what a nav bar
of like another Ember agency would look like. And so once we come over services, we see that there's a few other pages that show up underneath here. But the second I stopped hovering over, this goes away, which I'm not fast enough to do that.
Or I might have a little bit of a shaky hand. So how can I do that? So now let's come over to our actual application. And one of the first things that we're gonna wanna do here is we're gonna want to install Ember modifiers. So let's go ahead and do that.
I'm gonna kill this server first. And I'm killing it because once I add an Ember package, that's normally like a build time concern. So I'm gonna go ahead and install this. Okay, so I'll say Ember install Ember modifier. And I'm pretty sure that is the case.
It looks like we already have this installed, thankfully. I think I probably should hit that pause button real quick. All right, great. And so now my next move is I'm gonna go ahead and create a brand new modifier.
And so the way that I'll do that is I'll say Ember generate modifier, and I'll call this modifier hover. And so now let's think through our solution real quick. So I have this property that when I hover over this,
we'll start to show the list that's underneath it. And so it looks like if I take a look at this CSS that whenever we hover, we're gonna show this property. Instead, we may wanna turn that into some CSS class. So that's how I'm thinking about creating this modifier.
And so let's come over here and let's write a quick modifier for ourselves. So the first thing that we're gonna wanna do is we're going to want to add an event listener that will hook into our mouse over
as well as our mouse out. And so I'm gonna just use what we would see from W3C on how they would go ahead and solve an issue like this. So we're gonna add this event listener and this event listener is going to happen on mouse over, right? And now let's go ahead and let's create our callback here.
And so we will add to our element class name. We're gonna change this a little bit from pass sub menu to also have this open property. And then this will also mean that we need to change some of our CSS.
Also gonna wanna clear our timer. Amazing, and now we'll probably have this second element modifier here or this event listener for what's gonna happen on mouse out, okay?
And this will remove that name out for us so that we don't need to worry about it, but we're gonna do that on a timer. Sweet, now let's come over here. Let's get this actually into a timeout, okay?
Set timeout, this would be some function here. And we will do that every 800 seconds or every 0.8 seconds. This looks like this worked just fine. This probably just needs a semicolon
or does it need a comma because that's over. Okay, sweet. This is probably just an issue with my linter yelling at me. And all right, sweet. And now let's make sure that when this function returns
that we're removing these. So let's get two of these event listeners. So omit.remove event listener. And let's do this first one on mouse over. And then of course we'll have this effect run and then let's go ahead and do this once more, mouse out.
Okay, sweet. And now let's just make sure that these move these.
Okay, sweet. And I'm pretty sure if I just run yarn length fix after saving this, that this file will look just fine.
All right, amazing. And now we probably just have one more change to make here to make sure that we factor in for our open class. And now we'll add this into our template where it's needed so we can add this modifier right into here.
We just need to make sure that our element modifier has registered itself. Okay, it looks like this isn't registering just yet. Let's debug what the case of that could be. My first guess is that this is occurring
because we just need to go ahead and rebuild. Let's give this a rebuild. Okay, yep. And all we needed to do was trigger that rebuild.
So now we'll hover over here and we will move over and we see that this is open for about a good second here. So now there we have it. All it took was creating this element modifier right here and then using this directly in the element space
that we have available. Now, of course, this could be refactored a little bit so that this could be more generic, that it can be a little bit dynamic as well. And I don't want you thinking that, oh my gosh, this was such a long walk for a short drink of water. Because although it does feel like it was a long walk
for a short drink of water, what I do think is super important is that we need to be able to learn how to tame the dragon that you're dealing with. And so my system for going about doing that as a total ember noob is being able to say, okay, well, if I have this business requirement,
well, what things, you know, how do I do X in ember? So how do I fire a callback after the user takes some action in ember? Well, now I know the answer to that is an element modifier. And if I go one step above that to say, okay, well, what's above an element modifier? And I start my understanding saying, hmm, what do I need to know about Glimmer
that is in the context of an element modifier? Now, what do I need to know about element modifiers and how they work? One level deeper than what I would do, maybe akin to what a library author would have to know. Then my job becomes a lot easier because now I know where to look to debug.
I also know how to start dealing with some of the larger issues that I'm facing with. And instead of retreating or trying to escape the city, I still feel confident to attack that dragon, which allows me to feel like I'm beginning to master ember, that I'm much more autonomous, and that I do have a greater sense of purpose.
So hopefully that'll allow you to do the same. I wanna leave you on one note here. And I said the same thing at Emberfest, which is when I was first getting started, learning was very difficult for me. And I do notice that a lot of people do read the RFCs and there is a very good RFC culture here,
which is pretty amazing. But when you read, you read to collect the dots, and then when you write out into the world, you're able to connect the dots. So I do think it's super important to make sure that you're contributing back, whether it's a blog or a conference talk,
to explain how you're thinking about these types of issues from your perspective, because it'll really help a beginner like myself. And then my last note here, again, my name is Jay Baga, UI engineer at HashiCorp. And one big thing that I'm really gonna try to do over the course of the next year is create a free course on UI patterns
for understanding Glimmer. And then hopefully eventually one day down the line, I'll have my own Ember course. But that's down the line. Thank you again, everyone. Appreciate everyone sticking through all the way to the tail end for this. All right, have a good one.