 Welcome to Show and Tell with Ember Services. When I was a kid, my classmates and I would bring something from home to share with our classmates, a stuffed animal, a coin from another country, or maybe a favorite book. We could see little pieces of a larger world and how different people learn and grow. Today, we're going to do some Show and Tell of our own with focusing on Ember Services. My name is Jen Weber. My pronouns are she or her or they or them. I write for a living. Most of the time, I write code, JIRA tickets, and technical documentation at ACT Blue Technical Services, but I do also dream of writing books and stories. So for that reason, today, we're going to build a stateful, talking, selfie-taking story app. In 15 minutes, I'm going to show you some examples of three different kinds of services. You'll learn what a singleton pattern is and I'll share some of my own advice about when to use or not use services in order to solve a problem. So what's an Ember service? Services are a feature of Ember. We're not talking about microservice architecture or service workers here. We're just talking about Ember. The Ember guides tell us that a service is an instance of a class that lives for the duration of an application. It stores state and can be made available to different parts of your app. Said another way, services have reactivity and they are where application state lives. That was a bunch of words and we're going to break that down into its component parts over the next few slides so that you really understand what services are and what makes them special. A service is useful for features that require shared state or persistent connections. So things like user authentication state, push notifications through web sockets, or connecting to third-party APIs. I think one of the best ways to learn what a service is and how to use it is to actually look at some different examples since there's so many different ways that you can leverage service patterns. Here's our three services that we're covering today. One is going to be for sharing application state. It's going to be some form data that we type in and we don't save to a database that data just gets used in different places, different routes within our application. Number two is focused on code that should only run once during the lifetime of the application and it should only run when it's needed. So in this case, it's going to be a web camera permissions request. Number three, follow some patterns for streaming data. We'll make an API request to an unusual third-party endpoint and then do something with what comes back. And in this case, it's going to be a tool that turns text into spoken audio. All right, so let's start with some form data. I have this route called setup where my form lives and I'm going to type some stuff into it and use it later on within a different route. This is a common application need. We can think of this as our global application state. So let's go ahead and fill some stuff in. Let's start with form data. I have a route called setup where my form lives but I'm going to use this data later on within another route. This is a common need and we can think of this as our global application state. Let's go ahead and fill this form in and then these words are going to become part of our story later on. All right, so I filled in all my parts of speech. What does this code actually look like? Let's start with some form data. I have a route called setup where my form lives but I'm going to use this same data later on within another route. This is a common need. We can think of this as our global application state. So let's go ahead and fill in some of this data and these words are going to become part of our story later on. So what does this code actually look like for our service? This is my form data service and the values I'm entering on the form are going to be marked as tracked since we're going to be changing values and using them in the UI later. To use this service, I import the service decorator from at ember slash service and then I use at service form data in camel case. This gets the service for us and makes it available to the component. As of ember 4.1, you can just import service from at ember slash service but the syntax looks a little different in prior versions. Services are just one of the ways that state can get into a component. Some other ways include fetch requests, accessing a store like the ember data or passing data down through your component. Here's a sample of what it looks like to use some of our form service data inside of a component template. Next up is the photo booth. The photo booth involves some code that requests the user's permission to access the webcam and it stores the context for later when we might use it again. This uses only browser APIs. I'm going to turn on the camera, give it permission, take a selfie and go on to the next page. Finally, it's time to talk about our voice service called the narrator. Whenever I push play, the text should get read aloud. This feature is commonly referred to as text to speech and this technology has been around for a very long time although in recent years, computer generated voices sound even more realistic. So let's go ahead and press play for one of these examples. This is a computer generated narrator, awesome. If I was to navigate between different routes, this sample keeps going. This is a computer generated narrator, awesome. All right, so let's look at this camera service. In the service, my most complex code is actually outside of the service itself in a separate function. You don't need to read this great big chunk of code here. It's just here to show you just how much logic I'm actually keeping outside of my service. This function here fetches the audio contacts from the browser, which allows us to play audio. It's just not a method on the service, it's just in the context. Then my service itself acts as a public API that I can use to interact with the audio element that was created. Keeping this API surface simple makes it easier to write tests or stub, and it also means that when someone who is not me needs to edit this service, they don't have to read hundreds of lines of code in order to discover the logic that can change state. All right, so let's go ahead and use this service. We're looking at a component now that is making use of the narrator and you'll see that I've defined some actions here on my component that then call service methods. I could just use service methods directly in my handlebars template, but I prefer not to. And the reason is that my services job is to manage audio while my components job is to manage display logic and respond to user input. This rule of thumb helps me keep my code more organized following separation of concerns. All right, so now we're approaching this moment where all the pieces could come together, but before the grand reveal, we're gonna review some architectural patterns that you just saw and I'll go over some of the rules of thumb that I was following. Over the next couple of slides, I'm gonna share some computer science type terminology and it's not important for you to know what these words or their definitions are in order to get work done, but it is helpful for communicating these concepts with other people. So we're gonna start with the concept of a singleton. Services are an example of singletons. Only one service exists for the lifetime of the application and a singleton is a more generic computer science term that describes this behavior. Services follow lazy instantiation patterns. This little sloth is not gonna do work until she absolutely needs to. So similarly, the moment when the service starts to do work is the first time it is used when it's constructor runs. So why are singletons so helpful? Well, following these patterns, you can help prevent race conditions like two narrations playing at the same time. And when you use Ember services, you don't have to do the work yourself to ensure the only one instance exists. You get this assurance for free. From an architectural perspective, here's how a service fits into the overall flow of your app. Overall, in the Ember ecosystem, lots of different things have lifetimes, a time when they get created and destroyed. This setup and tear down process is what makes using a service superior to just keeping some data in a constant. The timeline starts when we inject the service into components using the app service decorator. And at this point, the service does not exist yet. As soon as the service is used for the first time, the services constructor runs and now the service exists. Using the service could mean rendering something from it in a handlebars template or calling it from an action within a component. And then if we use the same service across multiple components, those components all share the same instance of the service. There's just one and it sticks around even when the components using it have been destroyed. So when should you use a service? These are just some of my own personal experiences with when services are helpful, but there are many more. One option is to make application state available across deeply nested components or between routes. Another reason is whenever one having one instance of something is a good thing. Thirdly, if you need lazy behavior, services are great. And then lastly, if you need long lived behavior when the service is only torn down, when the app is torn down, services are gonna help you out. When should you not use a service? You shouldn't use a service if you only need something in one place. You should use component instead. You shouldn't use the service when you wanna share your code with non-EMBER apps. You might wanna make an NPM package or a separate JavaScript module and then reference it within your own service. Don't use a service if you want eager behavior and then don't use a service if you want multiple instances of something. The exception is that you can use services as a factory but I didn't cover that in this presentation because it's kind of an advanced thing and I've personally never needed to do it but it is a use case that exists. All right, so what are some of the best practices that I've found for my work? Firstly, UX always comes first. Consider the experience that you want to deliver and then let that guide your architecture. Separate pure functions into their own part of the code and use your service for managing state. Establish a public API. If there's a method or a property on your service that shouldn't be used from outside of the service, you can put a little hash in front of that function and protect it and mark it private. Don't duplicate state. If you have methods in your component that set multiple bullions to true or false, that is a code smell. Really try hard to use tracked properties and derive your state instead of keeping lots of bullions. I also recommend using primitives to store your data if as long as that serves your needs. So by that I mean save bullions, numbers, strings and only reach for objects and arrays when you actually need them. I recommend being careful with complex state. You need to make sure that your app doesn't, you need to make sure that your users can't get into a state that you aren't able to replicate when you go to debug. Give your services one job. If you need to, you can create services that use other services, but just make sure you don't create a cycle or say it's another way, a circle of dependencies where all of your services depend on one another, that would be a bad time. Consider your testing story whenever you are working in services. So keep your API simple to help with stubbing and testing. And then lastly, don't DDOS yourself. If you're dealing with streaming data, make sure that you're hitting a non-production instance. You don't want to be hit with a surprise outage or a big bill. All right, so let's get to our stateful talking, selfie taking children's story app. This story is inspired by a real life family dog who's pretty chill, but gets extremely excited whenever he hears a new sound. I present to you, Murphy hears a new noise. Once upon a time, there was a dog named Murphy. He loved to go everywhere with his best friend, Jen Weber. Everywhere they went together, the world was full of sights and sounds. On the morning walk was the room of the Toyota Prius. On TV after school, Murphy heard a song about Ria the cats. At home, whenever Murphy heard a rattle, he ran right up to the window and barked. Murphy liked to make his own noises too. Murphy especially loved new sounds. At the park was a musician with funny instruments that went lalalala and choo-choo. At the festival, a performer did an amazing trick. They announced to the crowd that they would now eat a piano. The crowd oo'd an odd. Murphy was full of zoomies. He ran around while wagging his tail, careful not to pull the leash held by his friend, Jen Weber. As much as Murphy loves new noises, there's one thing he wants to hear the most. I love you. So, this demo used a paid text-to-speech API because I wanted to make some points about data fetching and streaming. And if you thought that was cool, you'll be so excited to know that there are free tools that do text-to-speech available for all major operating systems. They're called screen readers and they're how a lot of people browse the web for entertainment, learn new things, and do their jobs. Knowing how to test your app to work well with screen readers and other assistive technology is an essential skill for everyone who writes front-end code. If you want your apps to be accessible, you have to test for it. If you follow this link, it will take you to one of my favorite videos for getting started learning how to use screen readers. My name is Jen Weber. You can reach me on GitHub as Jen Weber or J-W-W-W-Weber on Twitter. Thank you so much for listening. Enjoy EmberConf.