 I'm Ben. I recently moved back to England after living in San Francisco for a while, so it's really nice to be back, see some familiar faces. I want to talk to you today about forms, mainly because forms are hard. We all make really bad forms, let's admit it. I personally believe that making forms in Ember is a bit less scary than how they normally are. I work at a company called Zesty in San Francisco. We do corporate catering and corporate wellness, and as a consequence of that we're managing a lot of data, because we're largely just like a logistics company. We're also a startup, so we're changing our opinion on what we need to do all the time. We're always building new stuff, so we're constantly compromising on the quality of what we do. The combination of having to manage a lot of data and change our mind all the time is that we make really, really bad forms. This is something that interests me personally, because we have a lot of good engineers. We have a lot of good intentions, but we still make stuff like this. This is how you make a menu in our interface. You can submit your availability, like, hey, I'm free to work on Tuesday. We can do mobile as well, and we've got some pretty stuff as well, especially for client-facing or anything, a large majority of our users are internal. If they have any problems with the form that we've built for them, they can just come over and grab our ear. Not so much for, like, somebody who's a cook is not going to be like, we have a lot of tech-savvy clients, we basically feed techies. They always complain, but cooks, you can't expect very much of them necessarily. When somebody asks me to make a form, I normally worry about these things. I get ember data hooked up to my API. That's fun. Maybe I write an acceptance test, but that's not even guaranteed. Once I've done those two things, I'm happy. I'm like, sign it off, ship it, let's go. Move on. This is a form that I made recently that I was happy with. It's basically replacing something that we used to track in the spreadsheet. We used to get a text message about it, and then somebody would put it in the spreadsheet, and then that would get sent to somebody else. We're like, no, we need to track this internally. This is when somebody spends a bit longer than usual on a job, and they need to ask to get paid more. I set up an interface like this. It was like, tell us who the captain is, put in the reference for the job, and then what's the time that we're actually talking about? Because these are for internal users, I call these super users because they spend the majority of their life dealing with forms like this. They also need to repeat, repeat, repeat, repeat using this form. If the form is bad, it really sucks for them, but it's a completely different story when you think about how the user's operating. I was listening to a podcast recently that encouraged you to think of the user interface as a traditional story, as in storytelling, beginning, middle, end. This one doesn't really have a beginning or an end, you just keep doing it and doing it and doing it. The thing that the form doesn't do, and there's quite a few things here, and I realized this once I shipped it because I started getting people complaining at me about how lame it was. It didn't have any validation on it, so it didn't tell the user when they had entered something bad in, when it wasn't valid, the backend sent something back, but I didn't put anything into the form to actually display that to the user either. That sucked. I also didn't really put any instructions about what the different variables in the form are and what we intend to do with them, and I didn't help the user know about how far through the form they are in or when they click the submit button, it doesn't really do anything. These are things that I think forms should do. I forget to do this all the time. I want to set a standard. If somebody is merging poor requests in my company, we make sure that we thought about some of these things at least. It's never going to happen. The case that I'm trying to make today is that Ember really shines when we're building forms, and you might think that that's an overstatement, it probably is, but the things that I think I really like are that I can do things in Ember that I can't do normally, and a lot of this is down to the fact that we have, it's extremely easy to set up data bindings and manage where your data is going. There's a really excellent ecosystem of components and libraries, and the core stuff, like Ember data and Exidery stuff, like Ember concurrency are really fantastic, and they set up a really solid foundation. You might say to yourself, okay, well, forms are such a fundamental component of a website. Of course, a framework should be able to allow you to make forms in a good way, but if you look at Rails, for example, that's clearly not the case. If you've ever tried to use any of the form helpers in Rails, it's just like you end up worse than you started with. So I've got a couple of cases here that I'm going to explore, starting with really basic stuff, so bear with me. When the API is upset, the save hook that you've got on an Ember data model is actually a promise that you can start tapping into. So always put something like a then and a catch on it so you can stay on the page, present validation messages that you get back from the API. If you're using Ember data, it has an error's object built into it, and if you're using a standard adapter or serializer, this will hook straight up into what you already use. So be aware of this, it's pretty cool. So for example, I've got this field here at the end of my form, so why do you think you need to be paid more? And maybe the validation here is that we have to fit it in. So I get this reason back from the server, and I put it straight on the page, and I can even highlight this input, put some styling on it, and say, look, it's invalid, please have another go. But my issue here is that I can start typing into this form, so say for instance I forgot my keys. But I've still got the validation message here, and it's kind of, it's not updating at this point. I mean, yeah, it's not ideal. One thing that I've learned, especially when people are relatively new to the software is that they want to get their validation immediately. Like from a point of user interface, I was building that recently where a chef would have to take a recipe that they've got, say, written down, enter it, and then I'd take it and validate it, give it back to them, say, here's all this stuff I want you to fix. That totally didn't work. I thought that was a good workflow. But actually, it's much better to have every single time they're entering anything in. They've got that fresh in their minds. They're dealing with it right now, so tell them straight away, this isn't correct. So this is where data bindings is really great because I can just start typing here and be like, okay, I forgot my keys, okay, things are not a good enough excuse. It needs to be a bit longer, right? Okay, maybe it's a bit too long. Whatever. The other thing that's really nice here, so for example, this is part of that super user context here. We've got these inputs here for start time and end time. If you've ever tried designing anything that accepts times or accepts some kind of calendar thing, it's chaos. And really, the user in my case has a number like 905AM that they want to just type in. So if I just start typing in 905, it's like, oh, no, that's not cool. But whatever. So this is going to give you an idea that we've interpreted the data that you've provided in the way that you mean it. I can then say like, hey, it's going to go from to 12 and it's like, oh, this is approximately three hours, right? You see it's interpreted as 12PM. I've not typed anything else in there. So giving the user some feedback as to like, this is how we're intercepting, like how we're interpreting the data that you're giving us is also really good UI. I can take this example a little bit further. And the way that I'm presenting it in this format is not particularly compelling. But that's because I guess I deliberately made it really ugly. So the idea here is that I can take that data and essentially on the right hand side of the screen I've got a document that is updating with all of the information about this time request. So it's like we're really filling in like a paper form almost. So that's the context there. And this is something that I've been playing around with. I don't think it's perfect, but it's much better than just like, there's all these fields that I don't really understand and I'm just filling them in and like having a go. I don't know what got sent to the server. I don't know even what got saved in the server necessarily because it just took me straight back to the form. So this is like a slightly better opportunity to review what we've entered. One of the things that I struggle with a lot, especially when it comes to the scope of like when you're entering stuff in a component or even in a controller, those have different scopes and it's something that trips me up all the time. A lot of the time, because it's like more structured or more like well bids out, you consider the server as a data store, right? Because it has a database and it has the word data in it, right? Ember data also has the word data in it. And this represents kind of global app state and normally I think when you're thinking about building an Ember app, you're thinking about like, oh, I've got here's the data that I've got on the front end and here's the data that I've got on the back end. But in practice when you're building forms with Ember, you're going to have some data that's like stored on the controller as well. The controller is like managing the state in your actual page, right? There's also data that's actually in the DOM, so like in the text fields, right? So you could consider them as four stores, like I consider them as three. Now, when you hit model.save, so you're using Ember data, right? That's going to take the stuff that you've got in Ember data and sync it with the server, right? When you do model.save, whatever, that's taking the stuff that's in your controller or in the DOM and putting that into Ember data. The trouble is Ember data more or less represents like global state in the app. So if you've ever started like entering data in the field and then like navigated to another page or even if you've got say like an index root sort of thing on the left-hand side of the page and an edit form on the right-hand side of the page, when you start editing in that form, you are updating fields globally across the app. And that's kind of crazy because you really want, if you've got like a save button at the bottom of the page, you want to wait to like update the rest of the app and wait until you finish with that form before you update the rest of the app. So how I normally do this is I just like set random attributes on the controller. And the cool thing about this is it makes it really easy to like set up computed properties to do validation. This is a bit hacky and you can very quickly get lost in like all the different permutations of the word like reason value and reason text, all this kind of stuff that's kind of nasty. So maybe like eventually you start to set up like a framework of your own for keeping track of the changes that you've made in a form. So maybe you set up like a pojo that represents the request form and you've got a bunch of these different form objects set up in your app. I think it's a totally cool way to solve this problem. Maybe you start setting some kind of methods on it like is the form valid for example? Is the form ready to submit? Let's reset the form. Let's commit the form. Let's save it. Of course we've already got that. Somebody's already done this. There's an add-on for it. It's called ember change sets. There's other add-ons out there but I like this one. So the idea here is that you take the ember data model and then you create a change set for it. The change set is then the thing that you interact with setting variables and when you're done with the form you commit the change set and that updates the model. So in this example the top thing is the change set and the bottom thing is the model, the ember data model. Then I can start like editing the change set in the form and then when I actually hit the save button on the form it updates the ember data model. So that's really cool. The way the change set does validation like kind of add it in is that it allows you to provide this generic validate function. It will give you what the key of the thing you're validating is what the new value is, like how it's changed and that's kind of a bit low level. But the reason for that is because they provide also an accompanying library called ember change set validations which hooks in really nicely but it means that it's decoupled as well. So if you want to like put something else in like some other validation library that you use you can do that. So this is really nice. I want to bring this out as a standard across the entire company. Again this is like touches on the engineering problem of like how do we make sure that people actually do a good job when they're building forms. This is one of them. You just like tell everybody use this. Another problem that I often forget to address that is extremely easy to address using ember is the idea of showing people how their request is doing. So if I click on a button you need to give them some visual feedback to say this button's done something, it's doing something, it's working. This is where ember concurrency for me has like provided the most value. It's really good. Like the tagline for ember concurrency is that when you're worried about like switching components, switching controllers, like when your component dies what happens to the task that's a member of that component? What do you do with that task? Well with ember concurrency you kill the task. But for me the value add for ember concurrency is that I can say in my template the status of the task. So for example I can set some like progress winners and then be like hey the request like came up with a problem so like do some stuff right. I'm a big fan of putting complicated stuff in your templates. I know a lot of people don't like that, but that's how I roll. Another thing that I actually heard, I was like getting ready to do this talk and I saw there was a podcast about doing forms and they were all like yeah, autosaving. I've never tried doing that, it sounds way too scary. This is one thing that actually ember concurrency makes exceptionally easy. Like, it's shockingly easy. The big thing with doing autosaving is you need to know when to like actually perform a save. You want to hook it up to like on input but you don't want to send something to the server every single time you type anything. So you need to introduce a debounce, right. So the way you do this with ember concurrency is you yield at the top you say yield for a second and yield in ember concurrency it's using async generators it pauses everything, right. So it's going to wait until that timeout has finished or whatever it is, if it's a promise for example it waits for that to finish before executing the rest of the block. It actually means that you end up with really tidy code as well because then you're not covered in like then blocks everywhere. You just do yield this, yield this, yield this. So in this case you yield you wait, you wait for a second and then you start actually sending off the the save request and then you specify that the task that we're dealing with here is restartable which means that when you start triggering the task a second time you like shut it down, you're like let's begin again. So in this example we wait for a second and then it starts performing the save. So if I'm editing it's like waiting to do that debounce I'm typing stuff here on the keyboards that it starts performing that server request. So all of a sudden I can start making my apps just auto-save so they feel a bit more like working with like OSX applications or something like that. I have some words of warning about making auto-saving apps though. The first is from an engineer's point of view like often I make a form I know it's auto-saving, I know it's saved or maybe it doesn't even send anything to the server but it's stored in the data so I'm not worried about it. So the form submit button just becomes the close button for me, in my eyes it's just like close it down. But then the user is just like where did my data go I spend all this time filling this in where's my save button so often I put in a button that doesn't do anything. That makes everybody feel okay. The other thing is that you end up sending a data a bit more frequently than you normally do. When I said to one of the other engineers that I was going to give a talk and then invited him along he's like I don't know anything about Ember but I'd like to come along. The only thing he knows about Ember is that it just sends a load of like posts like really big posts just sends loads of them and it's because he's dealt with one of my terrible apps that auto-saves. I didn't like that very much. A final point that I want to make is that besides like the ecosystem of tried and tested solutions to common problems and core libraries Ember also makes it really easy to just try stuff out. Roll your own stuff. Let's see how it goes. So one thing that I've been interested in is taking a lot of inspiration from mobile development. Now often mobile development have good form experiences because often with the screen space being constrained you can only do one thing at a time. Often when you're building forms for the desktop on the web you end up just like being like oh I've got all this space. It's like just because you can doesn't mean you should. Put everything on the page. Why not? So I'm interested in seeing if I can make a form that looks like lots of different pages but it's actually just one page, one template. I don't need to worry about lots of routes, lots of controllers, lots of templates. Just one template and then I can section off bits of the form in like a workflow. So I made this component it's now like a shared component that anybody can use on any of our apps and the idea here is that you set up these sections and the section gives you a method that you can call to say hey progress to the next section in the form. So I'll show you what this looks like. So for the last form how I ended up redesigning it was I set it up so that once you selected the captain it would then show you the next section of the form. This is okay cool. You gave me the captain and now I've actually loaded up a list of jobs that that captain worked on last week. So tell me which job it is. This makes it a lot easier for them. And then it goes on to ask you about the times. So my final point here is these two things are things that I really suck at. So I want to know how to do it properly. If you know please tell me. One is forgetting that controllers live forever and components die very quickly. That's something that I have a lot of trouble with. So often when I'm developing the form I test it myself and I just test it by looking at that one page but then I don't go and navigate around the app and then come back to the page and see oh my god the data is still there. Oh my god the data isn't there. These two cases are not things that I ever consider and it ships and the user tries it out and it's a horror story. The other thing that I'm bad at is setting up listening for keyboards. Sometimes you want to listen to the keyboard or like an enter command. You're typing in an input. You want to hit enter and it to submit the search whatever it is. So that's scoped to just the input box. But then often I also want to listen to a keyboard more globally in the application. Like with this thing for example I'm listening to the left and right buttons and it actually doesn't like that at all. So sorry if you check this out I clearly haven't mastered it. The trouble here is you want to similarly to like the motivation behind the concurrency in the same way that you want to chain the lifetime of your task to the thing that like owns it you also want to chain the lifetime of your listeners to the thing that owns it. Otherwise you can get to other pages and you've leaked a listener out there and it's like listening to that right key that you're pressing and it's trying to do other stuff. So that's something I'm also bad at. Yeah, thanks for listening. Do we have any questions? So for anybody that didn't hear you're talking about ember state services, right? Yeah. So this is cool. This is like a similar solution to the problem of dealing with like I've got this form, it's got a state to it. How do I manage that state? So the way that this gets solved in this solution, my understanding is that you set up a service which is also like a global single term and that looks after the state of that particular object for you. So yeah, if you're interested in checking that out, it's called ember state services. Yes. Yeah, that's another problem. I mean I only do this like surprise trick to like internal users because then you're like more likely to have them trained into like the way that the form works. Right? So maybe the first time they do it they're like there wasn't a save button or like I didn't expect that to get saved. They're like, I can show you the form that we're actually talking about here because it's really horrible. It's one that like the analogy that I used today to describe it was it's like a kombucha like mother you started off with really good intentions you're like I'm going to make some nice kombucha but then you know it kind of grew a bit too much and started getting a bit stinky this is something I made a long time ago and like so somebody's entered some really nice data this is the name of a dish here and this is a form that auto saves and you'll be glad that I'm zoomed out so you don't have to like see all the horror details but yeah, it's a really big form and everything on this page auto saves. I like this button at the bottom that one got snuck in without me seeing it's good UI Any other questions? Great, thanks Ben. Thank you