7 ms·
Live previews with Rails and Stimulus 2
- 023984398 6y agoThis is cool but its not using hotwire (turbo frames / turbo streams)
- strzibny 6y agoIt does! Stimulus 2 is Hotwire too[0]. Turbo seemed more connected with existing models, and so I chose a Stimulus controller instead. But maybe I just don't know Turbo as much yet. [0] https://stimulus.hotwire.dev/ https://stimulus.hotwire.dev/
- oh_boy 6y agoAt least in the non-rails environment, Turbo and Stimulus are two separate libraries and have to be included individually.
- edwinvlieg 6y agoThe whole idea of Hotwire is to prevent `Rails.ajax` calls. You can easily accomplish this with less code: 1. Replace the `output` target with a Turbo frame. 2. Add a value to the `data-controller` div with a `preview-url`: https://stimulus.hotwire.dev/reference/values https://stimulus.hotwire.dev/reference/values 3. Change the `Rails.ajax` call with `let url = URL.new(this.previewUrlValue); url.searchParams.append('body', this.tweetTarget.value; this.outputTarget.src = url.toString();` 4. Change the `preview` action to render HTML with the same frame. Now you have an automatic refreshing frame with less code.
- strzibny 6y agoPerfect. I will try it, and perhaps update the post with this approach as well. Thank you. I haven't done anything with Turbo yet, although it's my next step. I first just tried to refresh my Stimulus knowledge with the 2.x changes. But as for the AJAX call, I still think it's quite simple solution that most will instantly understand (and that's a good thing).
- clairity 6y ago> "I will try it, and perhaps update the post with this approach as well." this would be very useful, as there are not many comparative articles that really help you choose one approach over another. a number of years ago, i went through a couple rounds of turbo vs. ujs vs. websockets vs. custom js vs. something other library i don't remember atm, to try to figure out the best option for the app i was working on. lots of articles talk about the strengths of a library compared to others, but almost none walk through an example (or two) with enough depth to show those differences explicitly.
- strzibny 6y agoI included a second example with Turbo Frame that hopefully shows how Turbo Frames work.
- JeremyNT 6y agoThere are a lot of ways to skin this cat! You can also have a hidden form submission that does this from the existing controller so you don't need to specify the URL. For example, you can add a hidden submit button like form.submit 'preview', data: { composer_target: 'submit' }, hidden: true Instead of a stimulus target, you wrap your preview area in a turbo frame <turbo-frame id="output"> ... </turbo-frame> In your (ruby) controller you can re-use the existing controller action: def create @post = Post.new(post_attributes) preview && return if params[:commit] == 'preview' ... end Do the turbo junk in a private method for the preview: private def preview render turbo_stream: turbo_stream.replace( 'output', partial: "posts/preview", locals: { post: @post } end end Your stimulus controller now just does this: preview() { this.submitTarget.click(); } I'm not sure which I prefer!
- strzibny 6y agoYep, that's what actually cross my mind first. In the end, I wanted to avoid depending on the model, but it's certainly interesting.
- JeremyNT 6y agoOnce the complexity ramps up and the side effects compound, that's when I really appreciate server side rendering. And incidentally, that's exactly when I like to have the whole model! I just refactored some old jquery stuff to use turbo and stimulus on a really complex form and it turned out well, I think. But below a certain level of complexity, it would have made sense to just do everything directly in stimulus with no AJAX at all. I know your preview example is contrived (it's tough getting a real-world representative demo into a blog post, so please don't read this as criticism, I don't mean to say it's a bad example and I'm not trying to pick on it!) but really simple use cases are exactly the "sweet spot" for doing everything directly in the browser using Stimulus without having to call back to the server. Only once you start piling in business logic or reshuffling major parts of the display around does it fully pay off, and that's also about when you'll start appreciating having the whole model and the ability to re-use partials (rather than having to pick off a few pieces manually). EDIT: And just to be clear, in the sample code I put in above, you don't actually have to use the model. You can always just shove a single parameter into a special partial and go.
- 023984398 6y agoI mean technically they are installed but the example is still using UJS and not the functionality provided by the hotwire libs.
- strzibny 6y agoYes, I was only focusing on Stimulus 2, but someone else posted here how to change the example to use a frame.
- jack_riminton 6y agoWould be great if you could add a small video/gif of this
- strzibny 6y agoYes, I will add it!
- yuppiepuppie 6y agoAs someone who is only vaguely familiar with Rails and doesnt really know anything about stimulus, it would be nice to have a "What this looks like" section with a gif of what the user sees. Otherwise, I dont know what this article is talking about. Im interested as there is probably some application to my daily work, but a wall of code and words doesnt tell me much very quickly.
- tpetry 6y agoIt‘s just some ajax logic: Put something in a form, an ajax request will be made an the response is inserted into the dom. The title is pure clickbait, it‘s nothing more than a simple ajax call. The type of logic you‘ve done 10 years ago with a little bit of jquery.
- strzibny 6y agoPlease enlighten me what would be your title for the post? The idea is making a reusable Stimulus 2 controller.
- strzibny 6y agoI just added it. The preview text will become bold, but the idea is that you can send any kind of HTML to inject.
- yuppiepuppie 6y agoGreat job! Now I get it!
- swanson 6y agoI think the article would be better if you were to include even a basic template instead of just adding the <strong> tag. Right now, my first impression would be "why not just wrap the content in <strong> in JS instead of making a network request on every keystroke?" The benefit of being able to reuse the server template logic isn't being demonstrate because of the simplicity of the example.
- Igor_Wiwi 6y agogood to see Rails dev related posts on HN again:)
- rolae 6y agoNice. I did something similar recently with an iFrame where you have a form for configuring customer sites and it displays the preview in an iFrame. I had to throw in some debouncing and special handling for hidden inputs, as changes to hidden inputs do not trigger a change event on the form. In the end the markup was very simple and is very reusable (StimulusJS v1): <div data-controller="iframe-preview" data-iframe-preview-url="<%= preview_path(@letter) %>"> <form data-target="iframe-preview.form"> <textarea name="body">My letter</textarea> </form> <iframe data-target="iframe-preview.iframe"> </div> This is when StimulusJS becomes really nice, when you can compose behavior in your markup with some simple data attributes. I did not think at first that I would need this controller in other places, but a couple of weeks later, I actually needed it, and was able to reuse the controller without modification for another use case.
- strzibny 6y agoNice approach. And yes, Stimulus is great for things like this.
- lucidone 6y agoWould be great to see a paragraph or two about how to write tests for this functionality. Testing has been my pain point regarding front end development with Rails. Compelling tech all the same and a great article.
- nicoburns 6y ago> Testing has been my pain point regarding front end development with Rails Are there any technologies where UI testing isn't a massive pain?
- ch4s3 6y agoI've recently found tests for LiveView with Phoenix to be pretty nice. They're a little bit slower than regular tests, but quite a bit faster than Rails with like Capybara or something like Cypress. Though they are less full featured than something like cypress. YMMV
- strzibny 6y agoI am heavy on testing as well. I believe that Basecamp guys would just use the Rails system tests. Therefore there is nothing unique about using Stimulus.
- sam0x17 6y agoIs capybara + headless chrome / selenium no longer a panacea for all integration testing? I've even used it in a React app.
- JeremyNT 6y agoThis is exactly what I use for my Rails apps and rails provides its own abstraction and helpers to make setting up Capybara especially painless. For any seasoned rails developer this is not difficult. Nothing about hotwire changes the testing approach in any meaningful way versus the old style ujs/jquery techniques, so the older system test tutorials and SO questions are just as valid as they were before.
- kaleidawave 6y agoThe post is well written but I don't see the point in this when you can achieve the same in 145 bytes of JS (vs 80kb of stimulus) and avoid a trip to the server: document.querySelector("input").addEventListener("input", e => { document.querySelector("p").innerHTML = `<strong>${e.target.value}</strong>` }); Reusing the logic could be done if the server was node or through compiling the formatter to wasm. I struggle to see how Hotwire makes things simpler and has comparable speed to a SPA?
- strzibny 6y agoYou are right, but the point is that you can do a lot of transformations in Ruby. The demo doesn't do it justice, you can be converting the content to Markdown for example. As for the Stimulus part, the idea is to use Stimulus for everything interactive, so those extra 80kb is for the whole application.
- wavesplash 6y agoThe author just used Stimulus, not Hotwire - if you add Turbo (the main part of Hotwire) you can remove all the ajax calls. See thread below: https://news.ycombinator.com/item?id=26555842 https://news.ycombinator.com/item?id=26555842 )
- strzibny 6y agoYes, it doesn't use Turbo, but Stimulus is part of Hotwire as well. And actually I do just send a complete HTML over the wire (even if just with AJAX).
- wavesplash 6y agoHey, thanks for the article. The goal of Hotwire is to remove Ajax calls and minimize Javascript client coding as much as possible. That starts with Turbo as the foundation and then Stimulus is the next layer up. There's nothing wrong with doing what you did w/ Stimulus, but calling it Hotwire creates unnecessary confusion for those new to Rails+Hotwire. That confusion is what I'm addressing in the grandparent comment.
- shay_ker 6y agoDoes anyone use Live Preview / Live Views in Phoenix, but in production with a B2C product? I'm a bit skeptical of Rails performance with the method the author details here, given that Ruby is much, much slower than Elixir. I'm curious if even Elixir folks deploy Live Views in consumer-facing apps. But I can totally see it being used for admin interfaces or internal applications, for sure.
- vmsp 6y agoI wouldn't say "much slower" https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/hipe-ruby.html https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
- strzibny 6y agoI believe both are used for customer facing applications. However, I am not sure about b2c at scale. You could argue that Hey.com is b2c. In this case, the main advantage is that scaling is stateless, so your application servers just have to be able to deal with more traffic. There is nothing more to it. As for Phoenix LiveView, it has both advantage and disadvantage of being stateful (something as using Action Cable). You now have to be scaling web sockets, but you save on authenticating the request (the request is lighter). Even if LiveView is stateful, it's done on the platform which is optimized exactly for this purpose. In both approaches, you also have to take into account that server latency might be a deal breaker for you.
- Sodman 6y agoSome anecdata as a hey.com user myself: It feels like they've optimized a lot for initial load of the main homepage (The "Imbox"). Obviously this is super important for an e-mail app... but once you start trying to load anything older than the most recent ~20 e-mails (Eg scrolling "The Feed"), then the load times can be horrifically slow (>10s, followed by a janky pop-in of HTML suddenly loaded). I'm not sure if this is caused by this technology in particular or if there's some kind of cache miss happening here, but coming from and comparing to gmail - which hey's makers like to dump on regularly - the performance is miserable. Sure, the number of transferred bytes is small and there's not a lot of JS to load - both of which I'm a big fan of, but the actual load time that I feel as a user is pretty bad for these AJAX calls. Overall I'm a fan of what they're trying to do with Hey.com, but this particular use case feels like a big UX miss to me.
- strzibny 6y agoBased on the feedback, I updated the post with one more approach based on the idea from https://news.ycombinator.com/item?id=26554476#26556540 https://news.ycombinator.com/item?id=26554476#26556540. The second approach avoids Rails UJS in favour of Turbo Frame. Of course there are even more ways how to do this kind of thing. Keep sharing!
- stevebmark 6y agoI'm happy I don't have to deal with these paradigms day to day anymore: - Making everything go through a "controller" instead of well designed clients and APIs. Mixing rendering, redirection, ajax handling as well as page serving as well as business logic all into a "controller" is painful. ("Models" and the spaghetti design patterns they come with are even worse) - PHP style templates that trigger database queries inline, making them difficult and eventually impossible to optimize - Scattering view code across random places instead of writing view code with its corresponding view logic in the same file/component - Wiring up functionality to templates by targeting selectors - Manually setting innerHTML for dynamic functionality - Dealing with rails "turbo" anything, which is not useful in a real world application, and dangerous (we've had production outages because of trying to use turbolinks) - Not being able to customize behavior because you're locked into Rails design limitations
- jypepin 6y agoI agree. I love Rails and think that it was beautifully designed for its purpose, but it's absolute hell once you start trying to integrate React and more complex frontend stuff into it through its view system (vs using it as an API and have a separate frontend).
- albertgoeswoof 6y agoYou’re probably going to end up doing all those things anyway- just inventing it yourself instead of following a convention
- aantix 6y ago