form_with local and flash messages

A little trouble

I was trying to get the flash message that is common in Rails, but I can't get only flash.now. .. ..

For example, write the following code to log in as a user in SessionController.

 def create
    @user = User.find_by(login_id: params[:session][:login_id])
    if @user && @user.authenticate(params[:session][:password])
      log_in(@user)
      redirect_to @user, success: 'You are now logged'
    else
      flash.now[:danger] = 'I failed to login.'
      render :new
    end
  end

When the login was successful, that is, "Login" is displayed after redirect_to, but when the login fails, that is, "Login failed" is not displayed at all.

problem

In conclusion, it was because I didn't add local: true to form_with. So I added local: true as below and it worked.

= form_with scope: :session, url: login_path, local: true do |f|
  = f.label :login_id, 'Login ID'
  = f.text_field :login_id, { class: 'form-control' }
  = f.label :password, 'password'
  = f.password_field :password, { class: 'form-control' }
  = f.submit 'Login', class: 'btn btn-primary'

Why local: true is needed

By the way, I knew that form_with had a local option. When using form_with, I implemented it while investigating various things, so the information to be referred to was mostly local: true.

However, even if you don't have local: true, you can set up a login session normally, and you don't have to. I noticed that, so I removed local: true. However, since I hit such a wall this time, I thought it was not good to leave this local: true as it is, so I will dig a little deeper.

What is the local option in form_with in the first place?

Let's take a look at the Rails documentation for now. https://railsdoc.com/page/form_with Then it says "local: invalid remote transmission". This alone is refreshing.

I wanted to see it in a little more detail, so I hit this one. https://techracho.bpsinc.jp/hachi8833/2020_03_09/39502#6 Then, there is a description that "If you specify local: true, remote + unobtrusive XHR submission of the form is disabled (remote + unobtrusive XHR is enabled in the default form)".

If you look at this, it's just a question mark, but "unobtrusive XHR transmission" is Ajax communication. So, the local option in form_with is "whether form_with in the relevant part disables (true) or enables (false) Ajax communication".

Ajax communication and this flash message problem consideration

By the way, from here on, it's completely speculative, so I think it's an incorrect description. Please use it as a reference. By the way, the detailed description about Ajax is a little different from the main story, so I will omit it. If you want to know more details, please see Reference at the end of this article.

Consideration

Why the original code didn't work

--If local is not specified in form_with, local: false is set by default, that is, Ajax communication is enabled. ――The code I wrote this time is also the same. --As a result, the process of creating a login session is performed by asynchronous communication. --Because of asynchronous communication, even if you go to generate a login session from the client side to the server side, whether it worked or not is not returned from the server side to the client side. --For example, information such as HTTP status 200 or 404 is not returned. --In this case, "flash.now [: danger]" is not executed because the information "login failed" is not returned from the server side.

By the way, as I wrote at the beginning, "Why did I get the message" I logged in "when I did not get the result of going to generate a login session from the server side without local: true, and the screen after login. The next question arises: "Does it transition to?"

I think this is the difference between the render method and the redirect_to method. Roughly speaking, render doesn't send HTTP requests to the server, and redirect_to sends HTTP requests to the server.

In this case, redirect_to is executed after the login process, that is, the request is sent from the server. Since the redirect_to method returns HTML in response to the request to the client, screen transition occurs on the client side.

local: true why it worked

It's the inside out of the above, but by setting local: true and disabling Ajax communication, that is, performing synchronous communication, the result of whether you could log in from the server will be returned properly. It will be able to detect it and issue a flash message.

Reference article

Recommended Posts

form_with local and flash messages
Display Flash messages in Rails
[Rails] How to use flash messages
[Introduction to Rails] form_with (local: true)