ruby-on-railsinertiajs

Rails and Inertia.js How to Implement POST Request


I have a Rails 8 application which is using Inertia.js and Vue. I am not sure how to implement a redirect_to() request using Inertia.js within Rails. My attempts are not working. I am using https://github.com/skryukov/inertia_rails-contrib in integrating Inertia.js into Rails.

Github repo is at https://github.com/Chrisgo-75/rails8_inertiajs_vuejs

The worflow is

a) HTML button "Add to Cart" is found on /app/frontend/pages/Store/Index.vue.

# /app/frontend/pages/Store/Index.vue

<template>
  <button
    @click="addToCart(product.id)"
    class="ml-4 rounded-lg py-1 px-2 text-white bg-green-600"
    >
    Add to Cart
  </button>
</template>

<script setup lang="ts">
import axios from "axios";
import { router } from '@inertiajs/vue3';

const addToCart = async (productId: number) => {
  try {
    const response = await axios.post('/line_items',
        { product_id: productId },
        { headers: { 'X-CSRF-Token': csrfToken } } // Include CSRF token
    );

    alert('Product added to cart!');

    // Extract the cart ID from the response.
    const cartId = response.data.cart_id;

    router.visit(`/carts/${cartId}`);
  } catch (error) {
    console.error('Error adding to cart: ', error);
    alert('Failed to add product to cart.');
  }
};
</script>

b) Clicking the "Add to Cart" button goes to /app/controllers/line_items_controller.rb Create Controller action.

# line_items_controller.rb

def create
    product = Product.find(params[:product_id])
    @line_item = @cart.line_items.build(product: product)

    if @line_item.save
      # redirect_to @line_item.cart, notice: "Line item was successfully created."
      # redirect_to @cart, notice: "Cart was successfully updated."
      # redirect_to inertia: cart_path(@cart), notice: "Cart was successfully updated."
      render json: { cart_id: @cart.id }
    else
      # redirect_to new_line_item_url, inertia: { errors: @line_item.errors }
      # render inertia: "Carts/Show", props: { cart: @cart, errors: @line_item.errors }
      render json: { errors: @line_item.errors.full_messages },
             status: :unprocessable_entity
    end
end # END def create

c) The LineItems Controller Create action passes cart_id back to /app/frontend/pages/Store/Index.vue where it performs router.visit('/carts/${cartId}').

Does anyone have a solution for implementing a redirect_to() Inertia.js request within Rails?


Solution

  • Axios does redirect but you have to handle the final response yourself, which is what you're doing when you render instead of a redirect.

    Inertia provides an api for manual requests and it handles redirects and page updates. Instead of axios.post do this:

    <template>
      <button @click="addToCart(product.id)">
        Add to Cart
      </button>
    </template>
    
    <script setup>
      import { router } from '@inertiajs/vue3';
    
      const addToCart = (product_id) => {
        router.visit('/line_items', {
          method: 'POST',
          data: { product_id },
        });
      }
    </script>
    

    https://inertia-rails.dev/guide/manual-visits

    Or use Link component:

    <template>
      <Link
        href="/line_items"
        method="post"
        :data="{ product_id: product.id }"
      >
        Add to Cart
      </Link>
    </template>
    
    <script setup>
      import { Link } from '@inertiajs/vue3'
    </script>
    

    https://inertia-rails.dev/guide/links