在 Turbo/hotwire 的背景下理解 StimulusJS connect() 与initialize()

问题描述 投票:0回答:1

刺激手册将连接/初始化之间的区别描述为:

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 时,它们不会被初始化。

谁能解释一下这是为什么?我花了太长时间才意识到这是如何工作的,而且这里的文档似乎没有解决这个细微差别

ruby-on-rails ruby-on-rails-7 stimulusjs hotwire-rails turbo
1个回答
0
投票

您要从

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);
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.