import { navigator } from '@hotwired/turbo' | |
import { Controller } from 'stimulus' | |
import { useMutation } from 'stimulus-use' | |
export default class extends Controller { | |
connect (): void { | |
useMutation(this, { attributes: true }) | |
} | |
mutate (entries: MutationRecord[]): void { | |
entries.forEach((mutation) => { | |
if (mutation.type === 'attributes' && mutation.attributeName === 'src') { | |
const src = this.element.getAttribute('src') | |
if (src != null) { navigator.history.push(new URL(src)) } | |
} | |
}) | |
} | |
} |
Here's a version using the ingenious stimulus-use package, reads a bit nicer:
import { Turbo } from "@hotwired/turbo-rails";
import { Controller } from "stimulus";
import { useMutation } from "stimulus-use";
export default class extends Controller {
connect() {
useMutation(this, { attributes: true, childList: true, subtree: true });
}
disconnect() {
window.removeEventListener("popstate", this.popStateListener);
}
mutate(entries) {
for (const mutation of entries) {
if (mutation.type === "childList") {
// re-initialize third party, e.g. jquery, plugins
}
if (mutation.type === "attributes" && mutation.attributeName === "src") {
history.pushState(
{ turbo_frame_history: true },
"",
this.element.getAttribute("src")
);
}
}
this.popStateListener = event => {
if (event.state.turbo_frame_history) {
Turbo.visit(window.location.href, { action: "replace" });
}
};
window.addEventListener("popstate", this.popStateListener);
}
}
Actually here is my latest version using the newly available navigator API + stimulus use ;)
import { navigator } from '@hotwired/turbo'
import { Controller } from 'stimulus'
import { useMutation } from 'stimulus-use'
export default class extends Controller {
connect (): void {
useMutation(this, { attributes: true })
}
mutate (entries: MutationRecord[]): void {
entries.forEach((mutation) => {
if (mutation.type === 'attributes' && mutation.attributeName === 'src') {
const src = this.element.getAttribute('src')
if (src != null) { navigator.history.push(new URL(src)) }
}
})
}
}
cool! does navigator.history
handle popstate for you, too?
awesome, thanks!
It's just a shame that stuff is documented so poorly :-(
Is there a way to get title in order to push it too ?
Is there a way to get title in order to push it too ?
I don't think so, the frame update would not include the title.
However you can probably have a stimulus controller that updates the page title on connect given a value, and push a HTML tag in the frame response that would instantiate such controller with a desired title value
For anyone else trying to use stimulus-use
without a build system, add StimulusUse
before useMutation()
and import navigator
directly from cdn (but if there's a better way, please comment!). In the end the result will look like this (in javascript
):
// js
import { navigator } from 'https://cdn.skypack.dev/@hotwired/turbo';
export default class extends Stimulus.Controller {
// from: https://gist.github.com/Intrepidd/ac68cb7dfd17d422374807efb6bf2f42
connect () {
StimulusUse.useMutation(this, { attributes: true })
}
mutate (entries) {
entries.forEach((mutation) => {
if (mutation.type === 'attributes' && mutation.attributeName === 'src') {
const src = this.element.getAttribute('src')
if (src != null) { navigator.history.push(new URL(src)) }
}
})
}
}
(Stimulus.Controller
is also used as stimulus is being imported via <script src="https://unpkg.com/stimulus/dist/stimulus.umd.js"></script>
)
@Intrepidd, thanks for this solution is precious; one thing I've found is that the forms with target _top do not honor the redirects.
How could this be accomplished?
@michelson I am not sure to follow, if you have a form with target _top and use my controller the URL won't change after the redirect ?
@Intrepidd, sorry, I've double-checked this, and it is working as expected.
fffx commentedJan 27, 2021