2

I have tried to make a minimal example that doesn't involve any specific server side technology. So I have just generate the webpage with bash and serve it with cgi.

$ mkdir example
$ cd example
$ cat > index.html
<meta http-equiv="Refresh" content="0; url='/cgi-bin/index.html'" />

$ mkdir cgi-bin
$ cat > cgi-bin/index.html
#! /usr/bin/env bash

set -e

echo Content-Type: text/html
echo
sed "s/NEW/$RANDOM/g" << EOF
<html>
  <head>
    <title>DEMO</title>
    <script src="/node_modules/@hotwired/turbo/dist/turbo.es2017-umd.js"></script>
  </head>
  <body>
    <h1>DEMO</h1>
    <p><small>lucky bonus number: $RANDOM</small></p>
    <turbo-frame id='example-frame'>
        <a href="?NEW">NEW turbo frame</a><br />
        <a href="?NEW" data-turbo-frame="_top">NEW full page</a>
        <p>number in query string: $QUERY_STRING</p>
    </turbo-frame>
    <br />
  </body>
</html>
EOF


$ chmod +x cgi-bin/index.html
$ npm install --save @hotwired/turbo@7.0.1
$ python3 -m http.server --cgi # or whichever server

This creates a webpage that looks like this:

initial page with no query string

Then when you click on turbo frame reloader link, you get this:

page with updated query string number, same bonus number, and no url change

The lucky bonus number has not changed because it only loaded the turbo frame, but the query string number, which is displayed inside the turbo frame has been updated.

The full page turbo link does this:

page with updated url and updated lucky bonus number

It updates the lucky bonus number (content outside the turbo frame) and updates the url. (I am sure it is working properly though. I have checked the network tab. It is only doing a fetch each time.)


What I would like to do:

I would like to have a link that only updates the turbo frame (leaves the lucky bonus number unchanged in my example) but also updates the url. I guess the use case is if you have a banner or menu that always stays the same, but a primary turbo frame with all the page specific content that should correspond to the url.

So in this example, I would like to see both the query string on the page, and the actual query string update, leaving only the "lucky bonus number" unchanged.

Alex028502
  • 3,486
  • 2
  • 23
  • 50
  • Unrelated to the nature of the question or my answer: I've never seen this way of testing frontend technology, and it's a neat idea which I may use in the future. EDIT: I mean the micro-server + CGI. – aidan Oct 10 '21 at 19:47

1 Answers1

2

This is definitely possible, though it requires some substantive changes to your implementation. Basically, you need to use Turbo Streams for the partial page replacement.

  1. You need a 2nd file to request, one which has Content-Type text/vnd.turbo-stream.html.
  2. Partial page reloads work just fine without Javascript (i.e. leave bonus number unchanged while updating inside the turbo frame), but if you want to update the URL, you'll need some JS.

See this gist for an extended version of your code. Below are the most important bits, mainly a new file template.html.

index.html is unchanged.

webpage/cgi-bin/index.html

Here I just changed the link tags. Full page reload requests index.html, where the Turbo-enabled link requests the turbo-stream-enabled template.html For some reason I needed to add data-turbo=true on the Turbo-enabled link; being inside a turbo frame wasn't enough. data-turbo-action=replace is supposed to be the HTML attribute that "replaces" the location value in the URL. That doesn't seem to be behaving like I expect from the docs.

#! /usr/bin/env bash

set -e

echo Content-Type: text/html
echo
sed "s/NEW/$RANDOM/g" << EOF
<html>
  <head>
    <title>DEMO</title>
    <script src="/node_modules/@hotwired/turbo/dist/turbo.es2017-umd.js"></script>
  </head>
  <body>
    <h1>DEMO</h1>
    <p><small>lucky bonus number: $RANDOM</small></p>
    <turbo-frame id='example-frame'>
        <a href="template.html?NEW" data-turbo="true" data-turbo-action="replace">
          NEW turbo frame
        </a><br />
        <a href="index.html?NEW" data-turbo="false">
          NEW full page
        </a>
        <p>number in query string: $QUERY_STRING</p>
    </turbo-frame>
    <br />
  </body>
</html>
EOF

webpage/cgi-bin/template.html

This is the most important bit. Your server must respond with a turbo-stream tag, with the action replace (because you want to replace HTML content) and with the target attribute set to your turbo-frame's id (because that's the content you wish to replace).

#! /usr/bin/env bash

set -e

echo Content-Type: text/vnd.turbo-stream.html; charset=utf-8
echo
sed "s/NEW/$RANDOM/g" << EOF
<turbo-stream action="replace" target="example-frame">
    <template>
        <turbo-frame id='example-frame'>
            <a href="template.html?NEW" data-turbo="true" data-turbo-action="replace">
              NEW turbo frame
            </a><br />
            <a href="index.html?NEW" data-turbo="false">
              NEW full page
            </a>
            <p>number in query string: $QUERY_STRING</p>
        </turbo-frame>
    </template>
</turbo-stream>
EOF

To test:

  1. open index.html in a browser
  2. click the 'full page' link, observe that bonus number changes, query string is now in URL and on the page. You got this part already.
  3. click the 'turbo frame' link, observe that bonus number doesn't change, query string is on the page BUT not in the URL. This is not all of what you asked for, but its close.
  4. open up DevTools, in JS console execute
Turbo.visit(`template.html?${<any number here>}`)
for example:
Turbo.visit(`template.html?${new Date().valueOf()}`)

observe that bonus number does not change, the links get new random numbers, and the query string is BOTH in the URL and on the page. You can do this repeatedly and keep refreshing your turbo frame. This is what you asked for, and it seems to require some JS. I'll update my answer if I find any other html attributes that could obviate the need for JS.

aidan
  • 1,627
  • 17
  • 27
  • oh that's a shame... I was hoping it work almost exactly the same with and without the turbo script included – Alex028502 Oct 11 '21 at 19:16
  • I guess if you combine template.html and index.html and make it somehow detect that it's an ajax call, you can get the url right – Alex028502 Oct 11 '21 at 19:22
  • I'm not so sure about your idea to combine the two. Also, "AJAX call" is kind of ambiguous now; most people use "AJAX" to refer to any asynchronous request, but with Hotwire you have turbo_stream responses now in addition to JSON. – aidan Oct 13 '21 at 01:16
  • I do think that with Ruby on Rails there might be a solution to your question. With Rails (or probably other frameworks), you can template out the shared HTML between `index.html` and `template.html` so that you don't duplicate code. You still need two HTML files though. – aidan Oct 13 '21 at 01:19
  • Also I'm just really curious: why do you need to combine turbo streams and query parameters? – aidan Oct 13 '21 at 01:19
  • It could be any url. This was the easiest to demo... just something where the end result will be the same whether the whole page is reloaded or the frame with the page specific content is fetched and replaced. – Alex028502 Oct 13 '21 at 07:09