[HN Gopher] Live previews with Rails and Stimulus 2
___________________________________________________________________
Live previews with Rails and Stimulus 2
Author : nilsandrey
Score : 112 points
Date : 2021-03-23 13:02 UTC (9 hours ago)
(HTM) web link (nts.strzibny.name)
(TXT) w3m dump (nts.strzibny.name)
| strzibny wrote:
| Based on the feedback, I updated the post with one more approach
| based on the idea from
| 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!
| jack_riminton wrote:
| Would be great if you could add a small video/gif of this
| strzibny wrote:
| Yes, I will add it!
| kaleidawave wrote:
| The 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 wrote:
| You 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 wrote:
| The 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
| )
| strzibny wrote:
| Yes, 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 wrote:
| Hey, 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.
| strzibny wrote:
| I included a second example with Turbo Frame, but I am
| keeping Stimulus since it's what the article is about
| (and it's in the title). Maybe you can do without it but
| you have to use some hidden forms or something. I believe
| Stimulus makes it "clean."
|
| Btw Turbo Stream is kind of cool, but in this particular
| case, do you think its' really better?
| yuppiepuppie wrote:
| As 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 wrote:
| It'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 wrote:
| Please enlighten me what would be your title for the post?
| The idea is making a reusable Stimulus 2 controller.
| strzibny wrote:
| I just added it. The preview text will become bold, but the
| idea is that you can send any kind of HTML to inject.
| yuppiepuppie wrote:
| Great job! Now I get it!
| swanson wrote:
| I 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.
| strzibny wrote:
| I was thinking of that, but I think the point is that you
| do _any_ transformation in Ruby. I focused on how to do
| that. Thanks for the feedback.
| Igor_Wiwi wrote:
| good to see Rails dev related posts on HN again:)
| rolae wrote:
| Nice. 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 wrote:
| Nice approach. And yes, Stimulus is great for things like this.
| 023984398 wrote:
| This is cool but its not using hotwire (turbo frames / turbo
| streams)
| strzibny wrote:
| It 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/
| oh_boy wrote:
| At least in the non-rails environment, Turbo and Stimulus are
| two separate libraries and have to be included individually.
| edwinvlieg wrote:
| The 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 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 wrote:
| Perfect. 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 wrote:
| > "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 wrote:
| I included a second example with Turbo Frame that
| hopefully shows how Turbo Frames work.
| JeremyNT wrote:
| There 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 wrote:
| Yep, 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 wrote:
| Once 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 wrote:
| I mean technically they are installed but the example is
| still using UJS and not the functionality provided by the
| hotwire libs.
| strzibny wrote:
| Yes, I was only focusing on Stimulus 2, but someone else
| posted here how to change the example to use a frame.
| shay_ker wrote:
| Does 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.
| nickjj wrote:
| > I'm a bit skeptical of Rails performance with the method the
| author details here
|
| The method used in the blog post is making an ajax request to
| the server and then displaying the results in a div. For
| comparison, GitHub is running Rails and when you type into its
| search box it's making an ajax request to the server and
| displaying the results in a div.
|
| Just wanted to bring that up here because this is different
| than what Live View does or what using Turbo Streams would do
| to broadcast changes over a websocket connection if you were
| using Rails.
|
| I don't know of anyone running a large scale app using Live
| View as a primary focus but I did chat with someone on my
| podcast about how they used Phoenix and Live View to build
| https://textdb.dev. That episode is at
| https://runninginproduction.com/podcast/68-textdb-is-a-
| simpl.... TextDB trended here on HN a few months ago at
| https://news.ycombinator.com/item?id=23948234. It handled the
| HN front page load without issues, but it's also not doing a
| ton.
| vmsp wrote:
| I wouldn't say "much slower"
|
| https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
| strzibny wrote:
| I 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 wrote:
| Some 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.
| stevebmark wrote:
| I'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
| albertgoeswoof wrote:
| You're probably going to end up doing all those things anyway-
| just inventing it yourself instead of following a convention
| aantix wrote:
| What happened with the outage and Turbolinks?
| strzibny wrote:
| Interested as well.
| jypepin wrote:
| I 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).
| FanaHOVA wrote:
| react_on_rails is actually quite nice. What have you tried to
| build?
| jraedisch wrote:
| Webpacker was the first time I ever felt comfortable writing
| React. It just worked for me. So I have to disagree here.
| lucidone wrote:
| Would 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.
| strzibny wrote:
| I 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.
| nicoburns wrote:
| > 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 wrote:
| I'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
| sam0x17 wrote:
| Is capybara + headless chrome / selenium no longer a panacea
| for all integration testing? I've even used it in a React app.
___________________________________________________________________
(page generated 2021-03-23 23:01 UTC)