aks
aks

Reputation: 9501

Unable to use flash when rendering same page with Turbolinks

I am trying to show flash message after hitting the update endpoint. I want to keep user on the same page and tell him that the info has been updated. I have turbolinks enabled.

Here is how my action looks like:

def update
    @form = Form.find(params[:id])

    if @form.update(form_params)
      respond_to do |format|
        flash.now[:notice] = "Form updated successfully"
        format.html  { render 'edit' }
        format.js { render 'edit' }
        # format.html  { redirect_to edit_form_path(@form), notice: "Form updated successfully" }
        format.json { render json: {message: "Form updated successfully", form: @form} }
      end
    else
      respond_to do |format|
        format.html { render 'new'}
        format.json { render json: {errors: @form.errors}, status: :unprocessable_entity }
      end    
    end
  end

I have tried both flash and flash.now both yields no result. If you see the line which is commented out that will redirect to the same page and render the flash.

here is how I am printing the falsh:

<%= notice %>
<%= flash.notice %>

I have used both but they don't seem to work.

From my observation, I can see that Turbolinks hit the format.js endpoint. Can anyone help me with this?

Edit

This is how my form looks like:

<%= form_with(model: @form, method: 'put') do |f| %>
  <!-- name -->
  <div class="field">
    <label class="label">
      From Name
      <sup class="is-danger">*</sup>
    </label>
    <p class="control">
      <%= f.text_field :name, class: "input" %>
    </p>
    <% if @form.errors.any? %>
    <p class="help is-danger">
      Form name <%= @form.errors[:name].first %>
    </p>
    <% end %>
  </div>

  <!-- create button -->
  <div class="control">
    <%= f.submit class: "button is-primary" %>
  </div>
<% end %>

Upvotes: 2

Views: 1619

Answers (2)

Kieran E
Kieran E

Reputation: 3676

Ajax forms with Rails

Warning: I'm on my phone and can't test this. But this should work regardless.

To signify that you want to submit a form through Ajax, you'll need to put remote: true on your form element, like so:

<%= form_with model: @form, method: 'put', remote: true do |f| %>
 <!-- rest of the form -->
<% end %>

When you click submit, you'll notice the page doesn't change but your Rails logs still show that you pinged your controller. Neat!

Next, you need to figure out what to do with this data. That's what respond_to is for.

respond_to will send different responses depending on how the controller was accessed. In your case, the important one is format.js, but you'll always want to provide a fallback for HTML.

def update
    @form = Form.find(params[:id])

    if @form.update form_params
      respond_to do |format|
        flash.now[:notice] = "Form updated successfully"
        format.html  { redirect_to <SHOW PATH> }
        format.js # Will automatically look for update.js.erb
        format.json { render json: {message: "Form updated successfully", form: @form} }
      end
    else
      respond_to do |format|
        format.html { render :edit }
        format.json { render json: {errors: @form.errors}, status: :unprocessable_entity }
      end    
    end
  end
end

Note that I made a few code changes.

Now when you submit the form, you should see that nothing changes, the controller is hit, the record is processed, then Rails will look for update.js.erb. That's where we need to go next!

After you create that file, fill in:

console.log("<%= notice %>");

At this point, verify that you can see the flash in your console after you submit.

You need to abstract the HTML that shows your flash messages out to a partial that I'm going to call _notifications.html.erb and it'll have this contents:

<!-- _notifications.html.erb -->

<div id="flash">
  <% if notice.present? %>
    <%= notice %>
  <% end %>
</div>

Finally, you have to replace the contents of your flash. Remove everything from update.js.erb and try this:

$('#flash').replaceWith("<%= j(render 'notifications') %>");
// Or
var flash = document.getElementById('flash'),
  flashParent = flash.parentNode;

flashParent.replaceChild("<%= j(render 'notifications') %>", flash);

That's it! Give it a go and let me know if it works.

Again, this was from my phone. You might have to fix a syntax error or two.


More reading

Upvotes: 1

Kieran E
Kieran E

Reputation: 3676

Turbolinks form with Rails

If the Ajax method is too much of an issue, remove remote: true and try this:

def update
    @form = Form.find(params[:id])

    if @form.update form_params
      respond_to do |format|
        format.html  { redirect_to <EDIT PATH>, notice: "Form updated successfully" }
        format.json { render json: {message: "Form updated successfully", form: @form} }
      end
    else
      respond_to do |format|
        format.html { render :edit }
        format.json { render json: {errors: @form.errors}, status: :unprocessable_entity }
      end    
    end
  end
end

Then you should have flash data on your page. Try using <%= flash.inspect %> to verify. Turbolinks should handle most of the Magic here.

The downside being that Turbolinks can give you some issues on mobile. I recommend looking into that if you try this method.

Personally, I prefer the Ajax method. It's a little more work but it gives you more fine-grained control.

Upvotes: 0

Related Questions