在 Stimulus 控制器中记忆 Bootstrap5 模态
Memoize a Bootstrap5 modal inside a Stimulus controller
我在部分定义的模态中有一个表单:
<!-- app/views/events/new_event_modal.html.erb -->
<div data-controller="events--form">
<div class="modal fade" id="new-event-modal" tabindex="-1" aria-labelledby="new-event-label" aria-hidden="true" data-events--form-target="modal">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title text-light" id="new-event-label">Ajouter un évennement</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Fermer"></button>
</div>
<div class="modal-body">
<%= render partial: 'events/form',
locals: { event: event,
calendars: calendars,
users: users } %>
</div>
</div>
</div>
</div>
</div>
<!-- app/views/events/_form.html.erb -->
<%= turbo_frame_tag dom_id(event) do %>
<div class="container text-light">
<%= form_with(model: event || Event.new) do |form| %>
<!-- form's content omitted -->
<div class="actions modal-footer">
<%= form.submit class: 'btn btn-primary' %>
</div>
<% end %>
</div>
<% end %>
因为它是一个日历应用程序,所以我用用户选择的日期预先填写表格,然后显示模态。这是通过一些“+”图标和 Stimulus 控制器完成的:
# app/views/simple_calendar/_mounth_calendar.html.erb
# each day has this icon
<%= image_tag 'add.svg',
size: '20x20',
alt: '+',
data: {
action: 'click->events--form#configure_and_show_modal',
date: day.strftime('%d/%m/%Y')
}
%>
// app/javascript/packs/controllers/events/form_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = [ "modal" ]
connect() {
this.element.addEventListener('turbo:submit-end', (event) => {
if (event.detail.success) {
this.hide_modal();
}
});
}
configure_and_show_modal(event) {
const date = event.target.dataset.date;
this.configure_flatpickr(date);
this.show_modal();
}
configure_flatpickr(date) {
flatpickr("[data-behavior='flatpickr']", {
altInput: true,
altFormat: 'd F Y',
altInputClass: 'form-control input text-dark',
dateFormat: 'd/m/Y H:i',
defaultDate: date,
mode: 'range',
})
}
show_modal() {
this.modal.show();
}
hide_modal() {
this.modal.hide();
}
get modal() {
return new bootstrap.Modal(this.modalTarget);
}
}
问题是get modal()
每次都实例化一个新对象,因此调用hide_modal()
会在新创建的对象上调用.hide()
并且不会隐藏显示的模态。
我知道我需要某种记忆,但我不知道如何在 Stimulus 控制器上实现它。
使用 ruby 我们会做类似的事情:
@modal ||= get_modal
谁能指导我这个方向?
实现此目标的一种简单方法是将模态实例存储在 class 实例上。
这样您仍然可以从您的 getter 访问它,但不必每次都重新创建它。
_modalInstance
只是一个约定,下划线表示应该是private ish。但是,如果控制器以某种方式断开连接并且必须重新连接已显示的模态,这可能会导致问题。
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = [ "modal" ]
get modal() {
if (this._modalInstance) return this._modalInstance;
const modal = new bootstrap.Modal(this.modalTarget);
this._modalInstance = modal;
return this._modalInstance;
}
}
实现此目的的更好方法是使用 Bootstrap JavaScript API.
bootstrap.Modal.getOrCreateInstance(myModalEl)
https://getbootstrap.com/docs/5.1/components/modal/#getorcreateinstance
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = [ "modal" ]
get modal() {
return bootstrap.Modal.getOrCreateInstance(this.modalTarget);
}
}
以下是我实现记忆的方式:
get modal() {
if (this._modal == undefined) {
this._modal = new bootstrap.Modal(this.modalTarget);
}
return this._modal
}
我在部分定义的模态中有一个表单:
<!-- app/views/events/new_event_modal.html.erb -->
<div data-controller="events--form">
<div class="modal fade" id="new-event-modal" tabindex="-1" aria-labelledby="new-event-label" aria-hidden="true" data-events--form-target="modal">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title text-light" id="new-event-label">Ajouter un évennement</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Fermer"></button>
</div>
<div class="modal-body">
<%= render partial: 'events/form',
locals: { event: event,
calendars: calendars,
users: users } %>
</div>
</div>
</div>
</div>
</div>
<!-- app/views/events/_form.html.erb -->
<%= turbo_frame_tag dom_id(event) do %>
<div class="container text-light">
<%= form_with(model: event || Event.new) do |form| %>
<!-- form's content omitted -->
<div class="actions modal-footer">
<%= form.submit class: 'btn btn-primary' %>
</div>
<% end %>
</div>
<% end %>
因为它是一个日历应用程序,所以我用用户选择的日期预先填写表格,然后显示模态。这是通过一些“+”图标和 Stimulus 控制器完成的:
# app/views/simple_calendar/_mounth_calendar.html.erb
# each day has this icon
<%= image_tag 'add.svg',
size: '20x20',
alt: '+',
data: {
action: 'click->events--form#configure_and_show_modal',
date: day.strftime('%d/%m/%Y')
}
%>
// app/javascript/packs/controllers/events/form_controller.js
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = [ "modal" ]
connect() {
this.element.addEventListener('turbo:submit-end', (event) => {
if (event.detail.success) {
this.hide_modal();
}
});
}
configure_and_show_modal(event) {
const date = event.target.dataset.date;
this.configure_flatpickr(date);
this.show_modal();
}
configure_flatpickr(date) {
flatpickr("[data-behavior='flatpickr']", {
altInput: true,
altFormat: 'd F Y',
altInputClass: 'form-control input text-dark',
dateFormat: 'd/m/Y H:i',
defaultDate: date,
mode: 'range',
})
}
show_modal() {
this.modal.show();
}
hide_modal() {
this.modal.hide();
}
get modal() {
return new bootstrap.Modal(this.modalTarget);
}
}
问题是get modal()
每次都实例化一个新对象,因此调用hide_modal()
会在新创建的对象上调用.hide()
并且不会隐藏显示的模态。
我知道我需要某种记忆,但我不知道如何在 Stimulus 控制器上实现它。
使用 ruby 我们会做类似的事情:
@modal ||= get_modal
谁能指导我这个方向?
实现此目标的一种简单方法是将模态实例存储在 class 实例上。
这样您仍然可以从您的 getter 访问它,但不必每次都重新创建它。
_modalInstance
只是一个约定,下划线表示应该是private ish。但是,如果控制器以某种方式断开连接并且必须重新连接已显示的模态,这可能会导致问题。
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = [ "modal" ]
get modal() {
if (this._modalInstance) return this._modalInstance;
const modal = new bootstrap.Modal(this.modalTarget);
this._modalInstance = modal;
return this._modalInstance;
}
}
实现此目的的更好方法是使用 Bootstrap JavaScript API.
bootstrap.Modal.getOrCreateInstance(myModalEl)
https://getbootstrap.com/docs/5.1/components/modal/#getorcreateinstance
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = [ "modal" ]
get modal() {
return bootstrap.Modal.getOrCreateInstance(this.modalTarget);
}
}
以下是我实现记忆的方式:
get modal() {
if (this._modal == undefined) {
this._modal = new bootstrap.Modal(this.modalTarget);
}
return this._modal
}