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

The {{x-foo}} in You

00:00

Formal Metadata

Title
The {{x-foo}} in You
Title of Series
Number of Parts
20
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
Publisher
Release Date
Language

Content Metadata

Subject Area
Genre
Abstract
TDeclarative, evented, data-bound components will change the way you build abstractions in browser applications. The best part is, you're already an expert, you just might not know it yet. We'll discuss how to: Use a component Know when not to use a component Communicate with the world outside the component Refactor large components Test components in isolation Make components accessible.
VideoconferencingWebsiteCore dumpMetropolitan area networkEmailHand fanMultiplication signPerspective (visual)Login2 (number)XMLComputer animationLecture/Conference
MiniDiscShared memoryNumberPrototypeRight angleDifferent (Kate Ryan album)Uniqueness quantificationQuicksortAbstraction
Element (mathematics)Term (mathematics)Web pageConnectivity (graph theory)Library (computing)Component-based software engineeringContext awarenessWordGame controllerMusical ensembleNatural numberMathematical analysisView (database)Cartesian coordinate systemLine (geometry)Link (knot theory)Figurate numberRight angleMessage passingDisk read-and-write headBinary fileHyperlinkQuicksortHash function
Scripting languagePointer (computer programming)Cursor (computers)EmpennageFunction (mathematics)Clef1 (number)Hash functionAttribute grammarProduct (business)GenderHyperlinkDefault (computer science)Functional (mathematics)Software frameworkSubject indexingTouchscreenMultiplication signConnectivity (graph theory)BuildingWindowUniform resource locatorCursor (computers)Pointer (computer programming)CodeMeasurementGreatest elementElement (mathematics)Marginal distributionSoftware developerKeyboard shortcutData structureMatching (graph theory)Link (knot theory)NeuroinformatikCategory of beingComputer-aided designWeb pageNatural numberSpring (hydrology)Computer animation
Function (mathematics)Electronic visual displayScripting languageMeta elementConnectivity (graph theory)Element (mathematics)Electronic visual displayScripting languageDependent and independent variablesOpen setTemplate (C++)CodeCommitment schemePoint (geometry)Type theoryExecution unitContent (media)Source codeInsertion lossXML
Scripting languageSet (mathematics)Program flowchart
Game controllerGroup actionMetropolitan area networkDirection (geometry)CodeSound effectPoint (geometry)Connectivity (graph theory)View (database)Stress (mechanics)Right angleComputer animation
Scripting languageGroup actionRotationComputer iconArrow of timeMountain passFunction (mathematics)Key (cryptography)Server (computing)Game controllerGroup actionUniform resource locatorLibrary (computing)Computer iconInheritance (object-oriented programming)Point (geometry)Arrow of timeCodeView (database)Keyboard shortcutTemplate (C++)TrailType theoryFirst-person shooterScripting languageFocus (optics)Process (computing)Form (programming)ExpressionConnected spaceMetropolitan area networkCurvatureAbstractionDataflowXML
SpacetimeElement (mathematics)ComputerClique-widthFrictionScripting languageConnectivity (graph theory)Game controllerSpacetimeMedical imagingType theorySource codeProduct (business)Keyboard shortcutXMLComputer animation
Slide rulePresentation of a groupAttribute grammarConnectivity (graph theory)QuicksortFigurate numberSoftware frameworkContext awarenessGroup actionSoftware testingCodeKeyboard shortcutWater vaporOffice suiteFunctional (mathematics)TelecommunicationCore dumpAuthorization
HyperlinkEmailScripting languageElectronic mailing listQuery languageConnectivity (graph theory)Inheritance (object-oriented programming)Cartesian coordinate systemState of matterWeb pageDemo (music)CASE <Informatik>Query languageGroup actionSubject indexingGame controllerPattern languageParameter (computer programming)NumberSoftwareTemplate (C++)Computer animation
Euclidean vectorAvatar (2009 film)Software bugInformationGame controllerQuery languageCategory of beingEndliche ModelltheorieInterface (computing)Product (business)Cartesian coordinate systemAttribute grammarNP-hardCore dumpContext awarenessDirection (geometry)Message passingGroup actionRight angleFlow separationRouter (computing)Bit error rateTemplate (C++)Special unitary groupMereologyUniform boundedness principleInheritance (object-oriented programming)Data managementWhiteboard
Menu (computing)EmailScripting languageLink (knot theory)Function (mathematics)Menu (computing)Open setGroup actionCASE <Informatik>Event horizonMobile appInterior (topology)Game controllerFocus (optics)Cartesian coordinate systemCategory of beingElement (mathematics)Connectivity (graph theory)CodeComputer animation
Menu (computing)Euclidean vectorAttribute grammarKeyboard shortcutCategory of beingPrimitive (album)TouchscreenCombinational logicLink (knot theory)Element (mathematics)Right angleAliasingPoint (geometry)NumberSpring (hydrology)Menu (computing)Mobile appGame controllerXML
Mach's principleLocal GroupGradientGroup actionCloud computingTemplate (C++)Combinational logicSoftware bugComputer configurationNumberAbstractionMultiplication signType theoryGroup actionForm (programming)Element (mathematics)1 (number)MathematicsElectronic mailing listoutputDynamical systemGreatest elementHacker (term)CodeAttribute grammarConnectivity (graph theory)Inheritance (object-oriented programming)Position operatorQuicksortPlug-in (computing)Selectivity (electronic)Fluid staticsSocial classContent (media)Revision controlState of matterInterface (computing)Event horizonSoftware developerRight angleTablet computerDifferent (Kate Ryan album)Web pageFigurate numberExtension (kinesiology)StatisticsIterationTap (transformer)Temporal logicInstance (computer science)Category of beingSystem callData modelDefault (computer science)DeterminantXML
Demo (music)Euclidean vectorBinomial coefficientBootstrap aggregatingFreewareGame controllerCodeModule (mathematics)Line (geometry)Template (C++)Interface (computing)Data managementCategory of beingConnectivity (graph theory)Natural numberIterationSet (mathematics)PreprocessorNumberCartesian coordinate systemMaxima and minimaEndliche ModelltheorieSubject indexingInferenceXML
Function (mathematics)AliasingTemplate (C++)Scripting languageCASE <Informatik>Cartesian coordinate systemFile formatGoodness of fitPrice indexComputer fileData structureWeb browserComputer animation
VideoconferencingCorrelation and dependenceOpen setWeb pageDemo (music)Library (computing)Address spaceXMLUML
Transcript: English(auto-generated)
next speaker, I described him yesterday as the second angriest man in JavaScript.
Ryan is simultaneously both the biggest fan and the biggest hater of Ember. I can prove that. I'll show you my IM logs. So I think it's really important, like I said yesterday, to get diverse opinions, people who have been around a lot.
So I'm really happy today. Ryan is going to be talking about components from the perspective of a MuTools core contributor.
Thank you, Ryan. I haven't talked to him lately. And this talk is really just going to share with you where I'm at with it. If anything is the Wild West in Ember, I think it's probably components where sometimes doing the wrong thing feels about as good as doing the right thing, because we haven't figured out all those things quite yet.
So I hope to show what I think are some of the right things to do with components. Components are just tags that have unique style and behavior, sort of like some people.
I'm counting to 30 in my head. OK. So aside from that, it's a custom element with an optional template, an isolated Ember view that controls that template. And then the view is the context. Or in other words, it is its own controller, sort of, kind of.
I had a JS Bin that Yehuda didn't like very much, where I made an application component. And I did application view equals application component, and application controller equals application component, and it worked. Then I get a message from Yehuda and I.M. that's like,
hey, want to talk about your talk? Can you just make sure you keep it within party lines? I was like, I don't know what those are. All right. So let's think about the most common component that you learn when you first learn HTML, the anchor tag. You might not think of this as a component, but it totally is, and it's actually kind
of a confusing thing. Think about the behavior of an anchor tag. If we don't have an href, it's just like, it does nothing. You don't even get an underline. You don't get styles. But as soon as you drop an href on it, now it turns blue, you get an underline. And if you click it, and the link is not internal to the document, you actually get a reload new page, send
you somewhere else. If you have a hash in front of the href, now all of a sudden we're saying, oh, wait, this is inside the document, and so now it scrolls down to that element if it can find it. So let's recreate the a tag.
Got my doctype, got a style here, so xa, it's blue, text decoration underline, cursor pointer. And then here I can say xa href equals emberjess and about. Then I've got this h2, throw a bunch of margin on top so that we have to scroll down. And now to implement this guy or girl, depending on the
gender of the component, I think that this is a guy. The next one's a girl. Tag name is xa. We've got some attribute bindings. Attribute bindings, whenever you set a property, it's going to keep that up to date on your element. Tab index, I'm doing this because I want to be able to
tab to this thing and allow it to be focusable. So I add a tab index to it. And then I did a role because I'm a nice guy to blind users. So the screen reader will actually think of this as a link, as it should. And then I just set href to null as a default. Tab index, you actually don't want a tab index at all if
there is no href. You don't want this thing to be tabbable. And so I do a computed property, and I check, do I have an href, set tab index to zero, which puts it in the tabosphere or null, and then ember will just remove that attribute completely when I say null. Go to href on click.
One of the great things about ember, sometimes people measure productivity and how much stuff they can do in a certain amount of time. For me, building a lot of the framework stuff and structure, I think of productivity as how many questions can I not be asked by my team if my component does what it's supposed to do, and if
the code makes sense. And I love that with these click handlers, you're forced to name things in ember. Like, I've got to name this go to href rather than just on click and then some random function. I have no idea what you're trying to do. At least now I have a clue about what the developer intended to do by the name of the function. And we all know how often comments that explain things
stay up to date. So I get my href, check if I started with a hash. And if so, then I scroll to the href. Otherwise, I visit the href. If I scroll to it, I go and select that element. If I didn't find one, I just give up. If I did find one, I find the bottom of it on the page, scroll my document down to it, and then update the hash.
Otherwise, I just do a window location. So here it is. You see I can tab to these things. And if I click About, I scroll down. And the back button doesn't work with this. I didn't know how to make that happen. And then if I click this, it'd send me off to ember.
So that's how the a tag works. It's a component that you're all familiar with. Then I thought, oh, what's another? Oh, no, dang. Oh, well, these were unicorns. Imagine sparkling unicorns.
Why did it not open it up? Here, let me go into Macvam and open this guy up.
We have a script. I thought, well, I did the anchor tag. Script is kind of a weird component also. And so I've got this xScript that points to my bower component, brings in Cornify. And then here, I actually have a body for a script tag. So a script tag is also a component. If you have a source, it goes and fetches it.
If you don't have a source, it yields the body. And so now I've got my xScript component. My layout is just a triple mustache to open up or to yield the template without escaping anything. And I don't know what Chris was talking about. Will insert element? I totally have an element here.
And I can set a display to none. Chris? Anyway, I don't know how I got this element. It must be magic. Hide the script tag so you don't see the code. And then fetch your yield when it inserts. Open an XHR, go get the script, eval the response text, or if I yield, get the text content,
and then yield that. And so that last thing is what you just saw. Creating a script tag out of that. So this stuff is pretty neat. But aren't we supposed to make stuff that doesn't exist yet? Like, I don't know.
Maybe an X-wing? Uh-oh.
Easy. All right, where are we going? Heads up.
OK, OK, OK, OK. Let's see if we can get this guy back over to me. Come on, man. So my controls are as they should be. Oh, wrong way.
I shouldn't talk, I should just do this, right? All right. It is acting really funny with all the Wi-Fi in here.
So let's turn this off. And then I'll show you the controls. Anybody know Jameson Dance? He just spoke at Mountain West JS and hooked it up to an Xbox controller. And I was like, oh, dude, that's the greatest component ever.
So you can see here, when I hit W, I go. And if I keep hitting W, then it turns red, which means I'm at max speed. And if I hit S, then it switches down. And I can go two directions at once. But I've got this concept of opposite directions. So if I'm going left and then I hit right,
I set the left to zero. Actually, that just stops everything. And then this side is the point of view, which rotates it or makes it go up and down. And of course, this launches. And I can click all these things to get the same effect. So let's check out this code.
Oh, and here's all my actions. All right, so the X-Wing code. To use it, it's just a simple little tag, X-Wing, and then a URL to the, I've got an express server that actually controls the thing. It's got a Wi-Fi chip in it. And then I connect to its Wi-Fi, which I was freaking out because I
couldn't get connected earlier. Note my dock type up here. It's kind of how I was flying it. So now I've got my template for the X-Wing. And I've got another abstraction, the X-Wing action. So I just say front. So this action makes it go forward, like tilt forward.
And then I bind to the parent components, vals.front. And then I've got these icons, icon arrow, and I can rotate it. And so you see that all these are just the same icon. I just rotate it around. And then left, so it's all the same thing. Left, left, left, and then the point of view one. Same thing, icon action up, counterclockwise, et cetera.
And these are my icons. Just throw some SVG in, make a template. You don't need any JavaScript. And now you've got icon launch and icon arrow. Now let's look at the JavaScript.
Tab index, because when it's not focused, it doesn't fly. I was trying to have my three-year-old fly it. And he kept clicking outside. And then it wouldn't control it. And it's up on the roof of my in-laws' two-story house. And I was walking across it, and it's super steep.
Thought I was going to die. OK. So the vowels are, I keep track of how fast it's going. So from 0 to 1. And anyway, that's just for the speed of it. Then I've got some opposites here, front, opposite is back, et cetera.
And these are all the key things that I respond to in my key map. So then I have one keyboard action on key down. And I throttle it, figure out if I get an action if I do perform it. And then down here, I can get the action and perform a paired action. That's like left and right, so they cancel each other once it happens. Animation, that was the flip.
And then otherwise, I just call some method on here, assuming I've defined it. And then, yeah, just some more stuff. Make an Ajax request. This is the real magic, is it goes off to an express server. And that express server has the drone library. And that's the thing that actually controls it. Yeah, so that is it.
Same controls are for like any first person shooter you've used before. Greatest component ever. Or is it?
What is going on? I hope some of you can appreciate that quote.
Doc type public offender, spacer gif, yes. Find the height to y of my controller, and then this is bound to y. All right, so I can just, woo.
Here's a spacer gif component. It's an image. Attribute binds to source with height. And then I set its data to, that is a transparent gif. Oops, not anymore. Yeah, there's a transparent gif for you. So use it.
This is production ready. So communication, this is often a hard thing for both people and code, especially when you put them together. So how do you get a component to talk to the outside world? And I remember when the core team was sort of screwing around with this idea of components,
we were actually making this function called, actually I called it component. And then we would get a view and we'd do an initializer and then set this.context equals this. And just started messing around with that. And I did a presentation back in Utah at our lunch JS. And I made a slide show thing and I had no way
to communicate out. And then as components started getting better and better and then actually shipped, we've got two ways to communicate. It's data binding and actions. And this is fantastic because now if you build a component, you don't really even have to document it. Like people know, I've got some attributes
that I can two-way bind to, or I've got some actions that it might respond to. Instead of in the days of other frameworks or just libraries, you've got to go and read the code or the comments or go talk to the author at your office who made the thing and figure out how to actually interact with this, or go read the test that they wrote,
et cetera. So let's first talk about data binding. I've got, I'm going to be talking about IC tabs. So at Instructure, our main software is called Canvas. So all of our stuff is prefixed with IC, IC Canvas. I see dead people. That's coming soon. So IC tabs is something that I just
finished a couple weeks ago. And it's kind of like the state of the art for me on how I build components in Ember. So I'm going to be talking a lot about it. So one of the use cases that I think is incredibly handy for a tab component is to persist to the query parameters
so that you can refresh, and those same tabs are there still or you can link to a page in that state. So here's my demo. I do IC tabs, and then I say selected index equals country. So that's in my application template. So my application controller down here
has query params country, and country is set to zero. So the first tab is what will show up. So let's see this in action. So I can click Japan down here in countries and click Texas in countries.
People from Texas are like, that's right. And Canada's like, where are we? OK. Now if I refresh this, Japan is selected. And then this one was bound to the food on my application
controller. So it's pretty awesome. So I actually wanted an API for ICTabs, or I'd say ICTabs query param equals, and then like a name. And I went into IRC, and the whole core team was like, you're a moron. And Yehuda's like, I'm so happy with everyone right now.
I was like, yeah, but what about me? No hard feelings. So that's data binding, which you can see will work all the way with all of the other Ember concepts,
where your controller sets the context of your template. Well, proxy's the model anyway. And then query params is just bound to those controller properties, and now your components combine to those controller properties. And so all of the data and the information is flowing the right direction. And when I wanted to do that weird interface of having
a query param attribute on my tabs, it was really hard. And I was like poking around. I'm sending Alex messages saying, hey, how do I find the router? And he's like, don't. And so, again, productivity for me is making things that I can create and then just forget about. And everyone will use them, and they just work.
And you get a bug every now and then, but for the most part, people will wonder if the thing is active, because GitHub hasn't been updated for several months. But it's because it's done, and it works. And Ember provides that for me by making the wrong thing hard and the right thing easy. You saw that query param thing was really easy. So actions, let's check out how these work.
I've got another one called IC menu. I was talking with another Ryan last night about popovers. And we're saying how it's kind of deceptive. It seems like a really easy problem. And you just click a thing and toggle a property and then have an if in your template, and now this thing shows up. But if you start to really think about how a popover works, now you've
got this outer click event that you need to deal with, because it's open, and then you go click somewhere else. And so you think you're really smart, and so you put an event on the body or on the document, and then you plan on some event bubbling up and then checking what was clicked and then closing or not. But you forgot that select tags don't actually propagate the click event all the way up, so that doesn't work.
And then you tab and focus on the other element, and so your thing is still open. So it gets really complicated. And so we have a component for this so that we don't have those kind of weird edge cases happening. And I don't know what people want to do when a menu item is selected,
and so I just hand that to their app with my on select event. So when I use IC menu, I make an IC menu item, and then I say on select remove. And that will go up.
Oh, never mind. My code's down here. So my application controller's here, and I've got actions on that controller, remove. So that's going to call remove. Same with save and everything else. So let's check this one out.
So I click this menu, pops up, and then I click remove, and I get my alert. And like I was saying, this is a pretty tricky thing, so got to figure out how to deal with all that stuff. So the thing is, simple tasks, there's no such thing.
Except link two in Ember, which is actually a really hard thing, but for us now it's simple. So Ember gives me the primitives to make the things that seem like they should be simple actually simple for people to use. So no one has to think about that anymore. Another consideration is attribute bindings for accessibility.
So when you open up this menu, I'm setting a property called aria-expanded, true. And that triggers to a screen reader, hey, this thing is open. There's also things like aria-has-popup, and you have aria-controls, and the aria-controls needs to tell the screen reader which element it controls, so it knows how to jump the user down there when they
do the right keyboard combination. So again, simple things are not actually simple. And with Ember's primitives, I'm able to just bind an attribute to a property. And sometimes it's just a bunch of aliases for me to get something accessible.
In my description, I talked about writing and refactoring large components. Components were originally thought of as templates. And in fact, the first version of components in Ember, if you didn't have a template, you didn't get a component. Fortunately, now you don't need templates. So this might be what an x-tabs component would look
like if this is the first time you're writing a component. You might just think, oh, this will be sweet. I just do x-tabs, and I say tabs are your groups. So maybe you're on a group page, and you want to make a tab out of each group. And then you're like, now I'm going to do all the hard work down here. And in your template, you create this div, and you iterate over the tabs, and you make the tabs, and you have a label, and then you iterate over the tabs
again and make your panels. And everyone's happy, and it's really easy to use. I won't put my hand there again. So there are some problems. Number one is x-tabs component does everything. You probably can't read that very well.
x-tabs component does everything. So it's going to get unwieldy. It's going to get really huge, and everybody's going to start saying things like, oh, no, there's a bug in tabs component. I don't want to go into that code. To be accessible, the tabs need some attributes that get changed. Where are you going to do that when you just have one big component? How are you going to go get those other elements and update their attributes?
Now you're getting right back into jQuery spaghetti. Why are we forcing tab.panel and tab.body on people? This is just a tab component. We shouldn't be bound to some sort of data model. And what if we want the tabs on the bottom? My template had them on the top. But does the position of the tabs really have anything to do with the behavior?
Or what if I don't even have any data, and I just want to make some static tabs? I've just got some static content. I just want to stick it in there. Or even worse, what if I want one static tab and then a bunch of dynamic ones from data? There's just no way to do that when you build these large components with big templates. So instead, what if we did it like this?
We make an xTabs, and that's a parent thing. Then we have xTabList, and that defines the behavior for the list. And then we have an xTab component for the individual tabs. Now each one of the pieces of this component are separated. I call these composite components.
So you composite a bunch of smaller components together. So now in this instance, I can have just a static tab called default, and then I can have these dynamic tabs for groups. And I'm not bound to some sort of data model. I can just use group.name instead of switching my group stuff into label and stuff like that.
The other thing is these child components can now manage their own state. So if their attributes need to change when they're selected, the tab can just do that itself. It knows it's selected, and then it can change its attribute accordingly. You don't have to have some parent, big, giant thing
using jQuery to go and select things. And then you're wondering, why am I using components? This looks like when I was writing jQuery plugins. OK, OK, you get it. But wasn't the old thing more convenient? People had to write a whole bunch of stuff now. So instead of doing the huge component, I make what are called convenience templates
on top of those components. So I do X tabs top and then tab. So it's right back to that old, to the first interface. But now I can compose these tabs however I need to. So on this one, I've got tabs on the top. If I wanted tabs on the bottom, I just make a new convenience template and move the tab list to the bottom.
And everyone else on the team can still just do X tabs top, tabs equals groups, and then they're done. So the trick is, oh, and now I have no template. Because the, well, in the old one. So doing this, you get the composition flexibility
with these convenience components without doing crazy hacks. Keep it declarative. Keep it simple. Two things to remember. I have a bias toward no templates. As soon as my component has a template, I start thinking, OK, I'm missing an abstraction in here. And the best way to find where those abstractions are
for these subcomponents, just identify them by, do they have events? Like, if I click a tab, does something happen? Yes. OK, that should be its own component instead of delegating from some parent and then checking an event.target and determining if it was a tab and then perform some action. And then the second is, if you need to manage its class or attribute, depending on the state of the whole
component, it should probably be, well, not probably, it should be its own component as well. And the best way that I found to talk about this with people and just myself is to try to think about native elements and the select option combination is just wonderful for this. You think about what it is. You've got two different components, an option tag,
which makes no sense outside of a select tag. And you have the select tag, and it's got a value. Depending on which option tag you interact with, that's what the value becomes on the parent. And so that's a great reference to when you're thinking about components and how should I abstract this, try to find a native thing, like a submit button inside of a form. Well, that changes the whole behavior of the form
once you have an input type submit down in there. Changes the behavior of hitting enter inside of an input text, right? So just having an input type submit, the whole form reacts to that.
Another tip is to test and develop with dynamic data. The great thing about Ember is you get things updated just when you change your data. And everyone who's wrapping bootstrap or jQuery things guarantee you your stuff doesn't work when you go and change your data. Like if you're iterating over an each and creating tabs with bootstrap,
you go change that data, you're going to have to do a lot of tear down and set up management. But if you build these things native to Ember and you consider the dynamic nature of the data that people will give you, you can make them a lot more usable. So to create this tab interface where I can just
create these dynamic tabs, and then if I say I delete a tab that is active, and now it's gone, I've got code in there to say, oh, hey, that thing is gone now, which one should I select? I should probably select the one previous to it. So again, it doesn't take a whole lot of code for me
to build something that used to be really difficult with any other tab kind of component. But when you build it native in Ember, you get this stuff almost for free. But you need to develop with it in mind. And then you also get interesting things like this, where I combine the data to the selected index, and now anything on the control can be bound to that data,
and the template can be bound to that data and control which tab should be active. Distributing your stuff on a container with an application initializer, pre-compile your templates individually as the layout property of the component. I use a thing called IC style that we made for minimum viable amount of CSS for the component
to work. If it's more than 10 or 15 lines of CSS, it's probably too much. And then I use Broccoli dist ES6 module to publish to everything. This is ICTabs. So you see my main script pulls in everything. I initialize it, register things on the container, and then I export them all.
And then I've got my dist that distributes all of these different formats. And so in the case of browserify, you can npm install ICTabs, go to your application file, require ICTabs, and then go into your template and just use it. And it'll work and look good. So this is how I distribute it. So if you're looking to distribute things,
I don't know if this is the best way yet, but it's the most convenient that I've found for us at Instructure. We are working on a lot of components. You can find them at this address. Only a couple of them have demo pages right now. More are going to be coming. And help us out with them. Ember really needs a few good libraries
that we can point to and say, hey, here's some components that you can use, and they work with your data. That's it.