hotwired / turbo Goto Github PK
View Code? Open in Web Editor NEWThe speed of a single-page web application without having to write any JavaScript
Home Page: https://turbo.hotwired.dev
License: MIT License
The speed of a single-page web application without having to write any JavaScript
Home Page: https://turbo.hotwired.dev
License: MIT License
I noticed that the progress bar is not displayed when submitting a form.
While trying to understand the reason, I noticed that showProgressBarAfterDelay
is called only after the submit is finished.
The method is called by visitRequestStarted
, called by startRequest
, and by keep going back into the stack, they are called into formSubmissionSucceededWithResponse
, so when the submit is already terminated.
The result is that the progress ber is not displayed.
Is this a bug, or did I miss something?
I'm creating a separate issue for this because @sstephenson mentioned this and it would be great to see this work in the future.
In my case, I have a sidebar where users can switch sidebar content using a turbo frame—but the content is not correctly reinstated when navigating back and forth using turbo drive.
Originally posted by @sstephenson in #21 (comment)
data-turbo-permanent
has not been removed, it just doesn't (currently) work for frame or stream renders.
Currently, I can hook into Turbo Drive via turbo:before-visit
and, for example, cancel an immediate visit to perform page transition animations, or whatever else. It would be nice to have a similar set of events to interact with Turbo Stream operations, as well as means to cancel, modify, or pass through the operation via those events.
Something along the lines of:
class Example extends Controller {
/**
* @param {TurboStreamEvent} e
*/
animate(e) {
e.preventDefault();
this.element.animate('...perform some animation');
e.detail.processStream();
}
}
<div id="example_target" data-controller="example" data-action="turbo:before-stream->example#animate">
</div>
Hi everyone,
I've found that Turbo will cause an infinite request loop if you lazy load a frame using src
when the frame in the response also has the same value in src
.
Let me explain:
The first cold response contains something like:
<turbo-frame src="/account">
Grabbing auth tokens and logging you in...
</turbo-frame>
Through some external logic this page sets an auth token in local storage or cookie.
So the subsequent lazy loaded request to /account
will have identified the user and will be something like:
<turbo-frame src="/account">
Hi user! Welcome!
</turbo-frame>
It appears that Turbo lazy-loads again seeing this src="/account"
in the response of the fetch()
.
Because both the original frame has src="/account"
and the new turbo frame has src="account"
I'm seeing an infinite loop being started.
My local fix is now is to not put the src="/account"
in the authenticated request response. This works, but it requires some extra logic in the view that I use to render the view of this frame. Eg:
<turbo-frame id="firmhouse-page-body" <%= 'src="/apps/firmhouse/orders"' unless @subscription.present? %>>
<p>Loading orders...</p>
<% if @subscription.present? %>
<p>Hello, these are my orders.</p>
<%= link_to "Back to Dashboard", "/apps/firmhouse/dashboard" %>
<% end %>
</turbo-frame>
But perhaps it makes sense to have Turbo check if the src
attribute is the same as the previous one or referer, and then not kick off a lazy load but just treat it as the final request.
Hi,
I am using https://viewcomponent.org/ and I am handling redirects for forms using stimulus doing the following:
async turboSubmit(event) {
const { success } = event.detail
if (success && this.redirectValue) {
Turbo.visit(this.redirectValue)
} else {
this.enableSubmit()
}
}
But it currently throws this error Error: Form responses must redirect to another location
, which is due to the following code
turbo/src/core/drive/form_submission.ts
Line 103 in aae160b
Cheers!
If a form has a field/button named "action" e.g.
<input type="hidden" name="action" value="signup">
this will break form submissions by appending "undefined" to the URL e.g. http://localhost/signup/undefined.
Turbo always submits forms using the multipart/form-data
encoding. Instead, we should follow the browser's behavior: read the form submitter's formenctype
attribute or the form's enctype
attribute to determine the form's encoding, or fall back to application/x-www-form-urlencoded
if none is set.
Implementation-wise, we can pass our FormData instance to the URLSearchParams constructor and use the result as the fetch body.
ref. https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/enctype
https://discuss.hotwire.dev/t/post-form-using-application-x-www-form-urlencoded/1615
Hi and thank you for this christmas present :-D
I've been using Turbolinks before - so this is my reference and "knowledge base" on the Turbo concept :)
I followed the installation note from https://turbo.hotwire.dev/handbook/installing and added @hotwired/turbo
and added
import Turbo from '@hotwired/turbo';
to my .js entry file.
This resolved in a referenceError being thrown in the inspector as seen here
Thanks to this StackOverflow question/answer i found a missing @babel/polyfill
library was needed.
Adding that with yarn and add import '@babel/polyfill';
to the top of my entry .js file, turbo now works as used to from Turbolinks :)
My package.json dependencies
block looks like this at current
"dependencies": {
"@babel/core": "^7.12.10",
"@babel/plugin-proposal-class-properties": "^7.12.1",
"@babel/polyfill": "^7.12.1",
"@babel/preset-env": "^7.12.11",
"@hotwired/turbo": "^7.0.0-beta.1",
"autocomplete.js": "^0.38.0",
"babel-loader": "^8.0.5",
"file-loader": "^6.2.0",
"lodash": "^4.17.20",
"stimulus": "^2.0.0",
"turbolinks": "^5.2.0",
"url-loader": "^4.1.1"
},
and my entry .js file like this
import '@babel/polyfill';
import { Application } from 'stimulus';
import { definitionsFromContext } from 'stimulus/webpack-helpers';
import Turbo from '@hotwired/turbo';
const application = Application.start();
const context = require.context('./controllers', true, /\.js$/);
application.load(definitionsFromContext(context));
only as a reference to the question below:
This is purely a suggestion on a "meta level" and not a specific and detailed PR on the docs.
Is there any dependencies on babel libraries or similar, that is needed for Turbo to run? if yes, could such be added to the documentation? Is this a misconfiguration on my end (chances are good - still stepping into new waters on webpack and babel topics) could the installing page display any known human errors?
I like the hotwired approach and is spending my christmas holidays moving from static-static PHP to a hotwired PHP application :)
When I use the link_to
helper with method: :post
set, Turbo handles this as though it were a form. It issues a fetch
request. If the response returned from this request is a 302 redirect to another host, CORS issues are encountered.
<a data-turbo="false" rel="nofollow" data-method="post" href="/auth/github">Connect GitHub</a>
I encountered this when doing something similar to #45 - the link_to
helper creates links that redirect to third party services after POST
ing to an endpoint on my app. When this is done without XHR, it's not an issue, but CORS issues arise with XHR, most likely because the redirect URLs are on external hosts (e.g. github.com).
Setting data-turbo="false"
doesn't change or prevent this behavior, which is particularly unusual. In the meantime, I'm using button_to(<text>, <link>, form: { 'data-turbo' => false })
as suggested in that thread. However, a
and button
are semantically different in HTML, so it's important this gets fixed for the sake of accessibility.
I had trouble running it as plain html with the CDN.
I could not find a releases github page with plain JS (not Typescript), and the CDNpack only seems to work with <script type="module"> import hotwiredTurbo from 'https://cdn.skypack.dev/@hotwired/turbo';</script>
After a stable release, it would be great to have some basic examples to see it in action, e.g. on codepen.
As discussed in Are transitions and animations on Hotwire roadmap?, @sstephenson mentions that there would be utility in exposing appropriate seams to consumer applications.
One potential solution could involve dispatching the existing turbo:
-prefixed events as a <turbo-frame>
element progresses through a navigation. Those events could be dispatched and bubble up from the turbo-frame
element, making the frame available as the CustomEvent.target
when appropriate. Page-level turbo:
-prefixed events would still dispatch off the document
element.
In addition to the value it would bring for ancestors to be notified of various lifecycle hooks, it could also enable the cancelation of turbo-frame
visitations, and could expose timing metrics as part of frame-scoped turbo:load
events.
From a technical perspective, the events could be dispatched from the various empty FrameController
hooks:
turbo/src/core/frames/frame_controller.ts
Lines 75 to 121 in c52d87c
Alternatively, a FrameController
could be provided with its own Session
instance scoped to the <turbo-frame>
element (as opposed to the current implementation which operates on the document.documentElement
element).
In theory, there could be a lot of value in unifying the implementations of how a page and a frame navigate, but might involve a larger set of architectural changes. Are there conceptual mismatches between a frame-scoped Session
/ Navigator
pairing and the current implementation which relies on FormInterceptor
and LinkInterceptor
instances?
There is a single mention of the turbo-frame[recurse]
attribute in the implementation.
There are no mentions of the recurse
attribute on the Turbo documentation site.
Is the recurse
attribute intended to be part of a Turbo Frame's public interface?
Firstly and perhaps most importantly, thank you so much for Turbo. I really enjoy it!
Let's take a very simple example of a new Rails application with hotwire-rails
created. We create a scaffold for products and within the form partial of the products, we wrap the form elements in a turbo_frame_tag 'product'
. The expected behavior now is if there are any validation errors, it renders the page with the validation errors. Turbo accomplishes this already.
However, if there are no validation errors and the record persists successfully, then I would expect it to render the display of the show page. However, because there is no turbo-frame-tag on the show page, it doesn't do anything and we still have the form displayed.
I think that this is very unexpected behavior even though things make sense with turbo. I know that this functionality has caused me a few headaches initially and also the community with trying to find proper workarounds.
I'm not the best Javascript person out there, but dabbled around in it a bit, so I would leave any PR to the experts. However, I did pull in the turbo library from turbo-rails into my application to tinker with things and came up with a minor-ish fix.
Within the FrameController
class, I modified the loadResponse
function. I added the const html
which before was simply inside of the fragmentFromHTML
, but the important bit is the else
statement. It looks like the element
is the rendered result from our Rails application and the turbo-frame-tag has been plucked out as a fragment of the original rendered response. So, if no element
is found, then that means that the rendered page did not have any turbo-frame-tags which means that our UI will look like it did not perform any action.
My thought here is to replace the body of the current page, with the entire contents of the rendered response in the event that no turbo-frame-tag was found within the response.
async loadResponse(response) {
const html = await response.responseHTML
const fragment = fragmentFromHTML(html);
const element = await this.extractForeignFrameElement(fragment);
if (element) {
await nextAnimationFrame();
this.loadFrameElement(element);
this.scrollFrameIntoView(element);
await nextAnimationFrame();
this.focusFirstAutofocusableElement();
} else {
document.getElementsByTagName("html")[0].innerHTML = html
}
}
When a FormSubmission request fails and return 400-500 code, turbo:load
won't be triggered. eg:
application.js
document.addEventListener('turbo:load', function () {
console.log('turbo load')
document.getElementById('demo').replaceWith('demo replace')
})
new.html.erb
<div id="demo">demo</div>
<%= form_with(model: @post) do |f| %>
<%= f.label :title %>
<%= f.text_field :title %>
<%= f.submit %>
<% end %>
posts_controller.rb
def create
@post = Post.new(post_params)
respond_to do |format|
if @post.save
format.html { redirect_to @post, notice: "Post was successfully created." }
format.json { render :show, status: :created, location: @post }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @post.errors, status: :unprocessable_entity }
end
end
end
class Post < ApplicationRecord
validates :title, presence: true
end
After submit form, it should show demo replace
, but it showed demo
because of turbo:load
hasn't be triggered.
Navigating to same-page fragments results in a new visit to the current page and fails to set the sequential focus navigation starting point to the target fragment.
<a href="#main-content">Skip to main content</a>
The above should allow users to move the focus starting point to the element with id main-content
. But, after Turbo Drive steps in and reloads the page content, the focus starting point is instead reset to the top of the page.
You can see this in action at turbo.hotwire.dev. Try tabbing through the page using the skip link and you'll end up in a loop.
I think this behavior is another result of turbolinks/turbolinks#75. I know you can use data-turbo="false"
to opt-out, and that changing the behavior may be deceptively complex (esp. when accounting for native implementations), but the current behavior seems out of step as Turbo seems to embrace an "it just works" experience.
The following workaround from this comment is good enough for me in the meantime:
document.addEventListener('turbolinks:click', function (event) {
var anchorElement = event.target
var isSamePageAnchor = (
anchorElement.hash &&
anchorElement.origin === window.location.origin &&
anchorElement.pathname === window.location.pathname
)
if (isSamePageAnchor) {
Turbolinks.controller.pushHistoryWithLocationAndRestorationIdentifier(
event.data.url,
Turbolinks.uuid()
)
event.preventDefault()
}
})
...but without access to something like pushHistoryWithLocationAndRestorationIdentifier
, I'm not sure how to keep my history in order. Is there a Turbo Drive analogue to the method? Or an approved method of manually adding to the session history?
You can download the latest distributable script from the GitHub releases page, then reference that in your <script> tag on your page. — https://turbo.hotwire.dev/handbook/installing (§ In Compiled Form)
I think that script is missing from the build in the released archive from the source code download at https://github.com/hotwired/turbo/releases/tag/v7.0.0-beta.1
It would be handy and more accessible to make it available without installing all the tooling (or to change the doc accordingly).
I want to trigger a (partial) Page update so my Idea was to use a Form which gets a Turbo-Stream UPDATE
Response which will do the partial page update.
However, as I want to submit the Form via a different Element and not the Forms <input type="submit"/>
I made a Stimulus Controller to handle the Form submition (e.g. on a button click).
However, if I do form.submit()
from the Stimulus controller the Form submission is not intercepted by Turbo and is performed "regularly". Thus, I have to render an invisible <input type="submit"/>
and trigger its click()
method in Stimulus to make it work.
Is this a bug? Or just a conceptual thing?
Hi,
Let's say I wanted tabs on a page to be loaded in frames but I wanted each tab to have its own distinct URL. Currently it doesn't seem like this is possible out of the box because switching between frames doesn't update the URL.
Here's a real world use case of where I wanted to use Turbo Frames but ended up aborting the idea of it and going back to making a raw ajax request and then not rendering the layout of the page at the controller level when I detected it was an ajax request.
We all remember Railscasts right? Here's one of its pages: http://railscasts.com/episodes/416-form-objects?autoplay=true
The neat thing about this page is if you're playing a video, you can navigate between the show notes, comments and the other tabs without the video player stopping. Each tab also has its own unique URL so you can bookmark it and share it with others.
In Railscast's case I believe just using Turbo Drive and the permanent attribute would have been good enough to make this work because Ryan is using a native video player.
However, a lot of video services like Vimeo expect you to embed an iframe and iframes have a very unique characteristic in that as part of the spec if the underlying DOM of the iframe changes then the iframe gets reloaded which means the video watching experience gets interrupted (even when using the permanent attribute).
So that leaves us with at least 2 choices to get an uninterrupted video playback experience:
Have each tab as its own controller but then manually set up some JS to make each link an ajax request and have Rails not render the layout at the controller level unless someone is directly accessing the URL.
Use the new Turbo Frames feature (which works), but then the URL doesn't get updated.
Is there a way forward where push state could be added to Turbo Frames, or would that not make sense for the use case that frames is trying to tackle, and in the above case we should wire up a manual ajax call to have that desired effect?
Hey guys, I have an issue with the following code:
// marketplace-filter-controller.js
export default class extends Controller {
static targets = [ "submit", "form" ]
submit() {
this.submitTarget.click(); // this works and just pushState on history browser
this.formTarget.submit(); // this sends the request as HTML (instead of turbo_stream) leading to a blank page
}
}
// html
<%= form_tag products_marketplace_index_path, method: :get, data: { controller: 'marketplace-filter', 'marketplace-filter-target': 'form' } do %>
<button type="submit" class="search-wrap__btn" data-marketplace-filter-target="submit">
<span class="lnr lnr-magnifier"></span>
</button>
// controller
def products
@products = Product.filter(params).limit(25)
render turbo_stream: turbo_stream.replace('products', partial: 'marketplace/products')
end
as mentioned on comments above, when using the submit button to simulate a click on js it works properly and replace the turbo-frame from returned by http response, but when using the form itself to submit
, it redirects to a blank page.
// request log using *form.submit()* request
Processing by MarketplaceController#products as HTML
// request log using *button.click()* request
Processing by MarketplaceController#products as TURBO_STREAM
any idea if its an issue?
First thing first: Thank you for sharing Hotwire! I like it a lot.
Turbo partly replaces rails-ujs. This makes sense. However, together with other functionality we lose double click protection which was implemented in rails-ujs with data-disable
and data-disable-with
.
Are there any plans or appetite for adding this directly to Turbo? Or do you suggest to handle this with a Stimulus controller instead?
I would kind of like to see this implemented directly in Turbo so this work for people out of the box like it worked with rails-ujs which is installed in Rails by default. On the other hand I would understand if there is no appetite for adding this.
Hi,
I'm using Turbo v7.0.0-beta.1
and TailwindCSS 2.0.2
.
I'm able to reproduce this 100% of the time and the issue goes away if I stop using Turbo Drive to intercept link clicks.
I've added mouse click audio and visual cues to better see when I'm clicking the mouse. The flickering is much worse in practice because that video was recorded at 30 fps, so it's missing a lot of the very fast flickering effects.
It's also worth pointing out while the hover effect is happening potentially dozens of times per mouse click, there is only 1 XHR created for each of those clicks.
<div class="flex space-x-4">
<a href="/a" class="bg-gray-500 hover:bg-gray-800 text-white px-3 py-2 rounded-md text-sm font-medium">
Home
</a>
<a href="/b" class="bg-gray-500 hover:bg-gray-800 text-white px-3 py-2 rounded-md text-sm font-medium">
Turbo Drive
</a>
<a href="/c" class="bg-gray-500 hover:bg-gray-800 text-white px-3 py-2 rounded-md text-sm font-medium">
Turbo Frames
</a>
</div>
The only thing I've modified is the link's href
to keep it short.
It should work identically to how it works when Turbo Drive is not being used. The button's hover state should not rapidly fire when a mouse click event is fired.
I'm not 100% sure if it's related to Turbo or not because I haven't tested things without TailwindCSS. Although, personally I've never had an issue with Tailwind's hover effects when not using Turbo Drive. I've also used Turbolinks 5 successfully without encountering this flicker effect with various CSS libraries.
Due to the change in #1, there is no way to set a form
's method to PUT
or DELETE
. It is however possible with the formmethod
attribute of the submitter
element.
Relevant code from form_submissions.ts
:
get method(): FetchMethod {
const method = this.submitter?.getAttribute("formmethod") || this.formElement.method
return fetchMethodFromString(method.toLowerCase()) || FetchMethod.get
}
The reason is visible on the following screenshot. Meanwhile getAttribute("formmethod")
returns the string from the HTML, this.formElement.method
returns "get"
with not respect to the value "delete"
in the HTML. I guess because it is not a valid value according to the spec (and yeah, "delete"
is not a valid value for formmethod
either.)
What is the best practice with respect to non GET
and POST
requests?
Thanks!
When clicking links while inside a frame, the Turbo-Frame
header will be set.
This helps the backend to know this is a frame request and for instance to not render layout.
When submitting a form, this header is not set, I think it should be.
Example : To respect progressive enhancement, we should be able to return a frame when the request comes from turbo, but perform a redirection when not.
Similar to the turbo:before-stream-render
event, it would be useful to fire an event after rendering a Turbo Stream page update
Hi , I'm trying Turbo , simple crud, table list:
example code:
<table>
<thead>
<tr>
<th>Name</th>
<th>Key</th>
<th>User</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<%= turbo_frame_tag "rooms" do %>
<% @rooms.each do |room| %>
<%= render room %>
<% end %>
<% end %>
</tbody>
</table>
but the frame tag is rendered separate from the list , why ?
Hi !
It seems that when placing a turbo-frame tag within a table tag, it ends up outside of the table in the DOM. As a consequence, when clicking the link, only the content of the target page ends up being displayed, and outer html is gone. I don't have this issue when using simple div tags.
PS: first issue ever opened, so I am sorry if some information is missing or if it's not the proper place to submit this.
We use Turbolinks in our Jekyll website and compile the JS using Jekyll Assets (which uses Sprockets).
However because Turbo now expects you to reference the JS file as a module (if you're not using NPM/Yarn) you can't include it in the application.js
without getting the error Uncaught SyntaxError: Unexpected token 'export'
.
Is there a way to include Turbo the same way we could Turbolinks? e.g. //= require ./vendor/turbo-7.0.0-beta.1
To solve this we've had to remove it from our compiled JS and have Turbo loaded in separately with:
<script type="module">
import hotwiredTurbo from 'https://cdn.skypack.dev/@hotwired/turbo';
</script>
<script src="./application-1234.js"></script>
Additionally by importing the module like this we can't access Turbo.
so we can't for example change the progress bar delay with: Turbo.setProgressBarDelay(0);
or clear the cache with Turbo.clearCache();
as Turbo
isn't defined...
The documentation suggests this works with links only, but it appears that data-turbo="false" does not work with forms:
<form method="post" action="..." data-turbo="false">...
When testing it appears forms are submitted through Turbo regardless of this attribute.
https://turbo.hotwire.dev/handbook/drive#disabling-turbo-drive-on-specific-links
Is there a good workaround for this? I need to be able to disable Turbo for some specific forms.
I was using the remote feature of rails-ujs and stimulus to build a modal with a form.
I had written the following code to close the modal when submit succeeds, and to display an error message when it fails.
<div class="modal" data-controller="modal">
<%= form_with url: '...', data: { controller: 'remote', action: 'ajax:success->modal#close ajax:error->remote#replace' } %>
</div>
// modal_controller
export default class extends Controller {
close() {
$(this.element).modal('hide')
}
}
// remote_controller
export default class extends Controller {
replace(ev) {
const { detail: [, , xhr] } = event
this.element.outerHTML = xhr.responseText
}
}
I think it would be nice to have an event to achieve the same thing with turbo.
For example, events like turbo:submit-success
, turbo:submit-error
.
Would be really helpful to add turbo frames events just like turbo drive has with the frame_id
as a payload for that event.
document.addEventListener('turbo-frame:load', (event) => console.log('loaded frame ->', event.turboFrameId))
Would this be something helpful to explore?
I got tripped up for a bit when trying to add a turbo-frame with lazy loading. It seemed like it wasn't working correctly, but the issue was a mistake on my part: I didn't properly add the frame to the response I was loading. There are instructions for doing so in the guide, but I can see this tripping up people.
My workflow went like this:
Until I looked at the Rails logs to see the request was being made, I had no indication of what was wrong.
Would it be possible to console error or warn the developer if Turbo wasn't able to find a suitable replacement frame?
I am trying to read the error response from turbo:submit-end
, but it is failing with:
base.js:96 Uncaught (in promise) TypeError: Failed to execute 'text' on 'Response': body stream already read
The test code I have is as follows:
async turboSubmit(event) {
const {
success,
fetchResponse: { response },
} = event.detail
if (success) {
return
}
console.log(await response.text())
}
Ditto on the issue title. It would be nice to have a programmatic way to invoke a stream URL, same way we currently can trigger a visit with Turbo.visit()
.
Turbo.stream('/some/url/here').then(r => console.log('maybe even return a promise'))
.
when using turbo with forms that don't respond with fetch response.redirected on POST request, turbo throws a "Form responses must redirect to another location" error.
It would be nice (especially for legacy application) that turbo can handle all kind of results of a form posts without throwing an error.
The form in question ist just a html form with POST and responds with 200 every time. expected behaviour would be that turbo does the same as with clicking a link.
Im not even sure how a "correct" POST should look like so that turbo works, documentation is not clear on that. from the source code it looks like response.redirected and response.ok must both be true.
I'm using turbo via NPM import and when I click a link for the first time, the page renders perfectly fine. But the following error is shown in the console:
However, on the subsequent navigations no such exception is thrown.
I don't know if this is a major issue since I'm not a JS developer. I just wanted to let you people know about the error.
Thanks!
I've got a Stimulus Controller that automatically submits a form when any input is changed. However, this is not picked up by Turbo. Here is the controller:
/*
Example usage (Rails):
<%= form_with(url: articles_path, method: :get, local: true, html: { 'data-controller': 'form-auto-search' }) do |form| %>
// From contents
<% end %>
Example usage (plain HTML):
<form data-controller="form-auto-search">
// Form contents
</form>
*/
import { Controller } from "stimulus";
export default class extends Controller {
connect() {
if (this.element.tagName !== "FORM") {
console.error("The form-auto-search controller should be added to the form element. It is set to: ", this.element.tagName);
return;
}
this.form = this.element;
const inputs = this.element.getElementsByTagName("input");
for (let i = 0; i < inputs.length; i++) {
const input = inputs[i];
input.addEventListener("change", () => this.handleInputEvent());
}
}
handleInputEvent() {
this.form.submit();
}
}
To get this to work, I have to import @rails/ujs
and use Rails.fire(this.form, 'submit');
instead of this.form.submit()
.
Am I doing something wrong?
Hi, thanks for this great library!
Currently, the media type expected for turbo streams fragments is text/html; turbo-stream
. This creates some problems:
Instead of an attribute, a vendor media type such as application/vnd.hotwire.turbo.stream
could be used, as suggested by RFC 6838
👋
Wondering if there would be an appetite for adding a configuration/setting to opt-out of some of the Turbo modules.
If you only wanted the TurboDrive for links, it might be nice to support a global opt-out of the formSubmitObserver
. If you aren't using Frames or Streams, you could opt-out of the streamObserver
, frameRedirector
, etc.
There are sometimes ways to opt-out of these behaviors on a per-page, per-form, or per-link basis, but I think it would smooth adoption / upgrading from Turbolinks if you could opt-in piecemeal.
I don't think it's a slam dunk, especially since the installation path is really nice and there is no config/settings (outside of the progress timeout). Happy to help implement if there is interest, but wanted to gauge support.
I use to submit the form " @ hotwired / turbo ":" ^ 7.0.0-beta.1 "
, the code is very simple:
<form action = "/ test" method = "post">
<input type = "input" name = "name">
<button type = "submit"> Submit </button>
</form>
This works great. But when I get an error, such as a 500 or 200 response, but with different content (debugging page) without
<html> <body> ...
, then the page is not actually refreshed, and the form remains (as if nothing was submitted).
f
Great package. Thanks!
I'm trying to submit a form from within a modal overlay, and I'm struggling to find the right approach for handling validation errors with Turbo.
I'm wrapping the form in a turbo-frame
tag so that I can gracefully hide the modal on a successful 200 response rather than letting the redirect swap out out the body. I have a Stimulus controller that hides my modal if the form receives a successful 200 response from the turbo:submit-end
event. This works great as long as the response is a 200 without validation errors.
But when the form response comes back with validation errors and unprocessable_entity
status, my new turbo -frame
tag with form validation errors is not swapped with the old form turbo-frame
. Of course, if I send back a 200 status it works, but in that case, my stimulus controller detects a successful response and hides the modal.
What would be a more sound approach for handling form error validation with Turbo from within a modal?
On a regular Drive page update, it's scrolling up to the top of the page. Normally that would be desired, but on some Drive links I want to maintain the current scroll position. I tried looking for a data-turbo-preserve-scroll
option or something like that but couldn't find anything. I could use Frames in this scenario instead, but then I'd lose the URL history/back button/etc.
What is the best practice for upgrading from Turbolinks 5 regarding the turbolinks-permanent directive? In this particular case, I use this to keep a video or audio component running between page transitions, without having the media element restart. The new turbo-frame does not seem to do the same thing. Has this been removed from the new Turbo? What's the best practice here?
With webpack this works:
const Turbo = require("@hotwired/turbo")
Turbo.connectStreamSource(new WebSocket("ws://" + document.location.host + "/recieve"));
While the suggested import from the docs doesn't:
import Turbo from "@hotwired/turbo"
Turbo.connectStreamSource(new WebSocket("ws://" + document.location.host + "/recieve"));
It prints this warning in webpack:
WARNING in ./js/index.js 7:0-25
export 'default' (imported as 'Turbo') was not found in '@hotwired/turbo' (possible exports: clearCache, connectStreamSource, disconnectStreamSource, navigator, registerAdapter, renderStreamMessage, setProgressBarDelay, start, visit)
Which leads to this error in the browser console:
Uncaught TypeError: _hotwired_turbo__WEBPACK_IMPORTED_MODULE_1__.default is undefined
Not sure if this should go on this repo or the stimulus repo but here's the issue I'm running into.
I am loading a turbo frame via
<%= turbo_frame_tag "event_list.1", src: events_list_path(page: 1) %>
This returns the following frame
<div data-controller="eventlist">
<%= render @events %>
</div>
And eventlist_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
connect() {
console.log('Im connected!')
}
}
When the frame is loaded, "Im connected!" is logged to the console
sometimes it works, but most of the time it does not connect. It seems like if it does successfully connect, then as long as I don't refresh the page it will continue to work.
I am using the the rails asset pipeline with vanilla javascript.
gem 'turbo-rails', github: 'hotwired/turbo-rails', branch: 'main'
gem 'rails', '~> 6.1.0', '>= 6.1.0.0'
gem 'hotwire-rails'
According to documentation:
https://turbo.hotwire.dev/handbook/drive#redirecting-after-a-form-submission
After a stateful request from a form submission, Turbo Drive expects the server to return an HTTP 303 redirect response, which it will then follow and use to navigate and update the page without reloading.
It appears that Turbo works fine with any 3xx code (e.g. 301 or 302). However is this going to be a hard rule? Many non-Rails frameworks (e.g. Django) assume a 301 or 302 redirect by default, and this will break standard behavior. Is there a good reason for this or should the docs be updated?
Hello, a tutorial exist please?
I'm using Devise GitHub omniauth login for an app and the login link looks like this:
# layouts/_header.html.erb
<%= link_to "Github Login", user_github_omniauth_authorize_path, method: :post, class: "some classes", "data-turbo" => "false" %>
After I enable Turbo, it no longer works. After inspecting the network, I found that when a user clicks on the link, it issues a fetch
type request to the server, rather then a document
type request.
My app is open-sourced here and you can reproduce it by uncommenting the // import { Turbo, cable } from '@hotwired/turbo-rails'
line from application.js pack file.
In normal web applications, multiple users often work on the same resources (unlike hey.com). In today's world of video conferencing, it is even more common for a web application to be used by multiple people in parallel.
With Turbo Streams, updated versions of resources can now be displayed live to all users without a page reload. It's a big step towards a fully collaborative application! But how does Turbo Streams deal with simultaneous updates of Turbo Frames now? Let's assume two users both submit a form pretty much simultaneously without any conflicts. The response may already include an update to the Turbo Frame so they can see their update, but now other users' changes are also distributed via the websocket and due to temporary delays in the backend, the last version sent via websocket may be an outdated version that is then displayed.
This can be illustrated with the following example:
Now the displayed information is incorrect because the turbo frame with the changed information of user A and not B is displayed, although the data of B is the persistent one in the database. These problems do occur in reality as race conditions are possible in the backend execution. Is there any logic planned to solve this problem? You could e.g. include e.g. an optional timestamp or commit version number (or any incrementing value) in the dom specification for turbo frames to ensure the chronological sorting, so that received turbo frames with an older version can be discarded by the JavaScript code.
This may be possible to integrate with custom logic, but it is such an essential part that it should be part of the core and not be implemented by everyone?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.