selecting Good Ember Patterns
This is a modal window.
The media could not be loaded, either because the server or network failed or because the format is not supported.
Formal Metadata
Title |
| |
Title of Series | ||
Number of Parts | 37 | |
Author | ||
License | CC Attribution - ShareAlike 3.0 Unported: You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this | |
Identifiers | 10.5446/34699 (DOI) | |
Publisher | ||
Release Date | ||
Language |
Content Metadata
Subject Area | ||
Genre | ||
Abstract |
|
EmberConf 201626 / 37
4
5
7
10
11
13
14
15
16
17
20
23
24
25
26
27
29
30
31
32
00:00
CodeVideoconferencingSelectivity (electronic)CodeSlide ruleLink (knot theory)Kernel (computing)Coma BerenicesNumberMobile appElement (mathematics)Front and back endsXMLComputer animationLecture/Conference
00:55
Computer configurationNumberElement (mathematics)Projective planeExistenceSelectivity (electronic)QuicksortView (database)Task (computing)Overhead (computing)Multiplication signSoftware developerComputer animation
01:40
Keyboard shortcutContent (media)Digital filterCategory of beingComputer configurationElement (mathematics)Axiom of choiceCodeGroup actionData miningElectronic mailing listMedical imagingElement (mathematics)Extension (kinesiology)Software developerMereologyDefault (computer science)AreaCore dumpConnectivity (graph theory)Bound stateComputer configurationSelectivity (electronic)CASE <Informatik>InformationMathematicsProjective planeVapor barrierMatching (graph theory)BitState of matterExterior algebraInverse elementOnline chatReal numberStack (abstract data type)QuicksortBuffer overflowAttribute grammarKeyboard shortcutComputer animation
04:21
Group actionComputer configurationFormal languageGame controllerPattern languageCodeFormal languageGroup actionEndliche ModelltheorieComputer configurationElement (mathematics)Electronic mailing listObject (grammar)Loop (music)BuildingSelectivity (electronic)XMLComputer animation
05:35
Group actionCodeComputer configurationFormal languageGroup actionDataflowDelay differential equationMereologyConnectivity (graph theory)Initial value problemFormal languageElement (mathematics)Form (programming)Query languageIterationOcean currentBit rateAttribute grammarKeyboard shortcutMultiplication signBoolean algebraPerformance appraisalPower (physics)CASE <Informatik>Parameter (computer programming)Computer configurationTemplate (C++)Mobile appWeb pageCodeSystem callMathematicsGame controllerCategory of beingLine (geometry)Event horizonSelectivity (electronic)State of matterLoop (music)Computer animation
09:38
Attribute grammarAerodynamicsBitTemplate (C++)LogicPower (physics)WordExistential quantificationAttribute grammarNumberKeyboard shortcutRevision controlGoodness of fitXMLUMLComputer animation
10:31
Computer configurationGroup actionAsynchronous Transfer ModeString (computer science)Default (computer science)Data modelEndliche ModelltheorieEquals signGroup actionElectronic mailing listDataflowFlagCategory of beingEndliche ModelltheorieGame controllerAttribute grammarQuicksortMathematicsSelectivity (electronic)Computer configurationBoolean algebraTwitterNeuroinformatikComputer animation
12:19
CodeBitArmConnectivity (graph theory)Template (C++)Computer animation
12:45
Euclidean vectorComponent-based software engineeringBitMobile appMultiplicationCodeConnectivity (graph theory)Medical imagingMultiplication signTouchscreenInternetworkingComputer animation
13:30
Group actionEuclidean vectorString (computer science)Goodness of fitConnectivity (graph theory)NP-hardGroup actionCategory of beingLevel (video gaming)Flow separationComputer fileInterior (topology)CodePattern languageMathematicsMultiplication signTrail
14:50
Component-based software engineeringGroup actionFlow separationConnectivity (graph theory)Level (video gaming)Group actionRevision controlProcess (computing)Algebraic closureDesign by contractUMLProgram flowchartSource code
15:10
Group actionAlgebraic closureEuclidean vectorAerodynamicsKey (cryptography)Correlation and dependenceRule of inferenceAlgebraic closureGroup actionComputer configurationConnectivity (graph theory)Design by contractDynamical systemInitial value problemQuicksortLine (geometry)Attribute grammarLoop (music)Functional (mathematics)Key (cryptography)Category of beingTemplate (C++)Message passingMultiplication signImage resolutionType theoryDifferent (Kate Ryan album)MultiplicationKeyboard shortcutSelectivity (electronic)Computer animation
18:56
Equals signEuclidean vectorIP addressProxy serverSelectivity (electronic)Connectivity (graph theory)Object (grammar)Template (C++)Group actionMereologyOverhead (computing)Food energyKey (cryptography)Computer animation
19:54
Keyboard shortcutSoftware frameworkState of matterMobile appLocal ringCASE <Informatik>WeightAxiom of choiceComputer animation
20:30
Group actionAlgebraic closureKeyboard shortcutGroup actionAlgebraic closure
20:53
Adventure gameKeyboard shortcutComputer configurationAlgebraic closureGroup actionDataflowConnectivity (graph theory)Group actionCodeMultiplication signFunctional (mathematics)Algebraic closureAdventure gameKeyboard shortcutMereologyObject (grammar)Power (physics)FreezingFile formatDataflowMathematicsFunction (mathematics)Closed setAttribute grammarDynamical systemParameter (computer programming)Selectivity (electronic)Fitness functionLine (geometry)
23:46
AbstractionCodeMobile appLevel (video gaming)Connectivity (graph theory)Selectivity (electronic)Computer animation
24:23
Computer configurationConnectivity (graph theory)Right angleAverageAbstractionLevel (video gaming)Selectivity (electronic)Mobile appFigurate numberJSONXMLUML
25:06
BlogComa BerenicesMultiplication signSoftware frameworkSelectivity (electronic)Level (video gaming)VolumenvisualisierungPoint (geometry)Mobile appCore dumpGroup actionLimit (category theory)Gotcha <Informatik>XML
26:19
VideoconferencingService (economics)Event horizonElement (mathematics)Mobile appSelectivity (electronic)BuildingComputer animationLecture/Conference
Transcript: English(auto-generated)
00:15
My name is Brenna, I am a front-end engineer for TED Talks where we write a lot of our internal apps and tools.
00:23
In Ember, we are looking for Ember devs, so come talk to me if you're interested in that. I have a feeling that some of the code might be hard to read so there's a link to my slides available. It's talks.brennaobrien.com slash ember-select if you wanted to follow along.
00:42
Okay, so hands up. Who's built an Ember app before? A lot of hands. Awesome. Who has used the select element in an Ember app before? Okay, now keep your hand up if you have been frustrated by the select element in Ember before. Right, a lot of hands, and I'm keeping mine way up high
01:03
because when I started out with Ember about a year ago, this was the bane of my Ember existence. It haunted me from project to project and consistently gave me a lot of troubles. So about a year ago, this is how you did a select element in Ember. It is a built-in view that
01:23
did a little something like this, and it did magic and made a select for you. So magic can be really good a lot of the times where it reduces the overhead that you as a developer need to go to to do common tasks, but I think we need to also beware of magic sometimes.
01:44
Basically, it's great when it does what you want, and then when it doesn't do what you want, it's extremely frustrating. So one of the first things that I found really difficult to deal with with the Ember select was the fact that it was two-way bound by default. So what do I mean by two-way binding?
02:04
This is a simple example where say in your select element the value in the select is reflected in the UI elsewhere. Those values are coupled. They're two-way bound. Change to one ends up being a change to the other. In a lot of situations where you have
02:23
kind of isolated information, this is totally appropriate and a good way to go, and it simplifies things a lot. But I ran into some use cases. This is actually something that I was asked to build where the select element state didn't match with what it reflected in the UI.
02:42
Here's an example of a filter that when a choice is made in the select, it would just update a running list elsewhere. So two-way binding wasn't appropriate there, and I had to jump through a lot of hoops, basically wrapping my Ember select in its own component to sort of simulate a one-way binding and
03:01
not have my data leaking out. The other thing that I found really difficult was I had no idea what HTML, what DOM this thing was actually rendering. I had a simple request to make some elements in a select disabled, and the solution I found,
03:21
here's a quote from Stack Overflow. This is a simple approach for rendering a disabled option with Ember select. Basically, you have to reopen the built-in view and add the support for disabled. As a new developer, that's a scary thing to do to go kind of modify and extend core code.
03:40
It's not super approachable, and if you're used to writing a select in HTML, you're used to just throwing an attribute on there, and so this is a bit of a barrier. So like I said, this haunted me on several projects that I worked on. This is a real quote from our Ember JS chat at TED.
04:00
This went on and on and on light at the end of the tunnel a little later in the year when I found out that Ember select is going to be deprecated. So I was happy about that, and I took an interest in learning about what my alternatives were, basically. So that's what I'm here to talk to you about today.
04:22
I'm going to talk to you about building a better select with modern Ember patterns. So we're gonna use real HTML so that we have almost complete control over what we want it to do. We're going to use the data down actions up pattern, and along the way, we're going to learn about a lot of cool new Ember features that have landed
04:43
since that select element was deprecated. All set? All right. So, let's take a look at first just building this out with plain old HTML. If you've ever done this in HTML, it should look pretty familiar. We're just going to use a little convenience with some HTML bars
05:03
and using each loop to iterate over our options to make that a little less tedious. So this kind of assumes that you've got like an array of objects that you're going to use for your options list that can come from a static list or a model in your app, and
05:21
we're just going to access something like you're doing a drop-down of languages. We use the language code for the value, and we'll use the language name for the label on our option. So now let's talk about this data down actions up part. This is a
05:42
kind of mantra in Ember lately. You might also see DDAU. If you want to confuse your non-Ember colleagues, throw that acronym in somewhere. So what that basically means is we want our data flow to be sending data down like into components into stuff that we're rendering, and then we want to respond to actions that are like bubbling up.
06:05
So this is actually really great because it's similar to what you might be used to dealing with if you're just using plain old JavaScript or jQuery with a select. How do we usually deal with form elements changing? We respond to an on-change action, and that action bubbles up your DOM.
06:21
So that's exactly what we're going to do with our real HTML rendered Ember better select. So what does that look like? All we need to do really is add to our existing code this on-change attribute, and we can tell that to fire an action, and
06:44
I'm going to call that action languageDidChange, and along with that action, I can go ahead and send the value of whichever option dispatched the change event. So this will be in wherever
07:01
template you're rendering the select, and then in the controller or in a component obviously later, you're going to catch that action, grab the value as a param, and then you can go ahead and do whatever you want with it. So in this case, I'm just going to set a property called selected language code to whatever value, and you end up with something like this.
07:24
So I pick my language, the code sends an action up, and then I'm handling it in this particular template by just displaying the value. So that's the actions up part of this. Let's also make sure we send data down. So the most common use case for this is usually when you want to
07:45
send in something like an initial value when you get to the page if that selects already holding an existing value. You also might want to send data down if for some reason this updates in another part of your app state.
08:00
You can also do the same thing. So in regular HTML, we indicate that a select element option is selected with the selected attribute, and this is one of those sneaky HTML attributes where no matter what you put in quotes, you can say selected equals false, and it'll still be selected. It's just the presence of it needs to be there.
08:25
But HTML bars is sophisticated enough now that we can do something like this where I'm going to set the selected attribute equal to some curlies, and you can write a really simple is equal helper here that just compares the two things that you give it, and
08:42
we'll ask this is equal helper if the current language in the iteration of our loop is equal to the selected language that we're passing in, and that's going to return a boolean, and it's smart enough to know to either render out that selected attribute or not depending on what your curly evaluates to.
09:05
Now this is really powerful. Some of you might have remembered that not too long ago, anytime we wanted to do a dynamic attribute, we needed to use the bind attribute syntax. This works out okay most of the time.
09:20
Something like the value is pretty easy to simulate with bind attribute, but what if we were trying to replicate that selected? How would you do that? That turns out to be not as straightforward. So having the power to evaluate stuff in line here is really awesome. So that, my friends, is dynamic attributes.
09:42
So bind attribute was actually deprecated way back in Ember 1.10. Hopefully, I don't think too many of you are back there, but sorry, this is a little backwards on my side. I think bind attribute,
10:01
something's weird with the version because the new feature can't come before the deprecated one, I don't think, or maybe it did. Yes, because we're good about being nice and leaving things in. So yeah, dynamic attributes are available as of 1.10 and bind attribute was deprecated in 1.13. Encourage you to use those because it gives you a lot of power
10:23
to do some simple logic within your templates and cleans up the kind of readability of your templates quite a bit. So put that all together and we get something like this, where I'm able to have an initial selection here, but I can
10:41
pass that initial selection in as data down and I can make changes with actions up and you'll notice that that original value does not get mutated by my select anymore. So that is really awesome and allows you all sorts of flexibility to manage your data flow as you see fit.
11:02
So remember my second problem dealing with select was that pesky disabled option. This turns out again to be pretty straightforward with our new tools. So let's return to the idea of having a model that we're using to generate our select options. A really easy way to do this is to
11:21
set a computed property on your model that evaluates to a boolean to use as a flag for something being disabled or not. So I'm going to switch over to the example of fruits and let's say we wanted for some reason to disable all the fruits in our list that are yellow. We can go ahead and use a computed property that sets that flag and
11:44
then we'll use our dynamic attributes again to say disabled is equal to that fruit dot is yellow property and again this is clever enough to know whether or not to display that attribute based on the boolean value of what your curlies evaluate to.
12:02
That looks a little something like that. No yellow fruits, we don't want those. So this is great. We've built something that is way more flexible. We have complete control over the DOM. The only drawback to this is we've lost the reusability of our magic.
12:26
Sometimes it's actually nice to have a bit of reusable code that you don't have to go through the slog of like writing out a lot of template for. So can we have a kind of happy medium between having code that's very flexible for us to use and having code
12:43
that's reusable? We absolutely can, that's where components come in. This is one of my favorite images ever on the internet, by the way. I put it as my lock screen background and it made me happy every time I looked at my phone. So
13:01
yes, we can have it both ways. A great solution here if you find yourself reusing the select code within your apps. Say there's multiple places around your app that you need to have a selector to choose a fruit. That's something where a component is really powerful to
13:22
abstract that code a bit away into your own reusable bit of code. So we actually don't have to do too much to change what we've got into its own component. We're going to move it into its own component file.
13:41
And the only thing we need to do now is make sure that that action actually bubbles up out of our component. So the kind of like way that probably most of you are used to doing this is by within the component, so the blue code, we're going to catch that fruit did change action again, and we're going to use this dot send action
14:04
to send that up out of our component. And then wherever we're rendering the component we need to pass in the name of the action that we want to be bubbled. So usually you just go with the same thing because that's straightforward and then out in that outer scope
14:23
you're going to then handle the bubbled up action. So this is a good pattern, but it's actually one of the things that I found really confusing when I got started. Having an action name in just as a string
14:40
was kind of hard for me to keep track of because it just looked like another property a lot of the time. And this also starts to bite you when you start nesting components several levels deep because you have to bubble actions up and up and up and up and up and it's a pain in the butt.
15:02
Components are a great tool for us, but passing actions wasn't really an easy process. Luckily, we have closure actions now. So this landed in 1.13, available in any version after that. And the way that closure actions work is this allows you to
15:24
instead of having to bubble up in an internal action handler you're actually just going to be able to, wherever you're rendering the component, so you can instead of
15:40
instead of giving it a named action, sorry the name is a little mismatch there, you can actually tell it that I want you to use an action called Update Fruit. So I love this because not only is it way more explicit in your template what's happening here, you know that this is going to do an action, run a function.
16:01
You also do not have to bubble this on your own. It's just going to look for anything in that outer scope called Update Fruit and it's going to be able to take the values that you've sent out from your deeply nested component. So you can go ahead and start layering things in
16:22
as like many layers in between as you need and that's going to bubble up without having to repeatedly send action. You just need to pass up the name in your templates. So this is really fantastic and again I think it's a lot more readable.
16:40
So that's the action up when we've turned this into a component. How about doing data down once we've turned this into a component? So it's going to look actually pretty similar. We're still going to iterate over our fruit and this time we'll just need to, wherever the template the component is called,
17:02
we're going to pass in that array of fruits and we're going to pass in the selected fruit as an initial value, and we haven't really had to change much of our template. We're just making sure we send in good data. But what about something like this?
17:20
What if we wanted to make that disabled key something dynamic? It's conceivable that in multiple places where you're using this fruit select that you might want to have a different type of fruit be disabled. So you know, what if I wanted to pass in disabled key is red? I've got this is yellow hard-coded in here. So
17:45
previously whenever I needed to do a dynamic key lookup like that, my solution was usually to render a nested component. So instead of just doing that option straight within the each loop, I would set up something like a fruit select option component and
18:04
this is going to allow you to make an attribute binding within that option component, and you can do something like set the value of the disabled attribute with a computed property in that one and you'll compare, you'll be able to
18:23
dynamically look up that disabled key with this line up here where we're using ember.get with this.get fruit and this.get disabled key. So this works, but it's kind of a lot of overhead just to get a dynamic key and you got to pass a lot of extra stuff
18:42
in so that that inner component knows about it. You got to send everything in there and your template is a little less readable than what we're used to seeing with just a simple option. So there's actually a really fantastic solution to that and that is our friend the get helper. So this landed in ember 2.1 and
19:04
the way that this works is it's just going to allow you to kind of bypass all that extra work to get a dynamic key lookup and you can do it right in your template here. So for the disabled attribute, I use the get helper,
19:22
I give it an object and then a key and it's going to go ahead and do an ember.get under the hood and just throw that in there nicely without a lot of extra overhead on my part and now I'm free to pass in a dynamic key to my component.
19:44
So we've built an awesome data down, actions up, select component that's tailored to our app, but still really flexible. What if you want to do two-way binding back? I know that two-way binding has kind of got a really bad rap.
20:01
I think maybe we all got burned by it early on when framework community was super gung-ho about that. But in some cases, like I say, it is appropriate if you're just dealing with a local app state and you actually have state that's tightly coupled. Sometimes two-way binding is just
20:21
the easier better choice for that. So there's actually a really great way to again have the best of both worlds and that is the mute helper. So this comes along with closure actions. So it's going to be available anywhere that closure actions are. So that's going to be 113 and up and
20:44
the way that that works is instead of passing in a named action, you're going to be able to just say, hey, I want you to mutate this value instead. So unchange closure action syntax again, but this time instead of putting an action name, a function name in there,
21:04
I'm going to actually just tell it to mutate a value called selected fruit. Now we just need to do a little extra work here to get this to kind of play nice with what we want. Right now our existing code sends in a fruit object for the data down part,
21:20
but our actions up is just sending up a value. So this isn't going to work really awesome as a two-way binding because we have a bit of a mismatch between the data that we're using. So what we really want is to be able to send up a fruit action so that that can be properly mutated with a piece of data of the same format.
21:42
But that's a pretty easy workaround here. What you're going to be able to do is, this is sort of like the power and flexibility that you have with closure actions, is instead of just letting that bubble straight up, you can go ahead and catch your fruit to change action within the component and
22:03
you can do what you need to do. So here we're going to take the value and we're just going to do a lookup by ID to find the corresponding object and then we'll use the closer action kind of inside here instead of doing this dot send action because
22:22
our action is passed in as an attribute, we can just go ahead and use a this dot get lookup for whatever got put into on change and that function is going to run and we're able to pass selection as a parameter up to that function. So lots of power, lots of flexibility when you're working with closure actions, and in fact
22:44
it's very used to, it feels more natural to how we know functions operate in JavaScript. So this is fantastic. Putting that together, this is a quick example of using the same component to either do a one-way or two-way binding. So I call it choose your own binding adventure.
23:04
Same code in the component, but as the consumer of the component you can choose what your data flow needs to be just by passing in either a named function or the mute helper with the value that you want to mutate. Really awesome.
23:24
So, what have we done? We've built our own select component that uses custom HTML. We've allowed completely custom data flow as we see fit in our app and we've also learned about all these really awesome powerful features like dynamic attributes, closer actions,
23:44
mutes, and the get helper. Put that all together and I say you have a select in your app that is flexible, reusable, and modern and that is exactly what your Ember apps and the things in your Ember apps should be.
24:01
So you can kind of like choose your level of abstraction here. Pick whatever is getting repeated for you in your apps as a select code. Abstract that into a component as you see fit.
24:20
And like do whatever you need to do that works for you. As an example, all of our internal apps are built in Ember, so we've worked on a little reusable component that suits our needs at TED called TED Select. So some of the features that we wanted were allowing the options to be sortable,
24:41
allowing multi-select versus single select, and this handles that. It doesn't handle everything. You know, there's a lot of things that you can make a select do, but I think choosing the right level of abstraction for your needs is a great path that we can follow moving forward in our apps.
25:03
So how did I figure out how to do all this? This is your friend. On the Ember.js blog the release posts every six weeks are really fantastic for keeping up to date with evolving things within the framework.
25:22
This is where I found out that Ember Select was going to be deprecated, and I started reading up and getting excited about new ways to do this. And I keep up with this whenever a new post comes out and see all the cool new stuff that this framework is evolving to do. I really think it's fantastic. I've seen in just a year's time
25:42
anything that's a pain point is actually getting solved. Even this morning hearing about the render performance gains in Glimmer 2, like that's been one of the other things that I've been having trouble with. And just to see that this level of action in the community and the core team to
26:03
bring us what we need and continue to grow Ember is really fantastic. So I do encourage you all to keep up with this and hopefully you'll get excited about new Ember features, and that can be incentive to upgrade your apps and keep things up to date. So I leave you with that. I hope you can all take away
26:22
stuff from here to build awesome select elements and hopefully other things within your app. Thank you.