在另一个控制器中访问刺激操作。

5

以下是我的一个 Stimulus 控制器:

import { Controller } from "@hotwired/stimulus"
import InfiniteScroll from 'infinite-scroll'

export default class extends Controller {
    static get targets() {
        return ["next", "grid", "footer", "infinitescrollelement"]
    }

    connect() {
        let infScroll;

        if (this.hasNextTarget) {
            infScroll = new InfiniteScroll(this.gridTarget, {
                path: '.next_page a',
                append: '[data-infinitescroll-target="infinitescrollelement"]',
                // append: `.${this.data.get("object")}-top-level`,
                scrollThreshold: false,
                status: '.page-load-status',
                button: '.view-more-button'
            })

            this.footerTarget.querySelector('.view-more-button').style.display = 'inline-flex'
        } else {
            this.footerTarget.querySelector('.view-more-button').style.display = 'none'
        }

        // When new content is appended, re-layout the gallery to ensure new photos position correctly
        ***infScroll.on('append', (event, response, path, items) => {
            ***layoutGallery(galleryElement)
        ***})
    }
}

***的三行是我遇到问题的地方。

基本上,当无限滚动添加新内容时,我需要在我的Gallery Controller中运行一个操作。我该怎么做呢?它不能同时运行,只能在调用该无限滚动事件时运行。

有什么想法吗?


1
仅供参考,如果您能提供最小的HTML代码片段,将会对回答问题有很大帮助。 - LB Ben Johnston
2个回答

7
根据Stimulus文档,跨控制器通信的推荐方法是通过浏览器事件
需要注意以下几点:
  • jQuery事件不是浏览器事件,且默认情况下不会被Stimulus事件或任何非jQuery事件监听器所捕获(从代码看,我认为无限滚动是一个jQuery工具)。
  • Stimulus控制器有一个方便的方法this.dispatch,可以轻松地分发事件,这只是CustomEvents的薄包装。

从HTML开始

  • 从HTML开始,我们可以使用Stimulus的事件监听器功能,称为actions
  • data-action="infinite-scroll:append->gallery#updateLayout" -> 这表示画廊控制器应该监听事件'infinite-scroll:append'并调用画廊的updateLayout方法(我只是随便起的名字,你可以叫它任何你想要的名字)。
<main>
  <h1>Gallery with infinite scroll</h1>
  <section
    class="gallery"
    data-controller="gallery"
    data-action="infinite-scroll:append->gallery#updateLayout"
  >
    <div
      class="scroll-container"
      data-controller="infinite-scroll"
      data-infinite-scroll-target="grid"
    >
      <img src="/image-1" />
      <img src="/image-2" />
      <img src="/image-3" />
      <img src="/image-4" />
    </div>
  </section>
</main>

从jQuery事件'append'触发非jQuery事件

下面是更新后的控制器代码,我们首先检查infScroll是否存在,然后通过infScroll.on('append',...添加jQuery事件监听器。在这里,我们使用this.dispatch触发一个真实的浏览器事件,我们给它命名为'append',控制器名称将自动添加前缀(感谢Stimulus!),因此实际事件将是'infinite-scroll:append',假设您的控制器已注册为'infinite-scroll'。我们通过detail对象传递监听器可能需要的所有内容,同时添加cancelable:false,虽然这不是关键点,但清晰地表达这一点很好,并且Stimulus事件分派的默认值为true。请注意,我们还传递了从jQuery监听器获取的event,这可能不是必需的,但知道这个事件和将要分派的事件是不同的事件是很好的。请注意,我们将target选项添加到this.dispatch中,这不是必需的,但它确实使清楚我们所讨论的DOM元素是哪个。使用this.dispatch的事件默认会冒泡,因此它们将被父元素捕获。
import { Controller } from '@hotwired/stimulus';
import InfiniteScroll from 'infinite-scroll';

class InfiniteScrollController extends Controller {
  static get targets() {
    return ['next', 'grid', 'footer', 'item'];
  }

  connect() {
    let infScroll;

    if (this.hasNextTarget) {
      infScroll = new InfiniteScroll(this.gridTarget, {
        path: '.next_page a',
        append: '[data-infinite-scroll-target="item"]',
        // append: `.${this.data.get("object")}-top-level`,
        scrollThreshold: false,
        status: '.page-load-status',
        button: '.view-more-button',
      });

      this.footerTarget.querySelector('.view-more-button').style.display =
        'inline-flex';
    } else {
      this.footerTarget.querySelector('.view-more-button').style.display =
        'none';
    }

    // When new content is appended, re-layout the gallery to ensure new photos position correctly
    if (infScroll) {
      infScroll.on('append', (event, response, path, items) => {
        // note: the 'event' here is the jQuery event, the dispatch below will also dispatch with its own event
        // passing the original jQuery event (which is not strictly a DOM event) in the detail as it may be used

        const detail = { event, response, path, items };

        this.dispatch('append', {
          cancelable: false,
          detail,
          target: event.target,
        });
      });
    }
  }
}

export default InfiniteScrollController;

1
明白了!完美运行。非常感谢你详细的解释。我会再读几遍以确保我理解了。 - rctneil
没有问题 - 很高兴能够帮助到您。 - LB Ben Johnston

3
我发现这个技巧很有帮助。它使得element.controllerName返回与Stimulus连接的元素:https://dev.to/leastbad/the-best-one-line-stimulus-power-move-2o90 -- 这篇文章逐渐揭示了一个重要的发现,但在这里就是:

控制器可以访问全局的Stimulus应用程序范围,其中包含getControllerForElementAndIdentifier作为成员函数。如果你有一个附加了控制器的元素的引用和控制器的名称,你可以获取页面上任何控制器的引用。然而,这并没有为在Stimulus控制器之外工作的开发人员提供任何解决方案。

以下是我们应该做的替代方法。

在你的控制器的connect()方法中,添加这一行:

this.element[this.identifier] = this

Boom!这个将一个对Stimulus控制器实例的引用挂在了与控制器本身同名的DOM元素上。现在,如果你能获取到该元素的引用,你就可以在任何需要的地方访问element.controllerName。

谢谢,丹尼。我发现这种方法比上面描述的刺激文档更清晰。 - chmich

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接