Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

how to handle error and redirect? #47

Open
laptopmutia opened this issue Aug 3, 2022 · 10 comments
Open

how to handle error and redirect? #47

laptopmutia opened this issue Aug 3, 2022 · 10 comments

Comments

@laptopmutia
Copy link

laptopmutia commented Aug 3, 2022

any examples?

so I have rails application with turbo and turbo stream
then I use stimulus js with request.js to send a patch request but I don't know how to handle the error

import { Controller } from "@hotwired/stimulus"
import onScan from "onscan.js"
import { patch } from "@rails/request.js";
export default class extends Controller {
  static values = {
    sellingId: Number
  }
  connect() {
    let sellingId = this.sellingIdValue
    onScan.attachTo(document, {
      suffixKeyCodes: [13], // enter-key expected at the end of a scan
      onScan: function (sCode, _) { // Alternative to document.addEventListener('scan')
        patch(`/sellings/${sellingId}/add-item`, {
          body: { "selling": { "inventory_id": sCode } },
          contentType: "application/json",
          responseKind: "turbo-stream"
        })
      },
    });
  }
}

here is my controller

  def add_item
    idnya = params.require(:selling).permit(:inventory_id)[:inventory_id]

    if idnya && @selling.update_entry(idnya)
      respond_to do |format|
        format.html { redirect_to selling_path(@selling), notice: "Item was successfully Added." }
        format.turbo_stream { flash.now[:notice] = "Item ditambahkan." }
      end
    else
      flash.now[:error] = "Item tidak ditemukan."
      render :show, status: :unprocessable_entity
    end
  end

I have tried to add this
format.turbo_stream { flash.now[:error] = "Item not found." }
it could render the flash message just fine

but I felt its wrong since its return 200 ok instead 422 error

@marcelolx
Copy link
Collaborator

Hey @laptopmutia,

Request.JS is just a wrapper around the Fetch API, so you should be able to handle errors as you would handle them with the Fetch API. See those examples here to see if it helps

Fetch API does follow redirects by default, but it won't render the redirected page so what you will have to do very likely is access the response of the redirect and propose a visit with Turbo, something like

patch(`/sellings/${sellingId}/add-item`, {
  body: { "selling": { "inventory_id": sCode } },
  contentType: "application/json",
  responseKind: "turbo-stream"
}).then((res) => {
   if (res.redirected) {
     Turbo.visit(new URL(res.url.toString(), document.baseURI))
   }
})

I haven't tested the code above, but something like that may work. Just have in mind that this will end up making two requests to the redirected location:

1st -> Fetch API because it follows the redirect (if you set redirect: 'manual' then you won't be able to know which is the redirected URL, see whatwg/fetch#763)
2nd -> The Turbo.visit itself will make the request to the redirected location to render the page

@laptopmutia
Copy link
Author

laptopmutia commented Aug 3, 2022

I just confused why the turbo stream is not rendered with 422 apparently the default behavior for stream processing is 200 ok

I fix my code wiht this

connect() {
  let sellingId = this.sellingIdValue
  onScan.attachTo(document, {
    suffixKeyCodes: [13], // enter-key expected at the end of a scan
    onScan: async function (sCode, _) { // Alternative to document.addEventListener('scan')
      const response = await patch(`/sellings/${sellingId}/add-item`, {
        body: { "selling": { "inventory_id": sCode } },
        contentType: "application/json",
        responseKind: "turbo-stream"
      })
      if (response.unprocessableEntity) {
        await response.renderTurboStream()
      }
    },
  });

  onScan.setOptions(document, {
    minLength: 1 // change the quantity to 5 for every scan
  });

}

is this code good? I kinda hesitate with the placement of async and await

@marcelolx
Copy link
Collaborator

Yeah, Request.JS does only process a turbo stream if the response is ok.

is this code good? I kinda hesitate with the placement of async and await

It is, you should be good with this.

We could improve the docs to mention that the turbo stream will only be processed if the response is ok https://github.com/rails/request.js#turbo-streams, or we could consider processing it always if the response is a Turbo Stream 🤔

@laptopmutia
Copy link
Author

I think I prefer the later, but not sure about it since all the example about turbo and turbo-rails never use it, they tend to response the failed request with html page/response

@marcelolx
Copy link
Collaborator

Yes, I don't see either why we wouldn't process the turbo stream.

@brentgreeff
Copy link

@marcelolx - My code is slightly different.

  const p = await post(to_url, { body: JSON.stringify(params) })

  if (p.redirected) {
    const redirect_url = p.response.url.toString()
     Turbo.visit(new URL(redirect_url, document.baseURI))
  }

Why does turbo do all the magic body-tag swapping if I create a vanilla rails form - but here I need to do the heavy lifting?

How can I tie the response into Turbo's generic behavior.

like

Turbo.magic(response)

Which should handle the redirect - ie 1 request not 2 - and it should behave as normal for turbo-replace etc.

@marcelolx
Copy link
Collaborator

@brentgreeff Because it is Turbo that intercepts the form submission, submits it, and then handles the response while Request.JS is only a wrapper around the Fetch API (doing all this magic ).

As far as I know, Turbo does not offer anything like that to handle the response of a request made outside of Turbo.

Couldn't you submit a form and let Turbo intercept the form submission and consequently handle the response of it?

@brentgreeff
Copy link

@marcelolx - Yea, I do have examples of that in my code, but this is one of those forms in a table stories - I have 1 form per row. - not sure if it would be valid. - I need to collect values from different cells and collate params to generate my request.

  • maybe I will look at how Turbo works, - as you said, if Turbo is intercepting form submits, - does it also use fetch - there might be code I can extract into a common: Turbo.magic(request) - or I have totally mis-understood how Turbo works - either way I might learn something.

@marcelolx
Copy link
Collaborator

marcelolx commented Aug 9, 2022

@brentgreeff Yeah, I did look yesterday at Turbo and yes, it uses Fetch to make the requests. For some reason, I couldn't understand yet, Turbo is able to access the response of the redirected location and avoid another request to the redirected path but I couldn't figure out how (I did not invest much time, so I probably was missing something).

If you find a solution, please share! We already process turbo streams, so if we could handle redirects (tell turbo to handle it somehow) it definitely would be valuable.

btw, I'll take a look into this again once I have some free time.

@brentgreeff
Copy link

@marcelolx - thanks for investing the time. -
I guess if people are using Request.js - they want a lot of control - so an explicit handoff to Turbo might be required. - but Turbo should have an interface to allow this. - response could be a redirect - or it could be turbo_stream-tags, or a normal html page.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants