I’m working on a Rails 8.0 app that uses Turbo Streams to dynamically update certain sections of the page after uploading a CSV file. However, after I upload the CSV and process the data, the page does not automatically update with the new content. I have to manually refresh the page to see the changes.
Here’s what I have so far:
Code in the Controller (ProductsController):
<code>class ProductsController < ApplicationController
@csv_data = session[:csv_data] || [] # Load uploaded CSV data from session
if params[:file].present?
CSV.foreach(csv_file.path, headers: true) do |row|
"sku_code" => row["sku_code"],
"product_id" => row["product_id"],
"product_title" => row["product_title"],
"sell_price" => row["sell_price"],
"tax_rate" => row["tax_rate"],
"suppliers" => parse_supplier_data(row)
@csv_data << relevant_data
session[:csv_data] = @csv_data
format.turbo_stream { render turbo_stream: [
turbo_stream.replace("products_table", partial: "products_table", locals: { csv_data: @csv_data }),
turbo_stream.replace("action_buttons", partial: "action_buttons")
format.html { redirect_to products_path, notice: "CSV uploaded and parsed successfully." }
redirect_to products_path, alert: "Please upload a valid CSV file."
rescue StandardError => e
Rails.logger.error "Error processing CSV: #{e.message}"
redirect_to products_path, alert: "Error processing CSV: #{e.message}"
session[:csv_data] = nil # Clear the CSV data stored in the session
redirect_to products_path, notice: "Data cleared. You can upload a new CSV."
def parse_supplier_data(row)
supplier_columns = row.headers.select { |col| col.match?(/supplier_d+_/) }
supplier_ids = supplier_columns.map { |col| col[/supplier_(d+)_/, 1] }.uniq
supplier_ids.each do |supplier_id|
"supplier_id" => row["supplier_#{supplier_id}_id"],
"supplier_name" => row["supplier_#{supplier_id}_name"],
"supplier_cost" => row["supplier_#{supplier_id}_cost"],
"supplier_ref" => row["supplier_#{supplier_id}_ref"]
<code>class ProductsController < ApplicationController
require 'csv'
def index
@csv_data = session[:csv_data] || [] # Load uploaded CSV data from session
end
def import
if params[:file].present?
csv_file = params[:file]
@csv_data = []
CSV.foreach(csv_file.path, headers: true) do |row|
relevant_data = {
"sku_code" => row["sku_code"],
"product_id" => row["product_id"],
"product_title" => row["product_title"],
"sell_price" => row["sell_price"],
"tax_rate" => row["tax_rate"],
"suppliers" => parse_supplier_data(row)
}
@csv_data << relevant_data
end
session[:csv_data] = @csv_data
respond_to do |format|
format.turbo_stream { render turbo_stream: [
turbo_stream.replace("products_table", partial: "products_table", locals: { csv_data: @csv_data }),
turbo_stream.replace("action_buttons", partial: "action_buttons")
]}
format.html { redirect_to products_path, notice: "CSV uploaded and parsed successfully." }
end
else
redirect_to products_path, alert: "Please upload a valid CSV file."
end
rescue StandardError => e
Rails.logger.error "Error processing CSV: #{e.message}"
redirect_to products_path, alert: "Error processing CSV: #{e.message}"
end
def clear
session[:csv_data] = nil # Clear the CSV data stored in the session
redirect_to products_path, notice: "Data cleared. You can upload a new CSV."
end
private
def parse_supplier_data(row)
suppliers = []
supplier_columns = row.headers.select { |col| col.match?(/supplier_d+_/) }
supplier_ids = supplier_columns.map { |col| col[/supplier_(d+)_/, 1] }.uniq
supplier_ids.each do |supplier_id|
suppliers << {
"supplier_id" => row["supplier_#{supplier_id}_id"],
"supplier_name" => row["supplier_#{supplier_id}_name"],
"supplier_cost" => row["supplier_#{supplier_id}_cost"],
"supplier_ref" => row["supplier_#{supplier_id}_ref"]
}
end
suppliers
end
end
</code>
class ProductsController < ApplicationController
require 'csv'
def index
@csv_data = session[:csv_data] || [] # Load uploaded CSV data from session
end
def import
if params[:file].present?
csv_file = params[:file]
@csv_data = []
CSV.foreach(csv_file.path, headers: true) do |row|
relevant_data = {
"sku_code" => row["sku_code"],
"product_id" => row["product_id"],
"product_title" => row["product_title"],
"sell_price" => row["sell_price"],
"tax_rate" => row["tax_rate"],
"suppliers" => parse_supplier_data(row)
}
@csv_data << relevant_data
end
session[:csv_data] = @csv_data
respond_to do |format|
format.turbo_stream { render turbo_stream: [
turbo_stream.replace("products_table", partial: "products_table", locals: { csv_data: @csv_data }),
turbo_stream.replace("action_buttons", partial: "action_buttons")
]}
format.html { redirect_to products_path, notice: "CSV uploaded and parsed successfully." }
end
else
redirect_to products_path, alert: "Please upload a valid CSV file."
end
rescue StandardError => e
Rails.logger.error "Error processing CSV: #{e.message}"
redirect_to products_path, alert: "Error processing CSV: #{e.message}"
end
def clear
session[:csv_data] = nil # Clear the CSV data stored in the session
redirect_to products_path, notice: "Data cleared. You can upload a new CSV."
end
private
def parse_supplier_data(row)
suppliers = []
supplier_columns = row.headers.select { |col| col.match?(/supplier_d+_/) }
supplier_ids = supplier_columns.map { |col| col[/supplier_(d+)_/, 1] }.uniq
supplier_ids.each do |supplier_id|
suppliers << {
"supplier_id" => row["supplier_#{supplier_id}_id"],
"supplier_name" => row["supplier_#{supplier_id}_name"],
"supplier_cost" => row["supplier_#{supplier_id}_cost"],
"supplier_ref" => row["supplier_#{supplier_id}_ref"]
}
end
suppliers
end
end
Code in the View (index.html.erb):
erb
<code><h1>Manage Supplier Associations</h1>
<% if @csv_data.blank? %>
<!-- File Upload Form -->
<h3>Upload a CSV File</h3>
<%= form_with url: import_products_path, method: :post, multipart: true, class: "mb-4" do |f| %>
<%= f.file_field :file, class: "form-control", accept: ".csv" %>
<%= f.submit "Upload CSV", class: "btn btn-primary" %>
<div id="products_table">
<%= render partial: "products_table", locals: { csv_data: @csv_data } %>
<div id="action_buttons" class="mt-3">
<%= link_to "Export CSV", export_products_path, class: "btn btn-secondary" %>
<%= link_to "Clear Data", clear_products_path, method: :get, class: "btn btn-danger" %>
<code><h1>Manage Supplier Associations</h1>
<% if @csv_data.blank? %>
<!-- File Upload Form -->
<div class="card">
<div class="card-body">
<h3>Upload a CSV File</h3>
<%= form_with url: import_products_path, method: :post, multipart: true, class: "mb-4" do |f| %>
<div class="mb-3">
<%= f.file_field :file, class: "form-control", accept: ".csv" %>
</div>
<div>
<%= f.submit "Upload CSV", class: "btn btn-primary" %>
</div>
<% end %>
</div>
</div>
<% else %>
<!-- Table Section -->
<div id="products_table">
<%= render partial: "products_table", locals: { csv_data: @csv_data } %>
</div>
<!-- Buttons Section -->
<div id="action_buttons" class="mt-3">
<%= link_to "Export CSV", export_products_path, class: "btn btn-secondary" %>
<%= link_to "Clear Data", clear_products_path, method: :get, class: "btn btn-danger" %>
</div>
<% end %>
</code>
<h1>Manage Supplier Associations</h1>
<% if @csv_data.blank? %>
<!-- File Upload Form -->
<div class="card">
<div class="card-body">
<h3>Upload a CSV File</h3>
<%= form_with url: import_products_path, method: :post, multipart: true, class: "mb-4" do |f| %>
<div class="mb-3">
<%= f.file_field :file, class: "form-control", accept: ".csv" %>
</div>
<div>
<%= f.submit "Upload CSV", class: "btn btn-primary" %>
</div>
<% end %>
</div>
</div>
<% else %>
<!-- Table Section -->
<div id="products_table">
<%= render partial: "products_table", locals: { csv_data: @csv_data } %>
</div>
<!-- Buttons Section -->
<div id="action_buttons" class="mt-3">
<%= link_to "Export CSV", export_products_path, class: "btn btn-secondary" %>
<%= link_to "Clear Data", clear_products_path, method: :get, class: "btn btn-danger" %>
</div>
<% end %>
roblem:
When I upload the CSV and it is processed, the Turbo Stream updates the table and buttons, but the changes do not appear until I manually refresh the page. I expect the page to automatically update without the need for a manual refresh after the file is uploaded.
What I have tried:
Using redirect_to after uploading the file to re-render the page.
Wrapping the Turbo Stream updates in turbo_stream.replace to replace the relevant elements (products_table and action_buttons).
Ensuring that the Turbo Stream is properly set up in my controller.
Ensuring the partials are being rendered correctly with the updated @csv_data.
What I need help with:
Why does the page not automatically update after the CSV is uploaded?
How can I make sure the page updates without needing a manual refresh?
Any help would be greatly appreciated! Thanks!