了解如何在Ruby on Rails中使用Turbo



我试图了解如何使用Turbo来刷新页面的内容,而无需刷新页面。我已经通过了一些视频和网络教程,但它并不适用于我有我的页面设置的方式,我试图弄清楚如果我的设置是糟糕的设计,我应该改变它,或者如果它可能添加Turbo功能到我的代码。我是Ruby on Rails和Turbo的新手,所以可能没有足够的理解来点击它。基本上,我的索引页上有一个表单,允许用户选择州,一旦提交,就会显示一个新的选择框,其中包含该州的城市以及该州所有设施的列表。一旦用户选择一个城市并提交表单,设施列表就仅限于该城市。表单被提交回索引页。我想通过设施部分更新城市选择框和表,该部分根据表单提交在索引页上显示设施,但是从我看到的每个教程中,表单都被提交给不同的函数,该函数调用新页面来替换当前部分。是否有办法做到这一点,从我目前的代码,或者我需要有窗体调用不同的功能,以及?

index.html.erb

<% provide(:title, "Facilities") %>
<div class="row">
<div class="col-md-8 col-md-offset-2">
<br>
<%= form_with(url: facilities_path, method: :get, remote: true) do |f| %>
<table class="table table-borderless">
<tr>
<td>
<div class="form-inline">
<%= f.label :state_id, "State" %> &nbsp;
<%= f.collection_select :state_id, @states, :id, :state_long, {:selected => params[:state_id], include_blank: 'Select A State'}, {class: 'form-control'}  %>
</div>
</td>
<% if !params[:state_id].blank? %>
<td>
<div class="form-inline">
<%= f.label :city_id, "City" %> &nbsp;
<%= f.collection_select :city_id, @cities, :id, :city_titlecase, {:selected => params[:city_id], include_blank: 'Select A City'}, {class: 'form-control'} %>
</div>
</td>
<% end %>
<td><%= f.submit "Update Search", name: nil, class: "btn btn-primary" %></td>
</tr>
</table>
<% end %>
</div>
</div>
<div class="row">
<div class="col-md-8 col-md-offset-2">
<% if @facilities.any? %>
<table class="table table-hover table-striped table-bordered">
<thead>
<tr>
<th>&nbsp;</th>
<th>Facility</th>
<th>City</th>
<th>State</th>
</tr>
</thead>
<tbody>
<%= render @facilities %>
</tbody>
</table>
<span style="float:right"><%= will_paginate %></span>
<% end %>
</div>
</div>

Facilities_Controller.rb

def index
@states = State.all
#Get the state id from the database based on the state long name
current_state_db = State.find_by(state_long: current_state)
if params[:state_id].blank?
@facilities = Facility.all.joins(:state, :city).order("states.state_short, cities.city, facility_name").paginate(page: params[:page])
else
@cities = City.where(state_id: params[:state_id])
if params[:city_id].blank?
@facilities = Facility.where(state_id: params[:state_id]).joins(:state, :city).order("states.state_short, cities.city, facility_name").paginate(page: params[:page])
else
#Check to make sure the city_id is in the selected state -> if the user changed the state a city was selected, it would keep the old city in the params
expected_state = City.find_by(id: params[:city_id]).state_id
if params[:state_id].to_i == expected_state
@facilities = Facility.where(state_id: params[:state_id], city_id: params[:city_id]).joins(:state, :city).order("states.state_short, cities.city, facility_name").paginate(page: params[:page])
else
#The current selected city is not in the state
@facilities = Facility.where(state_id: params[:state_id]).joins(:state, :city).order("states.state_short, cities.city, facility_name").paginate(page: params[:page])
end
end
end
end

我已经看过了多个turbo视频和教程,但它们都是指调用不同函数和视图的表单,然后用相同的turbo id替换当前视图。我希望找到一种方法,以提交到索引函数的形式做到这一点。我还在学习有关Turbo和AJAX的知识,但是从我所读到的理解来看,Turbo在幕后使用AJAX来完成这些工作。

如果你把它放在turbo框架中,只要你用匹配的框架响应,那么提交表单的位置就没有区别。也许这个例子有点复杂,但它展示了你可以做什么。它是自动提交到同一页面,控制器动作是空的,唯一不明显的事情是不同的帧更新不同的字段:

<%= tag.div data: {controller: "selector"} do %>
<%= form_with url: "/", data: {selector_target: "form"} do |f| %>
<%= f.collection_select :state, State.all, :id, :name, {prompt: true},
{data: {action: "selector#nextFrame", selector_frame_param: "cities"}} %>
# ^ this will update v this frame
<%= turbo_frame_tag :cities do %>
<% if (current_state = State.find_by(id: params[:state])) %>
<%= f.collection_select :city, current_state.cities, :id, :name, {prompt: true},
{data: {action: "selector#nextFrame", selector_frame_param: "facilities"}} %>
# ^ this will update v this frame
<%= turbo_frame_tag :facilities do %>
<% if (current_city = current_state.cities.find_by(id: params[:city])) %>
Facilities for <%= current_city.name %>
...
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>

注意,我没有设置selected选项,因为目标帧之外的字段不会重置。

使用刺激动态更新框架目标并提交表单:

bin/rails g stimulus selector
// app/javascript/controllers/selector_controller.js
import { Controller } from "@hotwired/stimulus";
export default class extends Controller {
static targets = ["form"];
nextFrame(event) {
const form = this.formTarget;
form.dataset.turboFrame = event.params.frame; // load the next frame
form.requestSubmit();                         // after submitting the form
}
}

最新更新