skip to content

Let’s Learn Ember.js

When I asked Robbie Wagner what Ember.js is, he called it “the Rails of JavaScript”. Learn what it is and how we can use it in our web projects.

Full Transcript

Click to toggle the visibility of the transcript

Captions provided by White Coat Captioning (https://whitecoatcaptioning.com/). Communication Access Realtime Translation (CART) is provided in order to facilitate communication accessibility and may not be a totally verbatim record of the proceedings.

JASON: Hello, everyone, and welcome to another episode of Learn with Jason. Today on the show, we had a last minute switch and I am so eternally grateful to our next guest for being willing to put together a whole show for us in under 24 hours. We're gonna be learning about something that is new to me, but not new to the web. We're gonna be talking about Ember JS. But before we talk about that, let me first bring to the stage, our guest for today, Robbie Wagner, how are you doing?

ROBBIE: I'm good. How are you?

JASON: Again, I cannot express how grateful you were showing up on 24 hours notice to this show. Not only that, but to show up to teach me something.

ROBBIE: Yeah.

JASON: Before we talk about what we're going to teach today, tell us a little bit about who you are, what you do, all those good things.

ROBBIE: Yeah, I'm Robbie Wagner, I'm Robbie the Wagner, Robbie Wagner, Chuck and I discussed, like Robbie the Wagner, like Robbie the Bruce vibes. So, I went with that. I have been doing web Dev since 2012 with 80% of that being on Ember. I'm on the Ember learning Core team, mostly Ember inspector maintenance these days and I'm a frontend engineer at Amazon right now.

JASON: Very, very cool. Well, yeah. So, I have been on your show before. So, it only feels right that you end up on mine. So, let's talk a little bit about Ember. Because I think for some of us, and I am some of us, Ember was something that I heard about a lot a long time ago. And then it sort of disappeared from my radar. And I, being the myopic, you know, the creature that I am, just assumed that because I couldn't see it that it didn't exist. I thought Ember had dropped off, gone the way of the MooTools, you know? Which I realize is very unfair as I'm saying that out loud. So, tell me a little bit about like -- you have been working with Ember. What's the story of Ember? Like?

ROBBIE: Yeah, I think you're definitely not in the minority in thinking that it's dead. Everyone's been saying it's dead forever. But it's, you know, it's kind of the vibes with Ember are like they do stability without stagnation, convention over configuration, all these buzzwords that are like Rails-y, very opinionated. The whole idea, if you go into any Ember codebase, it's similar to any other Ember codebase. It's not like a React codebase, where is everything and where's the meta framework. It's always been like that and they have always been supportive of old versions. As work, we use 3.28, which at least five years old, which is unfortunate. The fact that the new add-ons and things still work with that. You don't get that in any other framework. Angular, everything is dead. Upgrade right now or you can't use it. I think that's hindered Ember a lot, actually, because they do support everything too much.

JASON: Okay.

ROBBIE: People are like, this is not new and shiny. You're supporting old stuff. It's very crufty. I think that's hurt it some.

JASON: Okay. So, this is excellent. But we skipped I think the core statement which is what is Ember.js?

ROBBIE: Okay, sorry. I'm a little ramble-y.

JASON: No worries at all. I realized I didn't even ask. I also made assumptions here.

ROBBIE: Ember is a JavaScript framework. They call it the framework for ambitious developers. Or at least they used to. I haven't read the new home page, honestly. But it's like an all batteries included type of thing. So, think of like a Next.js with even more included, really. Because it has testing built in. It hooks into QUnit, and you can do tests that render kind of like a Cypress style. It has Ember data which is a package that's included by default. You can hook up to your API and have a model and call save on it. And don't worry about where is the fetch calling, it's like conventions for managing your data, caching everything, all of that. It has a router. Don't have to be like let me install React router or anything like than.

JASON: Got it, okay.

ROBBIE: Everything you would need to build a very robust application. I wouldn't use it for a marketing site, something static. I would use Astro or something lightweight. If you have a big, heavy, business-y application, I think Ember is a good thing to look at.

JASON: Got it. So, the way you're describing it sounds very much like the way I've heard people describe Rails. It sounds very much the way that I'm hearing developers describe Laravel. So, it's a sort of batteries included, anything you need -- you don't have to go hunting for like what the community opinion is because --

ROBBIE: Yep.

JASON: -- because the opinion is baked in.

ROBBIE: Yep.

JASON: Got it. There are tradeoffs with that, obviously. The same tradeoffs as with Rails, you do it the community's way, or the project's way, I should say. A community project. But you don't have like this vast variance in ways that you can manage state or ways that you can do routing or ways that you can do whatever. You do it the way that the project says you do it.

ROBBIE: Yep.

JASON: And one of the benefits that you mentioned early was that you have things like, you know, routeing is built in. And convention over configuration. Which that one, to me, is huge. Because there is, I think, a challenge when you walk into a lot of modern JavaScript projects where it doesn't matter if you know the language. Like if you know React, if you walk into a React project, even one that's fairly structured like a Next project every one is different. They're all kind of unique snow flakes where you really do have to do bespoke onboarding to every project when you work in React. Whereas when you use something like Rails or Laravel or Angular or apparently Ember, you just open it up and everything is in the same place and you can kind of navigate the project the way you would navigate any other project because that's the way it works. That's how it's structured. So, you are using Ember -- and then are you using Ember in your day job at Amazon right now?

ROBBIE: Yep.

JASON: Got it. Are you allowed to talk about this? What part of the project do you work on?

ROBBIE: Yeah, I can talk about it. I can't go into code specifics. I work for Art 19, a podcast hosting platform that was acquired by Amazon a couple years back. I wasn't there when it was acquired, but after that.

JASON: Got it. You said earlier, this is a fairly business-y, like bigger application. And so, a podcasting or hosting platform, you're dealing with like, I assume, multitenant organizations. You're dealing with dashboards, you're dealing with uploads. You're dealing with like billing and analytics and, you know, what other kinds of use cases are you handling inside of the app?

ROBBIE: Yeah, analytics is a big one. I'm in a project that's probably gonna take over a year to redo all of our analytics. Make them nice and flashy. We have like ad insertion is a big thing for podcasts. Dynamically figuring out where those should go, swapping the ads in and out. Letting people see their like -- what do you call it? Is wave form the word? What the audio looks like and where to put the ads in.

JASON: You do that inside Ember?

ROBBIE: Uh-huh.

JASON: Fascinating. I would have expected that would be some kind of backend process.

ROBBIE: No. I mean, you figure all that out and once you know the timestamp, you pass that to the backend and the backend will swap out at that timestamp.

JASON: That's super-cool. All right. Very cool. All right. So, I have a couple questions from the chat. One is: Does Ember have server side rendering?

ROBBIE: Yes and no. It's a little bit of a loaded question. It's a thing called Fastboot which runs -- is server side rendering for Ember. I don't know how supported it is on the most modern Ember stuff. And then there's a thing on top of that uses Fastboot, called P-render, if you want to have a static site instead of server side rendering. Like I don't know all the stuff about this and there are smarter people than me that talked on other podcasts and Chris Manson was on recently and talks about it. The new build site is Embroider. It's a Vite plugin and then Vite will handle how it gets built, what targets it gets built for, all that kind of stuff. So, one day we'll be able to do everything everyone else is doing the easy way. But for now, there's extra add-ons and potential hiccup it is you try to do that.

JASON: Got it. Very, very cool. People are saying -- Fadda is very familiar with Ember. Phanor was like me, didn't realize Ember was in production here. Jimicode, I can, but not here. Go to the Learn with Jason Discord and there's a spot to post projects. What is up? We got folks dropping in. What's happening? So, let's talk a little bit about what you want to cover today. Because I gave you exactly zero notice to plan for this.

ROBBIE: Yeah.

JASON: So, if I -- for a sort of Hello World -- or I guess an introduction to Ember.js, what is a good application to tackle in that scenario?

ROBBIE: Yeah. I don't necessarily have a great -- like the thing that I think would be interesting would be using some sort of actual backend so we could play with some Ember data stuff potentially. But maybe that's too much for today. I don't know. But the super rental tutorial on the website is pretty decent. Take you through using Ember CLI to spin up an app and building your first routes and components. So, kind of following along with that and then we can diverge from that if we want. You know, kind of how we feel.

JASON: Yeah, yeah. Why don't we do that. So, let me flip us over into our pair programming mode here. And before I get too far into this, I'm first gonna say, this show, like every show, is being live captioned. We've Amanda here from White Coat Captioning making all that happen. The link to view the live captions is down in the scrolling banner although the bottom of the screen here. That is made possible through our sponsor Netlify who is kicking in to cover the cost of that live transcription. Thank you very much, Netlify. Make sure you go check them out, tell them I sent you. Also, we're doing something new for right now. We just kicked it off last week. Tuple is sponsoring the pair programming part of this show. And the way they're doing that is by giving us access to Tuple, which is a tool specifically for pair programming. And so, I actually have Robbie on a Tuple call with me. And he can do things like draw on the screen on my screen. Watch this. Ah? Isn't that neat? So, now we -- and there's a whole lot more that he can do. He can do things like send -- he can take over my screen and copy-paste code. So, it's got like a shared copy buffer, which is really cool. He can also -- he can copy-paste code, he can actually take over and edit if I get lost, send through docs and links. Which saves some pain we were having before. Because in previous episodes of the show, you might remember, I have had people create a private gist that I have to DM and I have to open and copy from. Or they have to send me something in the private chat and they have to copy-paste that out, a link and then I have to open it and copy-paste over to chat. It's a nice little workflow optimization. If we weren't already on a call like this, we could also do voice and video while we work together. It's a cool tool. Thank you Tuple for doing that for us. We are talking to Robbie today. Robbie is on Twitter as RobbietheWagner. Let me drop that into the comments here. And let's also drop a link for Ember right into these comments. Framework for ambitious web developers. So, it still does say the same thing that you said before. If you can access your screen, he can hack, yeah. So, this is the thing about Tuple. Tuple is meant for pair programming, right? So, it is -- you don't get on this with a stranger. You get on this with people that you trust, right?

ROBBIE: Yeah.

JASON: That's the goal here. But yes. And there was a question -- so, TypeScript generics are Matt Pocock is still happening. Matt had something come up last minute. Robbie was kind enough to jump in. Check the schedule, Matt is still coming on the show. I think it's gonna be in about a month. Let's see... Jimicode, questions like these are on Tuesdays. I do a solo show where I'm happy to answer these questions. Today is Ember. You know what? I trust you too. Thank you. Are Tom DAIL and Yehuda Katz still involved with Ember?

ROBBIE: Yehuda is, Tom is resting on his pile of money somewhere I think. Yeah. I think he still works for LinkedIn. I'm not sure. I haven't heard much from him in a while.

JASON: Yeah. I was actually -- you know what? That's true. I haven't seen much from Tom Dell either. Okay. Why don't we go ahead and get started here? Let's jump in. What should I do first? If I want to build a project?

ROBBIE: Yeah. So, I think the tutorial, if you click read the tutorial here, is kind of a good starting point. You know, whether you want to go through the entire tutorial or whether you're just kind of wanting to learn how to get started. So, if you scroll down a bit, I think -- is it on this page? This page is like, you know, here's the stuff you're gonna build. But we might be on the next page when it has the like first command we need to run.

JASON: Orientation.

ROBBIE: Yeah, we got to install Ember CLI. Everything is through Ember CLI.

JASON: I realized I have a couple things open on my screen that I'm gonna hide real quick. I don't think any of it's sensitive. But it's also cluttery, I want it gone. This out here, bring this back, and open up a new terminal. And I need to -- let's get this over here and this over here. And so, I need to npm install global Ember CLI.

ROBBIE: And while that's installing, just kind of a note to this next command here. Like if you're building an app, you do Ember new, if you're doing an add-on, Ember add-on. That's one of the big powerful things about Ember. The add-on community, there's adds-ons for everything you want to do, authentication, Ember symbol auth, pretty much every Ember app uses it. You don't have to figure that out every time.

JASON: Okay. Got it. So, I'm gonna do Ember, new, and I'm just gonna run exactly like it says, super rentals.

ROBBIE: I'm gonna have some notes to add to that. Are you a PNPM user?

JASON: I'm not.

ROBBIE: Okay. Skip that. Do dash, dash TypeScript and dash, dash Embroider.

JASON: And what does embroider do?

ROBBIE: Embroider is the new build system.

JASON: Okay.

ROBBIE: So, embroider is the glue that hooks into -- right now, it's mostly web pack based and Vite-based soon. It will go away entirely and be like a Vite plugin. So, they're working on that.

JASON: Nice.

ROBBIE: But embroider is --

JASON: Oh, screen share is not active. I'm just out here doing stuff -- sorry, everybody. Okay. So, I have -- let me go back up through what I did. I installed -- installed the Ember CLI which is up here somewhere. There it is, npm install, I installed it globally. And then down here, I checked the version. So, we have Ember CLI 5.1 -- 5.10.0. I'm using Node 22. And then I moved into a my GitHub folder here and then we ran this new command, Ember new super rentals, dash, dash langen, and then turned on embroider, the new build system. It's been creating an app. It's all done. We can get into super-rentals here. And then I'm going to -- actually, I think I can just open this. And it's gonna be in the GitHub folder somewhere. Okay. Let's see... we'll close that. We'll open this. And it said then what?

ROBBIE: Yeah, if you want to spin this up just to see what it's got so far, you can do -- well, they say npm start. But yeah, that will work. That just is pointing to Ember serve. I was gonna say run Ember serve. Same thing.

JASON: Okay.

ROBBIE: So, yeah, this will open on localhost 4200. If you go to that, it will have like a welcome to Ember.js.

JASON: Hey! I like it when a project has a good mascot. And I feel like the Ember mascot is very fun.

ROBBIE: Yeah. This is actually an add-on, like the Ember welcome page. I don't know what the tutorial tells you to do. But the first thing I always do is remove that welcome page because I want to build real things. And so, you can like just remove the invocation of that component and the like thing from your package JSON if you want.

JASON: Okay.

ROBBIE: But we can also -- do you want to follow the tutorial or just hack around at stuff?

JASON: I'll tell you what. I have no opinion here. So, if we want to play it safe with the tutorial, we can do that. If you want to just show me some things, we can also do that. There's no rules. There's no rules on Learn with Jason.

ROBBIE: Okay. All right. All right. Well, let's just go through some stuff to start and maybe come back to the tutorial after we talk about it a little bit. Yeah. If you open your package JSON. I think in there should be something with welcome in the name.

JASON: Let's see... we got...

ROBBIE: Oh, lots of types. Okay.

JASON: Yeah. Here's all of our types. We've got the Ember CLI stuff. We got the welcome page.

ROBBIE: Yeah. So, you can get rid of that.

JASON: Okay. And then you said somewhere out here there's gonna be a call to that?

ROBBIE: Yeah. Everything is in the app folder. And templates, application, HPS. You'll want to remove that welcome page there.

JASON: And so, if I do something like... something like that, I come back out here. Now we're controlling the page.

ROBBIE: Yep.

JASON: Cool.

ROBBIE: Yeah. So, yeah. For routes, there's a router TS down here. This is where we define all the routes. I'm just gonna -- brief intro of all the kind of structure first and then go from there. Yeah. You put your routes in here. There's a routes folder where you would put your JavaScript -- or TypeScript, I guess, in this case. Your backing classes. Everything is class-based in Ember. and then you've got components are kind of your main building block and controllers are like -- I don't know, I feel like this is kind of maybe a weird setup for people that are used to a React or Next.js kind of thing. Where a route has both a route file and a controller. And the controller is kind of more like a component, and the route one is more like for logic as the route is balloting up. So, like you could have a model hook. That's kind of one of the big things in Ember. Which I guess we could do that just to show how that works real quick. If you want to make a new route file. Actually, I'm getting ahead of myself, sorry. Totally prepared here. You can go into the -- your console. Or not console. Terminal. And say: Ember g routes and then give it -- or route, singular. And whatever we want to call it. Like we could just do Hello World or something.

JASON: Okay. Any flags or anything?

ROBBIE: Nope. And so, that will generate your route.

JASON: G is short for generate, right?

ROBBIE: Yep. So, you could type out the whole word. And so, you'll get your route file, your template for that route. It adds it to the router and you get a test for it. Which is nice. So, you have all the things you would need and then you can fill them in. So, in this route file, like one of the big concepts like in the Hello World TS, made under the routes folder.

JASON: Sorry, I was just looking at all these --

ROBBIE: Yes.

JASON: Here's our Hello World under the routes folder.

ROBBIE: Yes. So, you can add a new method in this that called "Model."

JASON: New method inside here called model.

ROBBIE: Uh-huh. And you can return whatever you want here.

JASON: How does this work? I forget how this works. Is this right?

ROBBIE: Yeah, that's right.

JASON: Okay.

ROBBIE: Yeah. So, you could return like a string or an object or, you know, whatever kind of data you want to come through on to your route.

JASON: Okay. So, let's do something like this. Right? And we can do kind of a basic thing.

ROBBIE: Yep. And then you can go over to templates, Hello World. And so, an outlet there, that is all of the content from the parent. So, like if you had a nested route and you wanted to have like a header that was the same or like some things that were the same with your route route and then you wanted to change the content further down, you would use an outlet to like throw that content in there. We're not gonna need that for this test here. So, instead of outlet, we can keep the double curly brackets and say this.model.greeting.

JASON: Okay.

ROBBIE: And then you should see it rendering that now.

JASON: Yeah, yeah. Start it back up. And so, if I come out here and it's whatever I named the route is what we hit?

ROBBIE: Yep.

JASON: And look at that. And something interesting happened here. Because it pulled our application template in as well.

ROBBIE: Your application template is like, you know, in Astro you would have a layout or something for your common app Chrome. That's what you want to put in your application and wrap that around your outlet.

JASON: Got it. And so, the outlet is -- I think like outlet is the term that I've seen used in I think like Remix adopted it. I've seen it used in a few other programming languages. In HTML, things that are leaning on like HTML as the language of truth, like Astro, they use Slot.

ROBBIE: Right.

JASON: So, if you have seen a slot before, outlets and slots, I think Vue and Svelte use slots as well. This is the equivalent, right?

ROBBIE: Yeah, more or less. Yeah. And so, then like -- the difference -- the one caveat there is there's also a yield. Which is like the same as an outlet. But it's for components. So, like if you have a component and you want to pass in extra stuff that gets rendered when you render that component, like we can do an example here to show you. So, you might want to make a new tab in your terminal so we don't have to keep starting and stopping your server.

JASON: Gotcha. Let me do that.

ROBBIE: And we'll see if it -- sometimes it gets a little upset if you generate stuff and haven't restarted the server. We'll see how well this works. So, let's do Ember g component. Let's see... what do we want to do in a component? I don't know. I'm totally unprepared of ideas of things to build. Do you have any component ideas?

JASON: I feel like the standard for a metaframework is to build out a counter.

ROBBIE: Okay. We can build a counter.

JASON: So, let's build a counter. Do I need to do anything other than this?

ROBBIE: I don't think so.

JASON: Okay. I don't know if you said this, but I wasn't listening. I do like that it gets very -- I guessed, right? Because it was g route to make a route, and then we've got components, it was like g component to make a component. So, that I really like. Like it starts to -- it starts to feel kind of immediate like, all right, I know what I'm doing. I know where I'm going.

ROBBIE: Yeah.

JASON: So, now I've got my counter.

ROBBIE: It looks like it didn't make a backing class for it. So, you have the handle bars, but you don't have a type -- I'm guessing maybe the default is template-only. I don't typically use the generators. But I think the generators are nice and cool. So, I was like trying to lean on those. If you go back and try ember g component--help, I think it will give you all the things you could pass to it.

JASON: Okay. Let's maximize this so we can actually see what's going on. We have... dry run, verbose, pod, classic, dummy.

ROBBIE: Yeah, none of that -- it was towards the bottom.

JASON: Here's the components.

ROBBIE: Yeah.

JASON: We had --

ROBBIE: I think you want component-gc, gc is component class glimmer component. So, that's what we want I think.

JASON: Oh, cool. Okay. Is there a way to like undo what we did? Or if I just make a new one with the same?

ROBBIE: I'm not sure, actually. Let's try just ember--help? Maybe there is a destroy. Is there a destroy? I haven't used it in a while.

JASON: Let's see...

ROBBIE: Or maybe this is too much. We could just kind of blindly try Ember d--help and see if ember d is a thing.

JASON: Dang, there's so much --

ROBBIE: Oh, Ember destroy.

JASON: Oh, there we go. Ember destroy, and I just do a component. What am I doing? Why did I close that? I want to be back down here. We're gonna go ember destroy component counter.

ROBBIE: Yeah, there we go.

JASON: And it's gone. See, it's removed. And then we wanted to try again, but this time we're gonna do -gc.

ROBBIE: Yep.

JASON: Okay.

ROBBIE: Cool.

JASON: And this time we get counter, we get the counter test. All right. And then when I look in here, there's the backing class. And there's the template. Okay. Cool. All right.

ROBBIE: Yeah. So, that kind of worked. Just a little bit of back story into like this whole format. the new-new thing, which I didn't want us to get into today because it's kind of experimental still, there are single file components. So, it would be like .gts and you would have all of your template and backing class in one. So, there's a little bit of weirdness until that drops with this like do you want to generate both files or not? Like in my opinion, this should be the default, and if you don't want the backing class, you should just delete it. But anyway. So, yeah. So, in here we've got this interface which is your signature. The element is not necessarily the root element. It's whatever element you want to apply dot, dot attributes. We call that splattributes. So, you put that in your template, and any that you pass down, you want to give it a class or an ID or any HTML attribute, it will get passed down to that element that you've put this on. This defaults to null in case you don't have any elements. But if we want to go into our counter HPS... we could give it -- we could do just like a div. And give it dot, dot, dot attributes.

JASON: And with the -- sorry. Where do I put that?

ROBBIE: Anywhere. I'm unsure if we're gonna need the yield. I know we were talking about playing with them. But like if the counter -- I assume when we're doing a counter, we're wanting it to just have a plus and minus and increment a number, right?

JASON: We could go even simpler than that, and have a button when you click it a number goes up. I don't think we need controls.

ROBBIE: Yeah. So, then for this I would do, yeah, just a div. And --

JASON: Now, is the attributes in the div? Like as --

ROBBIE: Yeah. You don't need the curly brackets.

JASON: That was what I thought and then I was confused. And then I could do like a button. Oh, I don't need the curlies. I understand.

ROBBIE: Yeah.

JASON: Okay. And then we can do like a count and we want to make this live. But for now... I'll be happy to just get something up on the screen. Should we wait to talk about this? Or?

ROBBIE: Yeah, I would just get rid of the yield for now. We can come back to it potentially. But let's stick on the counter train and finish that up.

JASON: Okay.

ROBBIE: So, now that we've got dot, dot, dot attributes, if you go back to the counter ts file. You can -- you'll have to change that element to be like HTML div element. So, that it knows what the type of the element is.

JASON: Got it. And this is TypeScript, right?

ROBBIE: Yeah.

JASON: Just anybody that's maybe confused. This is TypeScript, this isn't an Ember thing. We're using the HTML type and just make sure that the -- the component here knows like how to autocomplete. Right?

ROBBIE: Yeah. Yeah. The main thing that this is good for is the args here. So, you can give it all the types of the args that you're expected to pass in and then you know like you won't know right now because we didn't do this as part of the like basic installation, but there's a thing called Glint which does types for the templates. So, then you can install that extension in VSCode. You can hover and be like, oh, the type of this arg was a string. And you know the specific union of strings or whatever.

JASON: Got it.

ROBBIE: It's super-useful to know that everything you want is what you expect.

JASON: Super useful. Okay. When we get in here, I'm gonna make an assumption that somewhere in here is where we're gonna add our counter, or like our current count.

ROBBIE: Yep. So, in Ember, it's kind of like a signals-y implementation. So, it's like stuff isn't auto-tracked unless you tell it to be tracked. So, like nothing will update unless you've told it should. So, kind of the opposite of like a React Hooks world. More like a SolidJS or I don't know. Signals in general if you have used Signals anywhere. So, the specific thing that Ember uses is called Tracked. And so, you have to import at tracked -- or import tracked and then use it as a decorator with that tracked.

JASON: Where is that?

ROBBIE: It's from @glimmer/tracking. There's a few extensions for VSCode where you can type E colon and gives things. TypeScript is sometimes okay with finding them. But I have mixed results with TypeScript.

JASON: Got it.

ROBBIE: Okay. Yeah. You could say tracked, current count or whatever we want to call it.

JASON: Is it just like... like that?

ROBBIE: Yep.

JASON: Okay. I'm gonna be completely honest, this -- this style of like class-based JavaScript has continued to move and I have paid zero attention to. So, I'm like so at C when we get into these -- like what is this?

ROBBIE: So, that's a decorator. Yeah, decorators have changed a lot. I'm not sure if it's actually fully shipped as like this is exactly what it's gonna be like thing. But with the mage of Babel, you can use them. And Ember is kind of on the legacy decorators syntax, which so is Angular, I think.

JASON: Yeah.

ROBBIE: So, we're all kind of stuck on this format. So, I don't know why the like format that's actually getting into the spec is not this format. When we're like the only ones using it. But anyway, a decorator is a way to like add extra functionality to a thing. So, like behind the scenes some functions run on that property that do magic. I don't know how to implement a decorator. I just know how to use them. So, I don't know like what that exactly looks like. But this is basically telling some magic behind the scenes, hey, this property is gonna change. So, whenever it does change, swap that -- oh, okay. NullVox is on here. He's gonna make me look stupid. He's the one that writes most of this stuff. Anyway. Yeah. So, this will tell it it will update it when you update so that when you're using in a template, it knows, okay, I should re-render that little part using that little available.

JASON: Awesome. Okay. So, this is -- this is basically the Ember equivalent of doing a create signal in Solid or a use state in React or a -- whatever. Like whatever -- insert your state management solution.

ROBBIE: Yep.

JASON: If you want it to update when the value changes, you do it like this in Ember. Okay.

ROBBIE: Yep.

JASON: So, now that we have this value, if I want to update it, do I have to use -- is there like a getter setter thing? Is there a specific convention or can I say whatever the hell I want and make it into a method?

ROBBIE: Whatever you want. And there's like no magic with this where you have to do like dot current or dot value -- it's not like a mysterious thing that you have to find the guts of it to set a value. You're just like, current count, yeah, plus -- yeah, like that.

JASON: That works. Okay. See? Look everyone, I remembered that in a class you have to use this-dot. Professional. So, then, if I want to actually use this, do I have to do anything in here? Or can I come out here and start adding stuff?

ROBBIE: Yeah. You come in here and the next thing we'll touch on is called modifiers. So, modifiers are little things you can put on your elements and call functions and stuff. So, specifically here, we're gonna be using the onModifier, which is going to set up like an onClick. We can say element basically.

JASON: Okay. So, let's dive into here and I wanted an onClick. And what's the syntax for that?

ROBBIE: So you do curly brackets, double curly brackets. And then just say just the word "On." And then space, quote, click. So, on is the modifier, click is what we're saying the event is, we're passing it in. And then you give it whatever function you want to call. So, this would be this.increment. Not in quotes.

JASON: Not in quotes. Okay. Okay. And so that will increment the count, but we're not displaying the count yet. So, is it like this.count here?

ROBBIE: I think we called it current count, but yeah.

JASON: Oh, you're right. And is it in double curlies like that?

ROBBIE: Yes, that's correct.

JASON: So, theoretically speaking with the way that we have structured this, when I bring up --

ROBBIE: We haven't dropped the component on the page yet. We do need to do that.

JASON: Of course. Why would I -- all right. So, now I need the counter. And for me to do that, I would expect that it works something like that and I just need to import that or is there more to it?

ROBBIE: It works like that. And you don't need to import it currently. In the new GTS world, you do need to import it, unlocks nice tree shaking and stuff. But for now, Ember is just magic and finds the component.

JASON: Here or did I forget something?

ROBBIE: I have an idea why it might not be working. Yeah. This context is not bound. So, if you go back to your component, counter.ts. We're gonna need to import an action decorator. And that's in @ember/object is where it's imported from.

JASON: Action from emmer object?

ROBBIE: Yeah, ember/object.

JASON: Like that? That's not right.

ROBBIE: Yeah, @ember/object. Yep.

JASON: Why are you yelling at me? Just because we haven't --

ROBBIE: Because there's no semicolon maybe?

JASON: And then do I just like this?

ROBBIE: Yeah, the reason it's called action, Ember used to have this thing called actions and you give it a string of the function name and it would look it up on an actions hash and call it.

JASON: Okay.

ROBBIE: That's just kind of a leftover from that nomenclature. It's binding this to this. This should work if you save it, I think.

JASON: So, trivia: With a decorator, I have seen them done like this. But you did it like this. I assume it doesn't matter.

ROBBIE: It does not matter. I tend to like to for tracked properties keep them all on one line like that. And then for functions, put it above the function. Especially because my function might get pretty long if I have a lot of params and giving them types and whatever. If I put the decorator really long, I put it on the line before it.

JASON: Look at us go. And NullVox said you could use an arrow. What does that mean?

ROBBIE: Yeah, you can use an arrow function, like the fat arrow. Bind this context --

JASON: oh, if I did it like this instead, then we month need the action and it would just work?

ROBBIE: Correct, yes. Oh, look at that.

JASON: Okay. Options then. You can do it several different ways. I kind of like not having to import things. That's nice.

ROBBIE: Yeah, NullVox has a lot of new nice things to do everything. I tend to be more old school. But, yes.

JASON: I like it. I like it. This is great. I mean, I love that. So, okay. So, we have -- we have a pretty clear syntax here for we want a value that gets tracked. We have a way to change that value. We don't have to do anything to get Ember to update whenever that value changes. It just works. What else -- what should we dig into next?

ROBBIE: Oh, I don't know. I should have had an idea of something to build. NullVox, what should we build? Yeah. I don't know. I think passing in some sort of arg is helpful to play with. Maybe doing some more with model hooks to like actual load data from somewhere.

JASON: Yeah.

ROBBIE: I just don't know -- I don't know what to build though.

JASON: You had mentioned backend stuff, though. We have, if we need it -- let's see... an adapter for xyflow. So, if we need it, I have my API here. And we can pull in like episodes of Learn with Jason.

ROBBIE: Okay.

JASON: And so, this would give us the ability to like, we could render some pages, we could make a list. We can show -- we can at least show how to do like an async data fetching call. And this will give us a decent amount of stuff that we can mess with. Like we could also make a component to display an episode and then pass all of this data in from a parent component from a page.

ROBBIE: Yeah. I think that sounds great. List of episodes, yeah.

JASON: Okay. Let me see if I can do this. So, I out here, I say Ember g route and we'll say episodes. Do I need any flags?

ROBBIE: No.

JASON: Okay. So, I'm gonna create a route. And then we're going to also create a component and that component is gonna be called episode. And we're gonna do the -- do I need the gc for this one? Well, we could argue this, right? So, yes.

ROBBIE: Well, yeah. Yes. In the current world, yes. You need it just to define your TypeScript stuff. There's a special template-only component you can import. I don't really know how much performance gains you get from not having that backing class, quote, unquote. I tend to just have them anyway.

JASON: Okay. So, then in here, let's maximize this again. If I want to see... so, I guess, so, the to dos are one, we need to load the data.

ROBBIE: Uh-huh.

JASON: Two, we need to iterate over that. And put the episode data into our episode component.

ROBBIE: Uh-huh.

JASON: And then so I guess that means that like 1.5 is to create an episode component that can accept arguments.

ROBBIE: Uh-huh. Yep.

JASON: Okay.

ROBBIE: Yeah. And we can either just use like fetch and, you know, throw data around, or we can try to go Ember data style, I'm not sure if we have enough time for that. But you could make like an episode model with all the fields an episode might have and then like throw that thing around.

JASON: What if we do a simplified model? Like we can just get the title and the poster?

ROBBIE: Okay. Yeah. So, I think you can --

JASON: That we can show the Ember data model, but don't get way into the weeds.

ROBBIE: Sure. You can Ember g model as well, I think. So, yeah, model episode, I guess.

JASON: So, yeah. What's the convention here. Is it singular or plural?

ROBBIE: That's a great question. I think it's singular. It does a lot of like magic pluralization and like de-kabafying words. You can basically throw lots of random stuff at it and it just works.

JASON: De-kabafying, I like it. All right. I've got the model here. The data fields that we want, like are we doing a method called generate schema or just throw the fields on that you want?

ROBBIE: You throw them on. Everything is decorator-based. So, adder is the decorator.

JASON: Like that?

ROBBIE: A-t-t-r. Like attribute.

JASON: Oh, attribute. Got it.

ROBBIE: And then you do -- you do parentheses, and you can pass different types to it. This is kind of normalizing data. If you give it a string, all of the types are strings. And it's like either string, Boolean, date, I don't know, it's like, you know -- there they are. Okay.

JASON: Let them go.

ROBBIE: Yeah. So, that just like -- if you're not sure like a thing from the backend might be a string, but you actually want it to be a number when you're using it, you can give it a number and it will like, you know, massage that for you before you use it. That's what this is for.

JASON: Got it, okay. So, that gives us our title and then we've got a poster URL. Feels pretty good. So, then what happens?

ROBBIE: Yeah. So, then you need to set up a couple of things. You need like an application. I think adapter is the one we want?

JASON: Okay.

ROBBIE: I'm gonna have to figure out how we do that.

JASON: If you want to drop that link in here, we can -- we can poke at that and also show that cool feature. Watch. No hands from me.

ROBBIE: Yeah. I don't have the exact link, per se. But I'm gonna give you the Ember data intro page and just to see how this pasting links thing works.

JASON: See how long I have to stand here before I get -- look! Did you see that? That's dope. I love that. So, we have our model. We get our model here. We get our -- we've done this part, got our attributes. Oh, this is cool. So, you can do actual relationships like that?

ROBBIE: Uh-huh. And they're by default, for better or worse, they are asynchronous. So, you can just, you know, call whatever this thing is here. Order.line items in your template and it will like magic find them and then when they're resolved, it will let you use them in your template. So, you don't have to await everything. Which is nice from a like hand wavy, you get to kind of just build stuff perspective. But it's also annoying when you don't know what's happening. So, it's like the pros and cons of magic.

JASON: Right, right, right. I mean, this is super-slick, actually. So, I like this. So, you said we needed an adapter.

ROBBIE: I think the adapter is what we want. And that... basically lets you put in like your host and your like -- I'm forgetting all the words. Like defining where your API is, what it looks like kind of stuff. So, then when you say like find episodes, it's like, oh, I know to go to Learn with Jason/episodes or whatever.

JASON: Right. Okay. So, we've got this bit. Let's find... where is our adapter?

ROBBIE: Yeah, where is adapter?

JASON: Not in here. Finding records.

ROBBIE: I also wonder what it will give us from the generator. Like if we do Ember g adapter application. Just to see what we get.

JASON: Yeah. I like running unknown CLI commands. Adapter -- what was it called?

ROBBIE: Application. So, anything that you want to apply across the board is called "Application." Like that root application template. You can have application model, or not model. Route. Like route file. Things that are application-wide. Anyway.

JASON: Okay. So, we've got --

ROBBIE: Oh, not super-helpful. Okay. So, by default it does default to JSON API as the format. I'm assuming that's not what you're using.

JASON: Sorry, it does what?

ROBBIE: It defaults to JSON API as the format. It's importing this -- I'm not on the clicky boy. Here we go. All right. I was looking at Streamyard trying to highlight stuff and I was like, this isn't working. Yeah. So, it imports the JSON API adapter. You actually don't even need this if your backend is fully JSON API-compliant, which most peoples aren't. There's always like edge cases. So, this kind of massages things for you. Assuming yours is not at all, there's a REST adapter and like a general adapter stuff as well.

JASON: Yeah, mine is definitely... we'll call it bespoke.

ROBBIE: Okay. I'm assuming the REST adapter is probably what we want then.

JASON: Okay.

ROBBIE: And I think it's where you would expect. Like adapter/REST. I don't know. Let's see if that happens to be right? Yeah.

JASON: Like that?

ROBBIE: Yep. Start there.

JASON: Okay.

ROBBIE: And then how smart is autocomplete here? Like if we type "Host," is it like -- I know what host is. I think.

JASON: Wait. What is -- it says host.

ROBBIE: String.

JASON: Target other hosts by setting the host property.

ROBBIE: Helpful. [ Laughter ] Oh. But I think this is --

JASON: Oh, but it is a string. So, I do something like this? Like is it?

ROBBIE: Yeah, I think this is like your root URL essentially. Like --

JASON: We're just gonna do this.

ROBBIE: Yeah. So, I think it's just --

JASON: We're not gonna get fancy.

ROBBIE: Well, it may or may not work with that. But like I think what you want is just Learn with Jason. And then you're probably gonna need a dot com, I'm guessing.

JASON: Oh, right, dot Dev, so, just this.

ROBBIE: Okay. And then you want to say namespace is the next thing. That will be like API v2.

JASON: Okay.

ROBBIE: And then you don't need episodes, because that's inferred from the model name. So, it will pluralize that singular episode and find episodes as the endpoint when you go find all episodes.

JASON: Okay. So, we've got this. And we go back into our model. Are we done in here? Or do we need to do more?

ROBBIE: I think we're done. We might have to come back. We'll start with this. And I think the model is more or less good to go.

JASON: Oh, for real?

ROBBIE: And then we go into whatever route you want to load episodes on. We need a route file.

JASON: Episodes, route.

ROBBIE: So, in your model hook -- no, before we get into the model hook, there's another concept. We're just throwing all the concepts at you. But there's services in Ember.

JASON: Okay.

ROBBIE: And you need to inject them. So, we'll have to import inject from I think it's like @ember/service, maybe?

JASON: Autocomplete to the rescue, okay.

ROBBIE: I usually do inject as service. That's like the convention in Ember for whatever reason. Like changing the -- sorry -- in the import, changing the name to service. Like inject as service.

JASON: Oh, oh, oh. I understand. Okay.

ROBBIE: And then you'll do @service and store. And that's our Ember data store. So, that's all you need for that.

JASON: Is that coming from something?

ROBBIE: It comes from Ember data. I think Ember data is still installed by default. But we should probably check before we assume it's there. So, let's look in package JSON --

JASON: Implicitly has an any type is what it's mad about.

ROBBIE: You have to give it all the types. You can do that while we're here. If you want to say declare store to say this is coming magically from somewhere.

JASON: Here?

ROBBIE: No. In front of store. Like -- sorry, I'm looking at the wrong thing again. I keep pulling up this because I want to see your face. There we go. Putting declare here in front of store. Just kind of I think that tells TypeScript like this is coming magically from somewhere. And then you give it a colon and we're gonna give it the type is just capital store. And it comes from -- if it doesn't auto-find where that is, it's from @ember/data -- or Ember/data/store, I think. See if that...

JASON: Okay. Import. And that's a type, so, we're gonna say --

ROBBIE: Import type.

JASON: Store from Ember/data?

ROBBIE: I think it's Ember-data/store.

JASON: Oh.

ROBBIE: I'm not positive.

JASON: There it is.

ROBBIE: We also have to make sure we have installed Ember data. That could be why it doesn't know...

JASON: Has no exported member. Oh. Is it top level?

ROBBIE: Yes. Because we're already at slash store.

JASON: Got it.

ROBBIE: Does it give you something if you look at that now? Let's see. Yeah, cool.

JASON: It says the store contains all the data for the records. Something is happening.

ROBBIE: Then you'll need a model hook. Just model method again.

JASON: Wait. I've completely forgotten. Did we do this already?

ROBBIE: Yeah. We did it just to show the string. We had a greeting string that we are accessing from a template.

JASON: 1,000% that has exited my memory. We'll look in here. Oh, oh, oh. I understand. Okay. Yes.

ROBBIE: Yeah.

JASON: So, this, and then we're gonna return something.

ROBBIE: Yeah, you want to return this.store.if we want do get all the episodes, you do find all episode. Or it might be plural. I'm not sure. We'll try this.

JASON: No -- I mean, the autocomplete gave me episode. So, we're gonna roll on that.

ROBBIE: Nice, nice. TypeScript is really helpful.

JASON: Okay. So, okay. So, there's a lot of magic happening.

ROBBIE: Yeah.

JASON: Which takes me a second to get my head around. This is actually pretty sick. So, we have our model. And we're saying that our episodes are gonna return this. And I'm gonna make an assumption that under-the-hood you will dump the response from the API and you'll be looking for things that match?

ROBBIE: Uh-huh, yep.

JASON: I'm gonna change this because this should be slug, then. And then we have our adapter. Our adapter is saying that whenever you get a request, send it here. And then you know how to build the URL based on like what the model is that I'm saying.

ROBBIE: Yep.

JASON: And because it's the application adapter, it's used for everything. We could create sub-adapters, I assume.

ROBBIE: Yes. You could do an adapter per model if you wanted.

JASON: Got it. This is connected here and then we get into our route. And our route is saying we want to use the store and then we want to get the result of find all episode which connects to that adapter and puts it into the store.

ROBBIE: Yep.

JASON: Sick. Okay. So, then to actually use this, we have our episodes route. And we have our episodes template. So, at this point, I don't think we need the outlet, right?

ROBBIE: Nope.

JASON: So, instead what we want to do is loop over our episodes which I'm assuming -- I don't remember how handle bars work. But it's gonna be like four each, you know, episode and episodes, or episodes.map or something like that.

ROBBIE: Yep.

JASON: And then we want to call our episode component which is up here that we have not declared yet. Because we need to get the arguments in.

ROBBIE: Right. Yeah. So, to start we could --

JASON: Should we do that first?

ROBBIE: We should probably spit out -- was title one of the fields we put on the model?

JASON: Yep.

ROBBIE: We could show episode.title to show that it's working. To do an each, you do curly brackets. I never know the name -- pound, I guess? I always want to call it hashtag, but it's not like -- what do people call that? Anyway. Each. This.model, space as and then I don't know what to call these either. Like the pipe -- like yeah. Episode. So, you're saying like give me this big list of episodes we found. Call each one an episode and then a closing pipe.

JASON: Okay.

ROBBIE: And then you'll need a closing each after the... yep.

JASON: Like that?

ROBBIE: Yeah. And then inside of there, you could just do more curly brackets like this.model.-- or yeah, this dot episode. You got it right. You're good. I'm just saying it wrong. But this looks correct, yeah.

JASON: Okay. So, this is in our template. So, I shouldn't have to... or let's see. We've got this one. Let me...

ROBBIE: Yeah. You should be able to navigate --

JASON: Here before I forget about it. And then I'm going to open up here. We're gonna navigate to /episodes. And is my API slow or did it break? Response must be normalized to a valid JSON API document. Oh, no! Okay. So, we've got chaos.

ROBBIE: Oh, I know why.

JASON: That I'm providing.

ROBBIE: I know why. You also need a default application serializer. Because if you don't provide one, it thinks it's all JSON API.

JASON: Okay.

ROBBIE: We need to do Ember g serializer application. And then whenever that comes up, you want to do the same thing we did before, change it from JSON API to REST.

JASON: Okay. Yep.

ROBBIE: Okay. And then I don't think he need to do anything in there. It might just work.

JASON: Might just work. It's not yelling anymore.

ROBBIE: There we go.

JASON: Encountered -- but no model was found --

ROBBIE: Okay.

JASON: For the model name 385. Oh, okay. So, this is me --

ROBBIE: So, we may need some extra serializer magic.

JASON: Yeah, because I think it might be dumping like indices.

ROBBIE: Yeah, can you see what API calls we're making in the network? Like what is the format -- first, are we hitting the right place? And then if we are, let's see what we can change in the serializer.

JASON: Let's see... we've got... episodes is hitting -- there's our fetch call. So, this gives us back an array -- is it breaking that array into three chunks?

ROBBIE: I don't think --

JASON: That's just the console, I think?

ROBBIE: Yeah, I think so. Maybe REST is actually too much. Like I think there's just a generic serializer that is maybe what we want.

JASON: Okay. So, out here. Nope. Where is our serializer?

ROBBIE: Oh, there's a JSON one.

JASON: Should we just try the JSON?

ROBBIE: Sure, we'll just try everything. I haven't worked on a new application in so long, I couldn't tell you how to configure all this. We'll figure it out, though.

JASON: Okay, so, happening --

ROBBIE: Is NullVox is still there and knows the answer you can tell us real quick.

JASON: Did it hit my endpoint? There's the call. Okay.

ROBBIE: Seems to have fetched it.

JASON: Hey! That's right.

ROBBIE: There you go.

JASON: That's just my API is not always the fastest.

ROBBIE: We're also doing an unbounded, just give me all of them.

JASON: That's true, we're loading literally every episode and I think this call might return transcripts as well. It's a giant amount of data. But look! It works!

ROBBIE: Yeah.

JASON: So, we've got that. And now what I want to do in our remaining -- how much time we got? Oh, we got like 20 minutes. We're in great shape. So, let's get a component --

ROBBIE: Yeah.

JASON: -- running with our stuff here. So, we have -- let me just, you know what? We're gonna close all that. Open up this and I'm going to get our component episode. And so, our to do list here is we have a title and a slug. And then I can use the slug to build a poster URL so we need to --

ROBBIE: Okay.

JASON: -- accept those and then do a little bit of work in the component or in here. To transform that slug into a URL.

ROBBIE: Yeah. Yep. Sounds good.

JASON: Okay.

ROBBIE: So, yeah. In our args there, we'll have -- I guess we're just passing -- like this is kind of maybe more cheating than anything else. But you could just say: Episode and give it an episode model as the type? And then you don't have to do everything else.

JASON: If I can spell it right.

ROBBIE: Yeah, I'm assuming it's probably an export-defaulted episode. So, it maybe isn't finding the name. But if you import it from dot, dot, slash models. It should find it.

JASON: Models, episode.

ROBBIE: Oh, should be not -- not JavaScript. It's TS.

JASON: Just get a -- could not find a declaration file.

ROBBIE: Maybe because we have to like run a type check. And it hasn't. So, it hasn't generated those yet. I'm not sure.

JASON: Oh, you know what? I don't have this --

ROBBIE: Oh, it's not TypeScript. Well, that's why.

JASON: Should I call it TypeScript and then that will fix it?

ROBBIE: Yeah. You have to give it types, though. So, just give that a string as well.

JASON: Be mad about? No initializer and it is not definitely --

ROBBIE: Oh, just give them declares. Declares are used everywhere in Ember because everything is magically loaded from somewhere. No S.

JASON: Oh, right. Right. Okay.

ROBBIE: There we go.

JASON: Got it. Okay. So, now --

ROBBIE: So, then that should know what that is, hopefully.

JASON: -- you are grumpy because... that? That?

ROBBIE: Maybe try TS?

JASON: There we go. I just needed to kick it a little bit. But now we're in good shape. So, we've got our episode model, all right?

ROBBIE: Yep.

JASON: So, then where does one do the transformation? Like when I want to take the slug and turn it into a poster URL.

ROBBIE: Yeah. That's -- it depends. So, a lot of people like to do a lot of that directly in the template. I tend to prefer to keep my templates clean and I would make like a -- just a native getter in the class that would be like poster URL and build it there.

JASON: Okay. So, if I do something like this...

ROBBIE: So, I would do -- make it a getter. Like with get in front of it. And like a method. Not the arrow function. No, sorry. Like get space and then poster URL.

JASON: Is that like a class thing that I don't know?

ROBBIE: Yes. Sorry. I forget that nobody really uses classes. But yeah. Classes you can have a native getter. And so, this is basically like a property. So, if you access like this.posterURL, it will just, you know, return that as like a string or whatever you returned. You don't have to like call it as a function.

JASON: Got it.

ROBBIE: And so, this is useful for like composing tracked things. So, if you have like a bunch of tracked properties and you want to have -- this would have had to have been a computed property back in the day --

JASON: Right.

ROBBIE: -- because you needed everything to trigger computing. But now it's just a built in class thing.

JASON: That's dope.

ROBBIE: Yeah.

JASON: So, then I've got this gives me a posterURL which takes the slug and turns it into a thing. I auto-completed my way to victory there. And that's correct. That's the right way to do it?

ROBBIE: That looks right, yep.

JASON: So, then, if I open up my episode... I should then theoretically be able to do something like this and we'll take, I don't know, we'll do an image. Give it a source of was it this dot poster URL?

ROBBIE: I think so.

JASON: Okay. And we'll give it an alt of this.title. And let's give it a width of 300. And we'll let the height auto on that.

ROBBIE: I think your alt needs to be this dot -- or it's like from the episode, right? So --

JASON: You're right.

ROBBIE: So, this is something that is a little different here. Instead of whenever you pass something in and it's not a property on the class, you use at instead of this. So, we would say @ episode instead of title.

JASON: Like this?

ROBBIE: Yeah.

JASON: So, then we can just put in one of these. And @episode.title. And then theoretically, that works. So, then if I go back to my routes... no. Back to my templates -- here. Then we should be able to change this out to be episode and then I do episode equals episode? Or can I just drop it in straight up?

ROBBIE: Yeah it's like that. You put an @ in front of the first episode. Yep. Any time it's an arg, you do @. And if it's an attribute, you don't. So, that's like how you can differentiate class being a normal HTML thing versus stuff you're passing.

JASON: Look at us go.

ROBBIE: Nice.

JASON: Like this is exactly what we wanted. It does exactly what we're after. We get our list of episodes. We have our got our data coming in. And, you know, it takes a little bit of figuring out what the conventions are. But the actual mechanics of this are really straightforward. Like we have a way to -- let me just walk back through and make sure I understand everything that happened. So, we have a way to turn data -- like when data comes in -- we serialize it and because we have my chaos JSON instead of a spec, we use the generalized JSON serializer. We've got a adapter which tells Ember where to go look for data which in this case is my API. Then we have our routes down here. And here's our router which is saying like for these, you know, slash whatever hook up a route. Which is already -- okay. Great. Then we've got these routes themselves. And when you call this route, is going to call the episodes endpoint of my API because it's got pluralization, which is a little confusing, but very cool. And then, it also encourages me to have much better API hygiene than I do. Because I'm assuming I would actually get bit by this later when I try to find one episode and it doesn't --

ROBBIE: We could try that. We could make an episode route and then see if we...

JASON: How much pain do you want to feel?

ROBBIE: How do you differentiate? Slug-based? Or do they have IDs?

JASON: I think the way I do it, die API/v2/episodes, and then just throw in -- maybe I do it right, actually. So, it goes to singular. That probably would work, actually. And then you throw in a slug.

ROBBIE: Okay. I don't know if we can successfully do it, but I'm down to try. We have a little bit of time though.

JASON: Yeah. We were gonna go out on a wind. But let's make sure we grind across the finish line on our faces.

ROBBIE: Yeah.

JASON: All right. Let's roll, let's roll. Here we go.

ROBBIE: All right. Yeah. So, I think maybe what I would do is Ember g route episode/episode? Episodes plural slash episodes. So, you're looking at the list of episodes and then you want to drill down is kind of what I'm thinking.

JASON: Okay, okay.

ROBBIE: And then --

JASON: End up with episodes/episode.

ROBBIE: And then in our router, you'll have to give it -- like tell it what that ID is gonna be. So, it will need to have -- so, you put a comma here after episode -- no you don't, I'm wrong. You put your ID here. So, it would be like... instead of episode, I'm thinking the URL will be like episode/slug. So, this will be, yeah, like colon episode -- or slug.

JASON: Like that?

ROBBIE: Well, I'm thinking that it would be just episodes/slug without the word episode again. So, I would make it just colon slug, I think. Yeah.

JASON: Okay.

ROBBIE: And I'm -- yeah. No, all right. So, hold on. You were right. I think this has to be episodes/slug -- I don't know. I need to look this up.

JASON: You know what? Why would we look at the docs, Robbie when we can just run it and see what happens.

ROBBIE: You're right. You're right.

JASON: Okay. So, we've got one of these. Maybe what we can try just to immediately get us out the gate is let's just have it show the current slug. And that will let us know if our routing works. So, in here, I would want to show like what do I do? Do I just do -- can I show the route? Like?

ROBBIE: Uh... yes. So, it's not just -- you can't just do it. In this we'll need a controller for this page.

JASON: Okay.

ROBBIE: So, we'll have to do Ember g controller episodes/episode. And then inside that controller, we'll have to inject the router service. Which will be similar to how we did the store service. You'll import, inject as service, and then do @service router.

JASON: And service here is different from inject as service.

ROBBIE: Yes. I think they both come from service, but like if you're defining a service of your own, that's the one it's trying to autocomplete you too there.

JASON: Remind me what this was?

ROBBIE: I think it's slash service. But I may be wrong.

JASON: No, it just auto-completing the way I thought. Declare store --

ROBBIE: No, this one will be router.

JASON: Router.

ROBBIE: And I wouldn't worry about trying to find the type unless it just happens to have it. Oh, it does have if. Ember routing router. Okay.

JASON: Okay.

ROBBIE: Actually, that might not even be the type, but I think it will still work. If you go into the template -- I think it's a router service. Slightly different. I think they have the same properties. I'm not too worried about it.

JASON: Whatever, right? Who cares?

ROBBIE: So, if you go into the episode HBS -- we have so many things named episode. Where are we at? Templates/episode/episode. Yeah. And then do this.router.something that I will find... let's see.

JASON: Params maybe?

ROBBIE: That's possible. Is it auto-completing to that? Or does it -- no, it doesn't auto-completing. Because we don't have the Glint extension installed. Let me see... properties... current route, current route name, current URL. Hm. It might be... yeah. Current route -- yeah. This.router.currentRoute.params, I think.

JASON: Like that?

ROBBIE: Yes.

JASON: Okay. So, let's give this a shot. And I'm going to episodes/test-slug. And it says... episodes slug is not a valid route name. Cannot contain a --

ROBBIE: Let me look at --

JASON: May have -- option instead.

ROBBIE: Okay. That's probably what we meant, then. Maybe the name is still episode, and then you give it a path of just colon slug. So, yeah, I would make that just episode, singular. Yeah. And then comma. And then curly brackets object. And then you give it path.

JASON: Path.

ROBBIE: Yeah, let me try to -- I'm trying to find the actual docs because I'm not sure. But we can also just keep trying stuff, I guess.

JASON: Unrecognized.

ROBBIE: Oh. It should be just slug. No episode slash for the path.

JASON: Okay. So, that hits here. Does that need to be like episodes episode?

ROBBIE: No, because it's already nested under episodes.

JASON: Okay. So, it's -- now it's hitting... it's hitting like it's not hitting that at all.

ROBBIE: Yeah. It's possible this path needs to be episodes/slug? I'm not sure. I thought it didn't. No. Yeah. So, I think it's right. Let me try to find actual docs on this.

JASON: And I don't want to put undue pressure on you, but we have 3 minutes left.

ROBBIE: Okay. It's so hard to find the right docs because there's so many versions of Ember and it's so old.

JASON: Linda has a bet, and I'm gonna take up that bet. Here. And Linda is wrong.

ROBBIE: Yeah, maybe it's just slash? Maybe we just need /colon slug?

JASON: No, nope. That's not it.

ROBBIE: And why is it rendering all this other stuff?

JASON: It's like whatever we're doing is causing us to just ignore all the way back up.

ROBBIE: It shouldn't do that without resetting namespace. Yeah, we can be explicit about it. If you give it episode singular/slug and then go to -- I think you need the first slash. And then let's just put in episodes/episode/test slug just to make it happy.

JASON: Naw, it's like doing a full ignore.

ROBBIE: I think it's just rendering the wrong template. Do we have an outlet in something?

JASON: Do we have an outlet. That is a great question -- let us see. Template, episodes. There's no outlet in this one.

ROBBIE: It's rendering this because it's like, this is the upper content.

JASON: Oh, okay. So, let's --

ROBBIE: Yeah.

JASON: Let's just stick this up at the top.

ROBBIE: Yeah, for now.

JASON: It's working.

ROBBIE: It was working before.

JASON: Was it working before and we came to this, and then it does the thing? It does. It does. Okay.

ROBBIE: Yeah.

JASON: So, everything was fine. And we got -- okay. So, that actually brings up a question, then, which is how do you do the like -- the nesting like this without this?

ROBBIE: Yeah, we probably should not have nested it. That was probably my bad. We probably should have just called it like you can do -- if you go to the router, I know we're out of time. But if in the router instead of nesting them like this, you could say like bring that episode part out to be like a sibling of episodes and then make the path /episodes/slug. You know what I mean? So, it's not a child anymore, but the like -- the path to the user appears like a child. So, that's how --

JASON: Got it. We have to rearrange a lot of these files to make it work. We do not have time for. But that would work.

ROBBIE: Yeah.

JASON: Well, cool. So, yeah. I mean, now we've got like -- despite the fact that we did exactly -- exactly what I thought we would do which is try to squeeze a little bit too much into the last second and get ourselves into a corner. But we did get it working, which I'm very excited about.

ROBBIE: Yeah.

JASON: And this is, you know, I feel like it's pretty obvious that we barely scratched the surface here. But the fact that I already kind of have a sense of how things move through this and that there's a very clear structure for like your templates go here. Your components go here. Your models go here. And you know exactly where to look to find certain type was data. It -- I can see why this has such longevity. And I can see why big companies would reach for Ember. Because if you need to onboard somebody new, if they've ever worked with Ember before, they're not walking into something they have to learn. They're just --

ROBBIE: Yep.

JASON: -- stepping into code that it's familiar. So, Robbie, where should somebody go if they want to keep digging? Like what else is?

ROBBIE: Yeah, I would go through the actual tutorial that we didn't do. It has --

JASON: That we opened and then didn't do anything with.

ROBBIE: Yeah. I didn't know what all it would have. It's more fun to just fly by the seat of your pants. Yeah, I would have them go through this. Poke around the docs. Be aware that a lot of stuff changed throughout the years. So, if you're trying to Google stuff, it's probably not going to be the best.

JASON: Make sure you're in the right version of the documentation.

ROBBIE: Yes. The docs are good. There's a lot of new stuff come out in the next couple of months. Like Vite is supposed to be the default. We'll see if that lands as thing it will. But there's a lot of cool stuff happening to where like this will be even simpler, even more familiar to people that aren't familiar with Ember and are more used to like a single file component in Vue or whatever. There's new stuff coming with StarBeam and resources and things like that which are a little bit hooks-y, which I don't love. But a lot of people do love that. So, like, you know, it's definitely not as old and clunky. You shouldn't just forget about it. It's got a lot of powerful stuff that you can keep today. There's a few tutorials, rock and roll by -- balance, I forget his last name. But he keeps that pretty up to date.

JASON: Rock and roll with Ember --

ROBBIE: Yea, that's the one. So, yeah, that. And yeah -- I'm not -- I don't know, I haven't had to give resources to people in a while. Check out the Discord.

JASON: You know what? Here's what I'm going to do, I'm going to give everybody your Twitter so once they have run out of resources, they can find you and ask you for more information.

ROBBIE: Yes.

JASON: And in addition to that, this episode, like every episode, has been live captioned. We have had Amanda here with us from White Coat Captioning doing the live captioning. Thank you very much. That is made possible through the support of our sponsor, Netlify. Thank you very much, Netlify. And we've also been using tuple for the pair programming. You have seen Robbie drawing on the screen. And you have up in the top some celebratory stuff that you can do if you want to do maybe a quick reaction.

ROBBIE: Oh, okay.

JASON: Right? Just little fun stuff that we can do together as we -- where was that when we were --

ROBBIE: Yeah, I didn't know that was there. That's great.

JASON: So, anyways. So, tuple has been sponsoring the show and the pair programming on it. It's great. If you want to try it out, click the link down here and you can try it out yourself, you know? Let them know I sent you. Robbie, thank you so much for spending some time with us today. I really appreciate it. While you're on the site, everybody, make sure you go and check the schedule. We have all sorts of good stuff coming up. Next week, we are going to learn how to put 3D on the web. This is gonna be dope. If you haven't seen the stuff Sara is working on, really excited about. If can't wait to learn. And Ryan Carniato coming in, and learn about SolidStart, like Svelte, make your -- NUX. Then the makeup episode with Matt Pocock and generics. And even more coming that I haven't put up on the site yet. We got our dates wrong. We'll fix that. Don't worry, everybody -- this is all gonna be okay. So, Robbie, thank you so much for hanging out. Chat, as always, thank you so much for hanging out. We are gonna call this one a resounding success. And thank you all for hanging out. We will see you next time.

Learn With Jason is made possible by our sponsors: