 how to start these things. Thank you all for coming to the 75th JavaScript Talk at RailsConf. I have seen some really great presentations this week so far. Did anyone see the Three Mile Island disaster presentation? I really liked that one. I had really great slides. My slides are not great. When I introduce myself, my name is Graham Kanzit. I've been a software developer for 10 or 15 years. I really want to leave time at the end of this for questions or discussion. I've run through this presentation a couple of times and it's always right up until the wire so I'm going to try and go quickly because I want to talk to everybody. I don't do Twitter's but I do have a GitHub and because it's RailsConf, this presentation is actually a Rails project so you can clone it right now and go follow along. It'll save me some time for having to alt-tab between demos and slides. It does mean I have to click the buttons at the bottom though. Hopefully that makes up for my bad quality slide layout. Okay. So before we talk about what I mean by old school JavaScript, I wanted to provide some background as to why you might want to go down this road. This is not a modern JavaScript sucks talk. It's not like React is terrible. It's more about picking the right tool for the right job. There's no silver bullet. I'm actually heavily involved in the React user group in Columbus and use it on several large projects. I should do like JavaScript. So story time. I work for a company called Orange Barrel Media. They're out of Columbus, Ohio. It says advertising in there and when you think of advertising in the tech space, you probably think of Google, AdWords, that kind of thing. But we're actually a large format outdoor advertising company. We're also hiring. You're a kickass front-end developer. We're looking for you. Come see me afterwards. So you might be wondering why a outdoor advertising company would deploy software developers. That's because we also operate and have these interactive kiosks. And they're kind of like giant location-aware content aggregators. You can think of the giant iPad sticking out of the ground. But it does have local content and events that the cities manage and coordinate. And the little operating system that you see, maybe you can kind of see on the screen there is just kind of the tip of the iceberg. We have lots of content. There's lots of content that cities and admins have to manage. So story continues. Recently we've had a lot of growth in this area and we have kind of an old couple-year-old Rails app that served the back-end well when it was really tiny. But our team is growing and the Rails back-end wasn't quite cutting it anymore. You're using an admin framework and it was good because it let us focus on features, gave us a consistent user interface, some nice UX niceties, and by niceties I mean things like drag and drop, undo, copying, those sorts of things. But it was becoming really inflexible for what our users and admins wanted to build. And another, you know, there's other sad things about it too. Like we have lots of dependencies and plugins and it's hard to upgrade Rails when you have 15 admin panel dependencies. So we went looking for something else and we wanted to still keep all the stuff we liked about the admin framework, the top three green checkmarks, but see if we can do better in the other areas. So after going back and forth, we're like, let's just use Rails. So this is Rails comp talk, that makes sense. Rails provides a lot of what we need out of the box. So here's my little vanilla ice cream cone there, just plain vanilla Rails. It lets us focus on features still and by features I mean the stuff that ends up in the operating system on the front end of those kiosks. It provides a consistent user interface in the form of whether we're bringing in like a bootstrap or a component library or just relying on partials. So that's nice. And obviously it's flexible and we can build anything we want for our users. The question mark was around these UX niceties. If we're just sticking with vanilla Rails out of the box, how do we get those nice drag and drop, you know, that sort of whizzy wig style stuff that users have come to expect really. So we were wondering how far we could get with just the JavaScript library that Rails itself provides. Modern browser support has come a really long way and you might not have to bring in a React, an Angular, what have you to get the job done. So what do I mean by old school? This is what I have dubbed the sprinkle continuum of JavaScript frameworks. And in a nutshell, since we're just using the out of the box UJS library that Rails has to provide this functionality, old school in the context of this talk is about using the Rails request response lifecycle with some JavaScript that's executed when the page re-renders to get what you want in terms of those UX niceties. So we're not even using jQuery, as you can see. There's nothing that stops you from using ES6 or Battle or anything like that. And even ES5, it's more about leveraging some of the modern browser technologies and it really depends on what your target audience is. So I'm going to call it yes, what works in your users' browsers or whatever works best for your users. Some of you who have used Rails for a long time probably remember RJS and that was kind of dropped or supplanted by UJS and I won't go into that too much, but basically that was writing Ruby code to execute JavaScript on the client. And we'll be doing a little bit of that in these demos. All right, so this is a basic example. This is sort of a contrived version of what one of our admin panels for displaying posters that rotate on the front of those kiosks. Basically we have a playlist, is what we're calling it, and these posters inside of it are called playlist items. And basically they get a duration and an order for how long they show. This is just a very basic HTML version, we're going to be building on it throughout the talk. So select list, you can pull up things to do, add it to the list, you can delete it, and you can see that the page is refreshing each time. This is all just vanilla Rails under the hood so far, just basic model view controller, nothing fancy. So take a look at the controller real quick just to prove that, you know that probably not. So this is the same controller for all the examples, and the only thing we really added is this format JS call here in our respond to block. And that's just because by default in Rails, when you're using these remote asynchronous actions, it'll just call the HTML format block, and we want to do special stuff. So this should all be fairly familiar to everyone, there's some to-dos in there, but it's just a few basic actions that now have the format JS block on there. All right so I kind of lied in the first example, remote create is actually in Rails by default now, when you use form with in Rails 5, you actually have to specify a local true in order to make it just use basic HTML post anymore. So while that page was actually just a post redirect get loop, we had to make it do it out of the box. So if you want your form to be asynchronous, don't do anything anymore. It used to be you put a remote true in there, but form with does that by default. Okay so let's look at stuff that is new in here. Taking a look at the create JS ERB file that comes back, we're basically going to render our playlist item, and you'll see that special J character there. That just is a escape the shortcut for escape JavaScript, you may have seen that before. So that's going to build HTML that allows it to be inserted into the page when the request comes back. We're going to assign that to a variable, and we're going to stick it into the end of our playlist items list. You notice that we're not using jQuery or anything here, once again browsers have come a long way. So we can leverage things like insert adjacent HTML and things like query selector. So at the end here you can see we're just adding the nicety of clearing the form once the user has submitted it. So let's take a look at what that looks like. It'll look pretty much like the other thing except it's a little bit faster. Partially this demo is running locally, so even the HTTP version with the redirect is going to be fairly fast. But basically what happens is that response, that view gets executed and gets inserted into the DOM. You've probably seen this before, and I promise we'll get to more detailed examples of leveraging this, but you can see when we destroy it, it's still synchronous. You saw a little flash of the page there. We're going to fix that up next. So looking at remote destroy, this is another one that's fairly common if you're reading through the Rails guides. We're just going to change our delete link that we have in our partial there to have a remote true, and this will submit it via Ajax now with a media type that lets it know that it should respond with the JavaScript partial. So that's all you have to do there. We added a little bit of disable width stuff, and we'll go into that a little later, probably familiar with that too. So let's take a look at the actual view that we're rendering for our destroyer. And we're going to introduce some helpers here. And once again, we're using all of Rails, all of Rails Buffalo. And we use these partials quite a bit in our application. You know, this hide, once it's deleted, is going to hide the item in the list, and then we're going to render a flash message. This is the part that I always consider, this strategy is most similar to RJS of old, and that is that we're actually using Ruby code to do JavaScript things. In this case, it's just helpers and not Ruby that's been transpiled to JavaScript. So you can see here in our application helper, we've got this hide, it's just returning a raw string that finds our model by the DOM ID and sets it to display none. DOM ID is a helper included in Rails, if you're not familiar with it, that just constructs an ID based on the model name and its ID from the primary key. So now, if we add our new item here and we can delete it, and we'll notice that we have nice flash messages and everything deleting quickly. Once again, might be hard to see the difference because we're localhost and fast, but it is much faster and faster for the user when they're working in there. All right. So the one thing we did change is we added this render flash method as well. And I think this is worth pointing out because this is a pattern that keeps coming up where we're sort of sharing a partial between an HTML view and a JavaScript view. And this is useful because if somebody has JavaScript disabled in this day and age, I don't know why they would, but it's a possibility. Or you just have pages that you might not necessarily want to have this functionality. You can still share the partials between your code for the JavaScript rendering or a server-side rendering. So render flash just takes a partial that we've extracted and a core exploit and render it and insert it into the flash container in the layout. The flash container also calls the partial here in the HTML. All right. So we're continuing with the CRUD theme. This one is probably the most straightforward, but I couldn't leave it out since you're already doing create and delete. And it's not really shown a whole lot in the Rails guides, but basically we're just changing the duration that we saw on the table to a form that we can update. So we're just putting inline form there. And you can see that it's doing its remote thing. It's updated. And I can refresh the page here, show you that it actually updated. And don't fall asleep, I promise there's other cool stuff. So we did add a view for the inline update, and basically all that did is render our special flash message to say, yeah, it was updated. The only reason I point this out is that there's, we don't always have, it's not always common to use views for things like create and update, especially if you're coming at it from a JSON API base where all there's usually either like an empty body that's rendered or you might just render the show view after you create, but we create all kinds of different views for doing the different stuff you want to do based on user action. All right, finally something a little different. We're going to look at soft deletes. So I'm going to take a little bit of a digression here to talk about soft deletes in REST. And I mean, this is Rails after all. So we're embracing all of Rails, including REST and HTTP. So we're going to be adding a destroyed playlist items controller. And the reason we're going to do this instead of just having an action that deletes or undeletes is that we kind of need to respect the 404 in HTTP. If we delete an item, we shouldn't be able to go back and refresh it and have that ID still return 200. It should be gone. However, how do we delete it via or undelete it via REST API if, you know, we can't find it anymore? And the solution that I like is just to add a different controller, a different route, basically treat these as different resources now and consider them deleted playlist items. If you have an undelete functionality across the bulk of your app, you might have numerous undelete controllers. And DHB Proud, no SQL here. We're just finding all of the playlist items that are not destroyed to set and the playlist item. And then in our strong parameters, the only thing we're permitting is a destroyed at timestamp. Now, probably want to make this a virtual attribute in real life, but for the sake of the demo and simplicity, just went with that. So the undue functionality itself in the UI, basically when we destroy an item, we're going to modify our flash message actually to return a special button. And what the button does is it will actually be a remote function that updates the record. And you can see that here highlighted in the second argument here. We're going to be passing destroy that nil to reset and essentially undelete the playlist item. You'll see we're passing a method patch. So this link essentially updates it. And then if you remember back to our render flash message, there was a, you could pass in and override the partial that you were passing into it. So here we're sending a special undue partial, which includes a link that appears over on the right to do this. We'll demo that here in a second. So we can kill off cityscape here. And you'll see that we now have our undue method rendered here. We'll get that. We'll say undone back in there. It's set a patch. It's updated the record. And it's provides very nice user experience similar to like a Gmail where you undelete something. So so far we have quite a bit of nice functionality going on with very little JavaScript written no frameworks and not even any jQuery. So if you notice the flash message there, we have it's saying specifically undone. And because we have a unique controller representing this action that gives us the opportunity to sort of customize what we say in response to a user doing this. So in the update view for the destroy playlist item, all we basically do is say it's undone and then we're going to call our show helper to basically unhide or reveal any deleted record that was hidden as part of the delete. So that's why we hid it before instead of actually removing it from the DOM. So the next thing we wanted to bring over into the CMS was this notion of copying. There are a lot of repetitive tasks in here and wanted to find a way to make sure that users could perform those actions easily would not have to repeatedly type stuff in. So this is the first time we're going to dip into sort of the model layer to add some functionality. And basically we're going to add a simple function that returns a hash of attributes that you want to copy. And you can actually add this to your application record as well and just by default grab a model's attributes and get rid of the timestamps, get rid of the primary keys. And that's usually good enough for most of them. And you can override that in your different models as needed. In the case of our playlist items, we just need to copy the duration and the poster. So the changes that we'll make to the playlist item itself, it seems a little complicated, but we basically just have another button that we're creating a record with an action here. And we're passing in those parameters that we're grabbing off of our record to copy. Once again, just changing the method to create because all we're really doing is creating a new copy of that record with the attributes that we've specified. So this is really simple, similar to the undo functionality in a lot of ways, just a different set of parameters that's dynamic and a different method. So we've added the button here, copy it, copy, copy, and see our delete functionality still there. Oops, it went too hard and undo. So a word of warning on the copying stuff, obviously this only works in cases where you have a very limited number of attributes. It's very easy to go overboard, especially with deeply nested structures and start ballooning the front end HTML code for every item there with all of the potential properties you want to copy. So use judiciously, but it can provide a really nice user experience. Okay, so get into reordering I mentioned. And this is the first time we're really going to do some more custom JavaScript here. And I focus more on the approach than the JavaScript itself. The playlist items, there's a requirement that they need to be reordered. So we could reach for a plugin, a jQuery or something like that, or build something crazier in React. But as we mentioned earlier, browsers have come a long way, it's 2018, and we have a lot of built-in functionality for dragging and dropping. So we're going to follow kind of a similar pattern that the Rails UJS library itself uses, which is adding additional functionality to an HTML element based on data dash attributes. So here we've given a URL to our table body of playlist items here, and that's going to be used by our JavaScript code to know where to post this reordering update to. You notice that this, in the back end, we've been ordering these based on position here. The ordering is actually done on the server side. So we already know there's a position attribute on the playlist item. And we won't forget to make our playlist item itself draggable. So here this works. You can see that they're now draggable and properly return a reordered flash message when you reorder it. So this is one of those examples that wouldn't really be possible without the usage of sort of that request-response-life-cycle pattern. And that's because when you update a playlist items position on the back end, we're going to make sure that all the other playlist items are kept in order and updated as well. So when you reorder one of these, it's going to re-render the entire table to make sure it's consistent and in sync. Now that's one way to do it in several other ways, but you often run into issues doing client-side reordering. Things can get out of sync. Someone can come along and update something on the back end while you're working on the front end. Different things like that. So while it's less performant to update all the records, if you have a limited subset like we do, it could be nicer to do it on the back end and just re-render this whole view when you change one record. So that's what we're going to look at next. So this is a little bit more verbose than it needs to be, but I kind of left it in because I wanted to show that even when it's bad, it's not that bad. So we have this an if-else block here and basically the if is going to check to see if that record changed or the position changed after it was updated. If it has, we're going to re-render the entire list of records and then stick them into the table so that we know we have a consistent view of all the records and their order. So if somebody came along and updated, if two users were in the system updating something on the back end and somebody reordered something, the whole list would be refreshed and they would make sure they would see the correct order. Down here at the bottom, that part of that if-else block, basically if it's changed, we're going to render a custom flash that says it's reordered and if not, we're going to ignore it and just say it's updated. For example, if we updated the duration. We do also need to call make re-orderable again in our implementation and this is because we've replaced the entire DOM element here. We need to make sure that our hand-rolled UJS function goes back and applies all that stuff to it again, which is what we'll look at next. The actual re-orderable JS code, this is mostly just to demonstrate the boilerplate that you need to set up for drag and drop in general in HTML API is a little weird. So basically this make re-orderable function just finds that data dash re-orderable and adds the requisite drag-start drag-over and drop events. You see that on drop we have a reorder here. That's a function that's not shown in this page, but all that really does is kick off the Ajax call to the back end to update that playlist item and we're actually reusing the Rails UJS Ajax wrapper there, but I didn't have space to squeeze it in here. But you have the code so you can go and look at it. So this is getting towards the end of what I wanted to demo. I did have some, I cut some of this stuff for time because like I said I wanted to do questions and discussion and go over a few other things before I wrapped up. I did start going down the path of trying to implement a combo box with HTML5 data list component. Love to talk about that. It's kind of interesting, but the API is also a little strange. It's not exactly like a combo box. So there are some things you have to change and fix up on the back end in Rails to kind of make it work, but you can still provide a nice out-of-the-box kind of type down Ajax based experience that users have come to expect there without bringing in plugins or libraries or anything like that. So leaning on standards once again. So the next piece I wanted to touch on before we move on, and this is one of the shortcomings of using this request response lifecycle format is that you are going to the server every time for making round trips to update your client page. And that can be very slow and while we're on a fast local network here, it doesn't always, it doesn't look as sharp and as crisp if you're, you know, on a bad connection or a noble bad network or anything like that. However, in adding a lot of the helpers and the UJS stuff with the remote creates, we did take care, you may have seen, to add some of the data attributes that sort of replace text and let the user know that there's an action happening while we're doing these changes. So let me just go ahead and drop my network down here. Slow 3G sounds about right. And now it'll make my actual slide transition slow. So it'll see when we deleted, it was probably too fast to notice before, but oh, that was unexpected today. Did I closing DevTools do that? Hold on a minute. So deleting something now on a really slow 3G network, you can see we added three little dots that could be a spinner or an icon or something that says loading. But it's important to give that feedback to the user because while most of the time it's going to be fast and responsive, there are going to be times where it isn't. So the nice thing is that Rails takes that into account with a lot of its UJS stuff. And it's just important to remember to add all those to each one. So the last thing I wanted to talk about is what we didn't do and showcase in here. Obviously a lot is the answer. But that's kind of why I want to talk with people and have some discussions around what are some really hard, you know, UI, UX stuff that we think we can only do with single-page apps that we might be able to do with this sort of old-school style request response lifestyle cycle JavaScript. Obviously single-page apps or many single-page apps within a larger app are still king in terms of rich interactivity. If I was going to build Photoshop in the browser, obviously this would not be the way I was going to do it. And you did notice there are a few other things that we didn't touch on that are still covered under the request response lifecycle sort of approach. And that would be things like paging, sorting, and filtering. And intentionally omitted those because there's a lot of edge cases that happen around merging parameters or filtering and sorting, messing with the state of the URL and the address bar. And I've gone down that path and it just does not work out very well in terms of providing a better user experience. So manipulating that all in JavaScript doesn't work as well or it's harder to do than the other stuff that we demoed here. So recommendation there is to probably just use a regular old posts or get to the server and rely on turbo links to smooth out some of the performance there for you. It's much easier than trying to merge in all those parameters and keep track of paging and that kind of thing. Okay, that is all I have and I managed to be under time actually. So thank you very much for watching another JavaScript talk at RailsConf and please let me know if you have any questions. Thank you.