刺激手册将连接/初始化之间的区别描述为:
initialize(): Invoked only once, when the controller is first instantiated
connect(): Invoked anytime the controller is connected to the DOM
我创建了一个带有家庭脚手架的新 Rails 应用程序,只是为了让自己轻松使用。我将忽略很多 Rails 约定,只是为了演示我的问题实际要问的内容而清除一些内容:
将homes/index.html.erb更改为:
<div id="container">
<div id="hello" data-controller="hello">Hello!</div>
</div>
<div data-controller="remove">
<button data-action="click->remove#addHello">Add</button>
<button data-action="click->remove#removeHello">Remove</button>
</div>
只是为了记录初始化/连接发生的时间,我创建了 hello_controller.js:
import { Controller } from '@hotwired/stimulus';
export default class extends Controller {
initialize() {
console.log('i have been initialized');
}
connect() {
console.log('i have been connected');
}
}
为了从 dom 添加/删除控制器,我有remove_controller.js:
import { Controller } from '@hotwired/stimulus';
// Connects to data-controller="remove"
export default class extends Controller {
removeHello() {
const element = document.getElementById('hello');
this.elementInMemory = element;
element.remove();
}
addHello() {
const container = document.getElementById('container');
container.appendChild(this.elementInMemory);
}
}
一切都按预期进行。每次我将刺激控制器添加到dom时,它都会在初始页面加载时console.logs“我已连接”,但它仅console.logs“我已初始化”。我对此没有任何问题,它按预期工作。让我们尝试一些不同的东西吧。
将 home/index.html.erb 更改为:
<%= turbo_frame_tag "container" do %>
<div id="hello" data-controller="hello">Hello!</div>
<%= link_to "Click me", home_path(id: "1") %>
<% end %>
并更改了 show.html.erb
<%= turbo_frame_tag "container" do%>
<div>World</div>
<%= link_to "Go back", homes_path %>
<% end %>
为了防止错误,在 HomesController 中删除了这一行:
before_action :set_home, only: %i[ show edit update destroy ]
如果需要,您也可以删除remove_controller.js,这并不重要,因为它没有执行任何操作。
现在,turbo 框架可以有效地从 dom 添加/删除控制器,即使它使用turboframes 而不是 JS。但这是让我感到困惑的部分,因为现在每次在 dom 中添加/删除控制器时它都会调用 BOTH 初始化和连接。我认为它会具有相同的行为,即仅调用一次初始化并且仅调用连接
好吧,好吧。让我们尝试使用涡轮流。我认为 Turbo Streams 有点像 JS,因为它具有附加/删除等功能。动作,所以也许这会有不同的行为。
为此,我将把index.html.erb更改为:
<%= tag.div id: "container" do %>
<%= render "form" %>
<% end %>
我将 _form.html.erb 更改为:
<div id="hello" data-controller="hello">Hello!</div>
<%= button_to "Click me", homes_path %>
在家庭控制器中,我刚刚将创建操作更改为:
def create
end
最后,create.turbo_stream.erb:
<%= turbo_stream.update "container" do %>
<%= render "homes/form" %>
<% end %>
当然,我从 Turbo 帧中添加/删除 dom 控制器得到了相同的结果。每次添加/删除它时,控制台都会记录初始化和连接功能。
tl;dr 看起来控制器每次通过 Turbo 添加/删除到 dom 时都会被初始化,但是当它们通过 Stimulus/Vanilla JS 添加/删除到 dom 时,它们不会被初始化。
谁能解释一下这是为什么?我花了太长时间才意识到这是如何工作的,而且这里的文档似乎没有解决这个细微差别
您要从
this.elementInMemory
添加相同的元素,该元素的控制器已初始化,因此当您单击添加时仅调用 connect
。
当您使用 Turbo Stream/Frame 执行此操作时,您将添加 html,并且新的控制器始终必须再次初始化。
import { Controller } from '@hotwired/stimulus';
// Connects to data-controller="remove"
export default class extends Controller {
removeHello() {
const element = document.getElementById('hello');
this.elementInMemory = element;
element.remove();
}
addHello() {
const container = document.getElementById('container');
// you could clone the element, which means a new controller
// has to be initialized for the new element
container.appendChild(this.elementInMemory.cloneNode(true));
}
}
或者像这样
import { Controller } from "@hotwired/stimulus"
// Connects to data-controller="remove"
export default class extends Controller {
removeHello() {
const element = document.getElementById('hello');
this.elementInMemory = element.outerHTML;
element.remove();
}
addHello() {
const container = document.getElementById('container');
container.insertAdjacentHTML("beforeend", this.elementInMemory);
}
}