hotwire(turbo-stream,turbo-frame,stimuls)를 이용해 자동완성 셀렉트폼을 구현하는 방법을 알아보자.
전체 코드는rails7-turbo-autocomplete-select-box을 참고
크게 아래 4가지 챕터로 구현했다.
- ransack으로 검색기능 구현
- input이벤트시 -> turbo_frame 사용(search_results_22222을 갱신)
- 자동완성된 내용을 선택시 update(patch) 발생 -> turbo_stream으로 search_form_11111을 갱신
- 존재하지 않는 키워드 검색시 신규추가 버튼 클릭 -> create(post) 발생 -> turbo_stream으로 search_form_11111갱신
텍스트 입력시 자동완성 부분만 turbo_frame으로 바꿔주고 자동완성된 내용중 선택시 update경유해서 turbo_stream으로 자동완성 + 검색 폼을 갱신 신규추가시에도 create경유해서 turbo_stream으로 동일하게 갱신해줬다.
각 챕터별 핵심 구현 코드는 아래를 참고
1.
gem 'ransack' -> bundle i
@qq = params[:q]
@q = Person.ransack(params[:q])
@persons = @q.result.order(id: :desc)
@assign_person = Post.first.person
2.
<%= turbo_frame_tag 'search_form_11111' do %>
<%= search_form_for @q, url: root_path,
data: {
turbo_frame: "search_results_22222",
controller: "search-form",
action: "input->search-form#submit"
} do |f| %>
<div class="input-group">
<%= f.search_field :name_cont, class: "shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md h-10", placeholder: "Search…", value: @assign_person.present? ? @assign_person.name : nil %>
</div>
<%= turbo_frame_tag 'search_results_22222' do %>
autocomplete되는 부분
<% end %>
<% end %>
import { Controller } from "@hotwired/stimulus";
// Connects to data-controller="search-form"
export default class extends Controller {
submit(event) {
event.preventDefault();
clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
this.element.requestSubmit();
}, 200);
}
}
3.
<%= button_to person.name, post_assign_people_update_path(person), method: :patch, form: {data: {turbo_frame: "search_form_tQPUuzjH"}} %>
def update
new_assign_person = Person.find(params[:person_id])
@qq = params[:q]
@q = Person.ransack(params[:q])
@persons = @q.result.order(id: :desc)
if Post.first.update(person: new_assign_person)
respond_to do |format|
format.turbo_stream do
@qq = nil
@assign_person = new_assign_person
render turbo_stream: turbo_stream.replace("search_form_11111", partial: "search/search_form_turbo", locals: { person: @person })
end
format.html { redirect_to root_path }
end
else
end
end
4.
<%= button_to post_assign_people_create_path, method: :post, params: { new_person: @qq[:name_cont] }, form: {data: {turbo_frame: "search_form_tQPUuzjH"}}, class: "flex items-center justify-center" do %>
+
<% end %>
def create
@qq = params[:q]
@q = Person.ransack(params[:q])
@persons = @q.result.order(id: :desc)
new_person_name = params[:new_person]
new_person = Person.create(name: new_person_name)
if Post.first.update(person: new_person)
respond_to do |format|
format.turbo_stream do
@qq = nil
@assign_person = new_person
render turbo_stream: turbo_stream.replace("search_form_11111", partial: "search/search_form_turbo", locals: { person: @person })
end
format.html { redirect_to root_path }
end
else
# エラーハンドリング
end
end
@qq가 nil이면 자동완성폼을 안보여주므로 create성공시 @qq = nil을 넣어주는게 포인트
5.
엄청난 문제점에 봉착
form하위에서 search_form_for
를 사용할 수 없음(form하위에 form을 만들어버리므로)
고로 form안에서 쓰지말거나 search_form_for을 포기하거나 둘중하나는 해야할듯..
다른 포스트에서 search_form_for
을 사용하지 않고 자동완성 기능을 만드는법에 대해 정리 예정