Mastering the Art of Forms
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 | 27 | |
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/35692 (DOI) | |
Publisher | ||
Release Date | ||
Language | ||
Producer | ||
Production Year | 2018 |
Content Metadata
Subject Area | ||
Genre | ||
Abstract |
|
EmberConf 201822 / 27
3
4
6
8
12
13
15
16
18
21
22
23
25
26
00:00
VideoconferencingForm (programming)Linker (computing)Decision theoryBuildingData managementEuclidean vectoroutputCore dumpMaxima and minimaElement (mathematics)Function (mathematics)Advanced Boolean Expression LanguageData typeCodierung <Programmierung>Component-based software engineeringQuicksortRaw image formatoutputMultiplication signExecution unitLibrary (computing)Different (Kate Ryan album)Wage labourTranslation (relic)Field (computer science)Category of beingProcess (computing)Parameter (computer programming)Connectivity (graph theory)NumberAssociative propertyFunction (mathematics)Pattern languageCodeForm (programming)Web pageBoolean algebraSpacetimeBuildingPasswordCuboidType theoryForcing (mathematics)Software developerLine (geometry)Order (biology)Patch (Unix)String (computer science)Computer-assisted translationSimilarity (geometry)MereologySign (mathematics)Cartesian coordinate systemCodeElement (mathematics)Complete metric spacePerspective (visual)WebsiteWeb 2.0Video gameOcean currentBitGoodness of fitData conversionDependent and independent variablesUniverse (mathematics)Software design patternEndliche ModelltheorieUtility softwareHypermediaFacebookFunctional (mathematics)TwitterDecision theoryFamilyCore dumpArithmetic meanInternationalization and localizationSlide ruleObject (grammar)Group actionDivision (mathematics)Inheritance (object-oriented programming)Attribute grammarSoftware engineeringMessage passingXMLComputer animation
10:08
outputExecution unitData modelComputer configurationHash functionLocal GroupEuclidean vectorMaizeElement (mathematics)Codierung <Programmierung>Component-based software engineeringForm (programming)Web pageArtistic renderingState of matterNumberError messageStructural loadDefault (computer science)Color managementMathematicsEndliche ModelltheorieClient (computing)Pattern languageServer (computing)Endliche ModelltheorieSoftware testingCartesian coordinate systemBus (computing)Connectivity (graph theory)Computer configurationResultantArithmetic meanComplex (psychology)Error message1 (number)Address spaceDifferent (Kate Ryan album)Pattern languageHookingElement (mathematics)Field (computer science)NumberPhysical lawoutputClient (computing)Group actionSystem callState of matterMultiplication signLocal ringForm (programming)InformationAttribute grammarWeb pageDatabaseDrop (liquid)Roundness (object)Hash functionArtistic renderingFunction (mathematics)Representation (politics)Touch typingInheritance (object-oriented programming)Fitness functionData structureType theoryTranslation (relic)Key (cryptography)Code refactoringPoint (geometry)Block (periodic table)RoutingCodeSocial classMereologyLogicLine (geometry)Electronic mailing listService (economics)Data managementHypermediaProduct (business)Sign (mathematics)Context awarenessFrequencyLibrary (computing)Task (computing)Entire functionBitKeyboard shortcutLevel (video gaming)PlanningGame theoryServer (computing)Network topologyFront and back endsData storage deviceSoftware design patternException handlingStructural loadConcurrency (computer science)Cycle (graph theory)Video gameComputer animation
20:01
outputError messageEuclidean vectorElement (mathematics)Keyboard shortcutState of matterData modelPersonal digital assistantCodeGroup actionCovering spaceComputer networkWebsiteAttribute grammarContent (media)InternetworkingField (computer science)Web browserNumberComputerMathematicsOperations researchIdempotentCategory of beingConvex hullComponent-based software engineeringOrder (biology)Core dumpDecision theoryForm (programming)Field (computer science)outputOnline helpVery-high-bit-rate digital subscriber lineLatent heatValidity (statistics)Cartesian coordinate systemCore dumpConnected spaceServer (computing)Endliche ModelltheorieWeb 2.0Semiconductor memoryCoefficient of determinationMereologyPattern languageGroup actionState of matterFeedbackMultiplication signMultiplicationDependent and independent variablesMoment (mathematics)Error messageSheaf (mathematics)System callService (economics)Structural loadUniformer RaumEvent horizonWeb browserLevel (video gaming)Software developerObject (grammar)Source codeTablet computerSoftware design patternElement (mathematics)Sampling (statistics)Query languageCASE <Informatik>Category of beingMessage passingExtension (kinesiology)QuicksortConnectivity (graph theory)InternetworkingLibrary (computing)Data managementDifferent (Kate Ryan album)Type theoryCodeMatching (graph theory)Address spaceCuboidPoint (geometry)PasswordMathematicsNetwork topologyInterpreter (computing)Internet forumKeyboard shortcutIdempotentBoom (sailing)Hecke operatorLogicAttribute grammarRegulärer Ausdruck <Textverarbeitung>EmailState observerComputer animationLecture/Conference
29:54
Linker (computing)AverageRow (database)Coma BerenicesComputer animationXML
30:17
Block (periodic table)Data typeComputer animation
Transcript: English(auto-generated)
00:20
Thank you all for coming to see me.
00:22
I know Forms is not the most exciting piece of UI. It's something that I've had to do a little bit, not a little bit, actually a lot in my current job, so that's why I'm here to share with all of you. Yeah, so let's get started. So a little bit about me, oh wait, I forgot.
00:41
This is the slide URL if anyone wants to follow along. I also posted it in the Slack channel. So for those of you that would like to follow along. Anyway, so a little bit about me. My name's Danielle. This is my Twitter handle. I'm a software engineer from New York. I love animals, so last week I was in Australia, and I got to meet and pet a kangaroo,
01:02
so it was like the single best day of my life. I also work at a company called Blue Apron, and so for those of you that think that Blue Apron is just a podcast sponsorship company, we do something else too. We also sell food to our customers,
01:22
and so I work on the warehouse side of things, and so what that means is that I build tools for our warehouse associates to do their jobs, and so there's a lot of data that goes into what happens in our warehouses, and so there's a lot of pieces from tracking labor and work order units to tracking inventory
01:42
and different things having to do with incoming raw inventory and whatnot. So that's what I'm gonna talk about, all the lessons that I've learned having to build a lot of forms. So forms are everywhere.
02:02
A form might be the first thing that your user sees. It's the first impression of your product, and so it really might determine whether they convert, they become a customer, or they enter any sort of conversion funnel. So they're also how we connect with our friends
02:21
and our family on Twitter and Facebook, whatever else people use for social media, and then they're also used for really basic utility. This is a Google Calendar screenshot, and so you can see there's a lot of input fields here, all that need to be understood and intuitive for the user.
02:43
So why is it that developers, if forms are so important, hate building forms so much? Everyone that I told, I was telling this talk, they had just like a wince look initially, and it's true, that's how a lot of people feel. This was me a year ago. I made this tweet.
03:01
I put it out into the Twitter universe, and I got a response from someone on Ember Core. They were like, do you want to talk about it? So I outed myself, and we had a really good conversation. I don't actually remember who it was because the conversation went away eventually,
03:21
but there were a couple people that I talked to, and after I realized that it makes no sense to complain, I decided that, okay, I'm gonna kind of zero in on all these patterns that I've come to see and use repeatedly, and I'm gonna make some decisions that's going to make building forms better for me and better for my coworkers.
03:44
So there's two core pieces of this. I would say that really good design patterns is one, and then managing your data is another one, and that kind of builds the foundation of what you need to build efficient and scalable forms. Then so with those two, it really clears the path
04:02
for a great user experience and also building a web accessible website. So component patterns, let's talk about those. So first off, this is our example form. It's just a sign-up page that I created.
04:22
It has two basic inputs, radio buttons, and then if you can see there, there's a disabled third input that it has a dependency on that radio button grouping. So this is an example of a form component that could be used, and so we've basically just used some handlebars,
04:44
input helpers, and then some label tags. We've used a couple different types in the input, and so this is pretty easy to read. It's there. It's not necessarily reusable, but it gets the job done. And so considering what the code is
05:02
that we would need to use to create this component, what are the design questions that we would ask before we would actually make something that is more modularized and reusable by other developers? So first of all, we want to look at what types of input we're using. So as we saw there, we had a text input
05:24
and a numbers input. We were using a radio box, not a check box, and so these are questions that we want to think about. Also, are we using a password versus email? Are we using phone numbers instead of email? So start thinking about those things.
05:41
Is this a required field? It's self-explanatory. Is it dependent on another component? So like I said before, we had one component that had a bound property to the radio buttons, and so we want to see what is dependent on each other. And then also, do these components supply functionality
06:04
for other components? So for example, we want to think of our components as having an input and an output. We wanted to think about it functionally. And so we have an input, and so an input would be an Ember object or a model or a Boolean or something,
06:20
and then our output is our HTML element. So the HTML element that is being outputted is that a custom div? Is that an element that is just another form item? What are we actually outputting? So this is the first snippet of code that I'd like to iterate on over.
06:41
It's pretty simple just to highlight what we have. Obviously, we can look at this and see that, okay, there's some common patterns. We have the label tags that we're using. We're using that twice. We have the ID tags, or not ID tags, the input helpers. We're using that twice,
07:00
and then the attributes that we're passing into the input helper, that's all pretty much the same. So we could probably take this and make a component out of it. So we could do something like this. We have, in the scope of our application, an input text field,
07:21
and so it's kind of customized to our application, and we build our own API so that the application knows how to handle this type of element. So this is what our input text field example would look like. So what we've done is we've taken those label tags
07:42
and the input helper, and we've created this dynamic element, and so we're using the translation library, the I18N for Ember, and so we've created a pattern that concats a string that creates an input label, and then we're using our input ID
08:01
in three different places because we're basically just using the same string path to create these HTML elements. So we pass in the required attribute, the value, and yeah. So what is the benefits of doing this? We don't save a lot of line space,
08:22
and some would actually argue that being explicit in the parent component is actually better. I understand that argument, but as far as the benefits go for doing it this way, we're standardizing our API of the form elements in the scope of the application
08:41
and among the code's components. So we have the same input that we're using for all of our text fields, and so we're not giving any, so we've said, okay, we're gonna use label tags, and we're not gonna use divs or paragraph tags. We're using label tags, and then we're gonna use the input helper,
09:01
and so we standardize that API. The next part is that we're standardizing the UI. So we want to make sure that from the user's perspective, we also have a similar look across the application, so we don't want to say, this is, my username input is blue, but my, I don't know,
09:22
my first name input is red or something, because then that's an inconsistent UI, and the user said, they think, okay, well, I'm supposed to do one thing in this place, and this other thing in this place, and they might not understand that it's just a text field, just type in your valid input. Okay, so this is the part of the talk. I know it's late afternoon.
09:41
I want to give everyone an opportunity to get up and stretch if they need to, or clap or snap or wake up your neighbor. I'm gonna keep talking, but you guys can, don't feel like you just need to sit there and stare and listen at me. Go ahead, get up.
10:06
Cool. Okay, so the next part is we have this form, the form example that we had before. So we're taking the snippet of code. As we saw before, we have a label tag,
10:21
and then a couple input tags, two of which are radio buttons, and then one of which is using a number input. So on this last line here, we have an attribute binding that depends on the values from the radio buttons, and so we want to group this logic together
10:40
so that A, it's organized, and B, it's reusable, but also so that we're not completely constrained to what the layout of the elements. So what might this look like? So this is something that I would do. I like using components to yield more components.
11:03
I think it's a really great way to create dynamic components so that it's not bound to the UI of the parent component. So we have an input of just a model, and then we have an output of a couple elements. So this is what that form group component might look like,
11:21
and so we're just using a yield helper, which is using a hash helper, and then the hash has a couple key value pairs. So we have a question text, and then we have two components that it's yielding out to the parent component. So as we said here,
11:40
we're using the translation helper again, and then we're using the component helper, which takes the name of the component and then also some attributes that go with it. And so this is for those of you that are more visual. This is what the structure would look like from the component tree.
12:01
So we have the parent component, which is the sign-up form, and then we have the two inputs, so we have first name, last name, and those are rendered by that sign-up form component. And then we have that parent input group component, which yields our radio buttons and our lucky number input.
12:22
So this is different from the first one. As you can see, we kind of switched around some of the input. So that pattern that I just suggested, would that work with this? So let's say we had, for example, just to give some context, if we had a sign-up page and we wanted to use the same elements for an account page,
12:42
but for whatever reason, our designer product manager said, well, I want to switch everything around or put them next to each other. How could we handle this? So yielding those components with the groupings would actually work, and so that's what's great about yielding these components
13:01
when we have these groupings, because then we can kind of move them around the way that we need to and apply classes to them as we see fit. And so when we do yield components, we have the same benefits as we did from the first refactor, but then also with these form groups,
13:21
like I said before, they're reusable without being bound to their initial layout. And so like I said before, we can yield these child components and move them around as we see fit, apply classes to them, and add more elements into the block that's being yielded. So this is what our refactored code would look like.
13:46
And so this is kind of inspired. This is how I like to think about components. It also kind of inspired the UI or whatever on this talk. So components should fit together like a game Tetris. Plan ahead how a component fits into the larger application.
14:00
Components might not cleanly stack on top of each other, but just make sure that they all work together even if you do have a couple holes. So now let's talk about data. So here's another activity. True or false, all data should be loaded once the application completes a transition into a route.
14:21
Maybe. True or false, true? False. This isn't a trick question. It's fine. So that's false. So data isn't required by the view, should not be holding up the page when it's rendering. So I think that because of the conventions in Ember,
14:42
it's really easy to kind of get held up by saying, okay, everything gets loaded in the route. We can use an array or whatever, all settled promise or a promise hash and we can just get all our data in the route and then the user has everything that they need. Well, it's not the best user experience
15:03
and I will explain why. So we have switched our example. We took out the radio buttons and we now have a state dropdown. So state, I guess we'll just call it, say US states or whatever, yeah, US states. And so it's an optional dropdown and so we don't have that information localized
15:23
so we need to make an API call. Since it is optional, we don't want it to hold up rendering the component but we also want to make sure that we're fetching it efficiently and we're also handling the error because if somebody does decide that they want to populate that field, we want to make sure that it works.
15:42
Oh, everyone can stretch again if they need to. So we're gonna go through a couple questions now to figure out if we need to, should we load it in the component or should we load it in the route? So is it essential when the user lands on the page?
16:04
Nope, okay, moving on. Is it important in the scope of the route? If it's not, cool. Is it a concern of the component? Yes. And so because we have all these states
16:21
that are concerned with this component which is responsible for creating this dropdown when it's expanded for the user to interact with, it probably belongs in the component. So this is an example of how you could load the data. So we have this will render hook. Really any life cycle hook for the component
16:41
could be useful. Here I'm just using will render so that it happens before the component is rendered and then also will render isn't blocking so if something, if the promise hasn't resolved by the time the page wants to load, this isn't a blocking call. And so, oh yeah, so we're using will render.
17:02
We set the options once the promise has resolved and then we can use just a global service like error handler service to send an error if for some reason the error, the API call does not come back successfully or it comes back with an error. So this is a little bit, adds a little bit more complexity.
17:22
I would also recommend handling retries in the component. Ember concurrency is a good library to use for this. So we use the same will render hook and we can perform a task once the, when will render is being called. We can count, I've used five as just trying five times
17:41
and if it doesn't work after five times then we say okay, this is a failed API call or maybe our service is down or whatever. But if it, for whatever reason it fails after two times then we have our options. So the benefits of this is that user is able to interact with the application sooner which is what we want.
18:03
And then also relative data is handled within the scope of the component and that just means that it's easier to use the component across the application rather than having the data load in the route. Okay, true or false? Ember models should always reflect the backend that persists them.
18:20
True? False. The data store can be used to represent client side state so use that to the application's advantage. So TLDR, just because we have user models and what other models do we have?
18:42
Animal models and addresses, it doesn't mean that that entire database schema has to match what's going on in the Ember application or any application really. So let's say we have our form that we've been building and we can create a model out of that. So we have input, input, input,
19:03
all these inputs are updating our form. We've now simplified the, and let's say behind that form though we have a couple different pieces of a server side model that need to be kind of consolidated for one API call. And so this might get a little hairy
19:20
but if we have this model layer we're able to take advantage of Ember data's model state and so it's easily available in Ember Inspector and we can remove it and do everything that we need. And so this design pattern is otherwise known as a facade. It's basically just taking everything, taking a couple layers of complexity,
19:40
simplifying them for us to use. Okay, true or false? Data should be validated at the model or server level. True? There's no trick questions guys.
20:02
I will tell you why. Data can be validated everywhere. So hear me out. So first let's think about how we would make this decision, this design decision. Is the validation specific to the input type? Is it, if we have a text box and we want to validate
20:21
email address with a regex match, can we do that? Look at passwords. Is the password long enough? Does it have all the characters that we want? Can we do that? So yes, move on. Is the error messaging generic enough to reuse? Can't be blank. I've seen that. That's a super generic message.
20:40
So yes. And that means it could probably be validated at the component level. So I really like using Ember CP validations. It's a very low-level validation library and it's really easy to use. It creates this extension on any Ember object and it works with Ember computed properties to watch any computed property in any Ember object
21:01
and say, okay, this is valid, this is not valid, based on the way that the validation has been set up. I think this is the last one. True or false? Never use two-way binding. True. I'm glad you guys.
21:24
So two-way binding is not that bad. I know that I always tried to avoid it for the longest time. It's like, oh, data down, action up. And so that's not always the case, though, when it comes to forms. And those of you that do not agree, just hear me out.
21:41
So again, we start thinking about what questions we have. Is the element an input? If it is, then you could probably use two-way binding. Hear me out. So we have, so let's take the signup form model
22:01
that we were discussing before, and we have all of these inputs that are bound to the model, this model that we have. The model has state, so we can control that from the application level. We can still validate it at the application level. It doesn't need to be moved to another object
22:21
to be validated. And we can also debug it. It's great because Ember Inspector has all of our models right there, and we don't even have to dig through the rendering tree to see what's available. But I will say the one caveat is trying to handle data that's coming down from a server object.
22:41
And so if you do have a form, and it already should be populated, the one difficult thing is making sure that the form is read-only and that going up, the form does not update the original data source. And so I can give an example of one way that I would solve that
23:02
with an asterisk that doesn't solve all problems, but this is just one way to do it. So we have a form, like we mentioned before, and so we're using a signup form model, and this kind of extracts this application level, or the front-end level code that we want. And so signup form makes an API call,
23:25
and then we can actually, because of Ember data, we can unload that model, and then we have, and we can update the model upstream. And so now we have this, everything kind of goes up, and then once the API call is resolved, then it updates everything,
23:40
and it unloads the signup form model that doesn't really have anything to do with the persistent data. So another tip I have, just kind of concluding that section, is to simplify until everything makes sense. Be thoughtful in your code and design patterns, and there's rarely always cases that hold up,
24:01
except for observers. Never use observers. Okay, so I think this is our last stretching moment. If anyone wants to stand up and stretch, I'm just still gonna keep talking. And so this last part, I'm just gonna talk about how these different patterns, the design patterns and our data management can help us
24:23
with user experience and web accessibility. So I'm not gonna read this whole thing because I think everyone here knows what web accessibility is, but the one thing I want to highlight is that it's not, it doesn't just, it's not just about for people with disabilities,
24:42
it's also about people that are in other geographical occasions that might have access only to mobile phones or slow internet connection. So we want to make sure that we are designing for those people as well. Not everyone has great wifi speed or whatever. So things to remember on those two topics.
25:04
Make sure you're labeling all of your inputs or use ARIA. So again, I won't read this, but ARIA is just another, it's for all HTML elements to make accessibility better.
25:22
Luckily with forms, we have label tags, so we don't always need ARIA. So this is just an example. We have our label tag, and we have our input field, and then, so we're using the for attribute and our label tag needs to match
25:42
to our ID attribute and our input, and then that's how those interpreters can know, okay, this is the label for this input. So indicate required fields and make sure the browser is doing what is expected.
26:02
So this is an example of something that came out of, this is a Google, yeah, this is a Google Chrome, and so at some point Google Chrome just kind of did an update, and so the API for their inputs started showing these required tags, which isn't bad.
26:22
It's fine, but it was unexpected for some users and for some developers, and so just make sure that you know the difference between presenting an error message but then also requiring an HTML element in a browser because that API might be different or it might change and it might do something different.
26:42
Just another example. This is a required field on Firefox if you just open the browser and it already has a red thing around it, so it looks like there's already an error when really the user hasn't done anything wrong. So use logical tabbing.
27:00
I don't know about you guys, but I hate when I tab something or I type something in and then I tab it and I start typing, and then I'm here, and I'm like, wait, hold on, what's happening? Usually this happens when developers have implemented some sort of form and there's JQuery and they intercept the key events and then you've got a tab and it goes over here
27:23
and then it goes down here, and so make sure that you're using logical tagging because that can be really confusing for a user. Yeah, what the heck. And then use thorough and success and error messaging. So users, if they don't receive any feedback,
27:42
it could just be button mashing. We know that from the warehouse because we have applications on iPhones and before when we were first prototyping stuff and letting people use them, you just have it and if something doesn't happen, it's just boom, boom, boom and then, so we weren't blocking ourselves from multiple requests and that's an issue.
28:02
So make sure you are, as a developer, interpreting what a user could do. And so this is also called idempotency and so what we want to make sure is that any input that is given to, in the event of a RESTful service,
28:20
anything that's sent to the server, we want to make sure that it is, if we send the exact same response multiple times, that it is also not making multiple requests. I got this from Wikipedia. And so just back to the user feedback part,
28:40
this is an example of blueapron.com and how you can, you have a modal that pops up and a user can probably change something and then we could just save it and close the modal but blueapron does like a toaster message so that it says, okay, we already saved it so you don't need to update this again.
29:05
So I hope that you will join me in thinking that forums aren't that bad. There's actually some interesting stuff that you can do with them and there's actually, this is just the tip of the iceberg. I know that's a really cliche phrase to use but there's just so much more to know
29:22
about the different patterns that make it easier for your application and for users and so if you have resources, this is just a couple resources that I have. These are two really good past EmberConf talks, the Mozilla web docs and if you want to tweet someone, send a random tweet out into the Ember ecosystems,
29:43
someone will respond to you and ask you why. And yeah, that's it. Thank you. Feel free to tweet me if you have any questions.