【问题标题】:StencilJS IonContent scroll event firing to child componentStencilJS IonContent 滚动事件触发到子组件
【发布时间】:2020-05-03 03:32:30
【问题描述】:

IonContent 有自己的滚动事件,我需要在子组件中监听它。

我正在监听没有触发的窗口滚动事件,我意识到它被包裹在处理自己的滚动事件的 IonContent 元素中。

下面是我的布局示例

<ion-content scrollEvents={ true } onIonScroll={ this.windowScrolled.bind(this) }>
    <bf-filter-selections scrollElement={this.ionContent} ref={(e) => this.filterElementWrapper = e}></bf-filter-selections>
</ion-content>

我尝试了一些方法:

  1. 我尝试从 发生 IonContent 滚动时的父级
  2. 我尝试将父级的引用传递给子级,这样我就可以 监听滚动事件
  3. 我尝试在子节点上创建一个属性,当 滚动事件发生,然后观察变化

这可能会回到我缺乏的一些基本 js 知识,但令人沮丧的是,当内容滚动时,我似乎无法在子组件中触发事件。

在我尝试过的所有方法中,这就是它们不起作用的原因

1) 从父组件调用子组件的方法,代码告诉我在子组件上找不到“windowScroll”。

filterElementWrapper 是 HTMLElement

 filterElementWrapper: any;
 windowScrolled(e: any) {

    this.filterElementWrapper.windowScroll();

  }

子组件方法

  @Listen('onscroll')
  @Watch('scrollOffset')
  public windowScroll() {
    console.log('scroll');

    if (window.pageYOffset > this.resultHeaderElementOffset) {
      this.resultHeaderElement.classList.add("sticky");
    } else {
      this.resultHeaderElement.classList.remove("sticky");
    }
  }

2) 尝试将父组件的引用传递给子组件

我不再有支持这一点的代码,但基本上在子组件中,我创建了一个类型为“any”的属性并将父级的引用传递给子级。

然后我创建了一个“Listen('ionScroll', { target: this.scrollElement})”。

该方法永远不会在子级中触发。

3) 在子组件上创建了一个名为“scrollOffset”的属性。我在触发滚动时更新了此属性,并“监视”子组件中的属性更改。同样,该方法没有触发。

设置滚动偏移属性的主包装器

<ion-content scrollEvents={ true } onIonScroll={ this.windowScrolled.bind(this) }> 
   <bf-filter-selections scroll-offset={this.scrollTop} scrollElement={this.ionContent} ref={(e) => this.filterElementWrapper = e}></bf-filter-selections>
</ion-content>

父级中设置的状态属性

@State() scrollTop: number = 0;

在滚动时更新 state 属性

windowScrolled(e: any) {
    this.scrollTop = e.detail.currentY;
}

被监视的子属性

@Prop() scrollOffset: number;

观察属性变化的方法

  @Listen('onscroll')
  @Watch('scrollOffset')
  public windowScroll() {
    console.log('scroll');

    if (window.pageYOffset > this.resultHeaderElementOffset) {
      this.resultHeaderElement.classList.add("sticky");
    } else {
      this.resultHeaderElement.classList.remove("sticky");
    }
  }

在所有情况下,都没有调用该方法。

【问题讨论】:

  • 也许您可以分享更多信息为什么您的解决方案不起作用,也许还可以提供更多代码 sn-ps 以真正了解您到目前为止所做的工作。
  • @ChristianMeyer - 我添加了一些我尝试过的示例。感谢您的反馈

标签: javascript ionic-framework ionic4 jsx stenciljs


【解决方案1】:

由于某种原因,方法 3 已经开始起作用了。

我不确定是不是因为在发布更新后我已经完成了项目的干净构建,但现在正在调用该方法并且一切都按预期工作。

但有一件事,我仍然想知道这是否是最好的方法?理想情况下,我想在子组件中监听父组件的滚动事件,但仍然无法正常工作。

如果有人有更清洁的解决方案,请告诉我

【讨论】:

    【解决方案2】:

    我不能 100% 确定您的方法 3 为何开始工作,但我认为这是因为 onIonScroll 处理程序更新了 scrollOffset 状态,然后触发了 @Watch() 装饰方法。


    你在这里混淆了一些东西,所以我先试着澄清一下:如果一个元素发出一个事件&lt;EventName&gt;,那么你可以通过添加一个@987654329 来绑定一个监听器到那个元素@ 属性,例如。 g.:

    <my-button onclick={console.log} />
    

    将函数console.log 绑定到click 事件。相当于在 JavaScript 中做同样的事情是:

    document.querySelector('my-button').addEventListener('click', console.log);
    

    如果您使用了addEventListener('onclick'),那将不起作用,因为事件名称是click 而不是onclick。如果您想为 HTML 中的元素添加事件处理程序,您只需添加 on 前缀(请参阅DOM onevent handlers)。或者,这也适用于 JavaScript:

    document.querySelector('my-button').onclick = console.log;
    

    (区别在于您只能设置一个onclick 属性,但如果您使用addEventListener,则可以附加多个click 事件监听器)

    请注意,大小写在 JavaScript 中很重要,但在 HTML 中不重要,所以

    <my-button onClick={console.log} />
    

    相当于第一个例子。


    理想情况下,我想在子组件中监听父组件的滚动事件,但仍然无法正常工作。

    这是不可能的,因为事件只能在 DOM 树上冒泡,而不能向下冒泡。这个概念是“事件上升,道具下降”。对于您的情况,我能想到的最干净的解决方案与您已经尝试过的类似:在父级中侦听事件,然后将必要的信息作为道具传递给子级:

    import { h, Component, State } from '@stencil/core';
    import { ScrollDetail } from '@ionic/core';
    
    @Component({ tag: 'my-parent' })
    export class Parent {
      @State() scrollTop = 0;
    
      setScrollTop = (e: CustomEvent<ScrollDetail>) => {
        this.scrollTop = e.detail.scrollTop;
      }
    
      render() {
        return (
          <ion-content scrollEvents onIonScroll={this.setScrollTop}>
            <child-component scrollTop={this.scrollTop} />
          </ion-content>
        );
      }
    }
    

    除了使用onIonScroll 作为&lt;ion-content&gt; 的属性,您还可以像这样使用@Listen() 装饰器:

    @Listen('ionScroll')
    setScrollTop(e: CustomEvent<ScrollDetail>) {
      this.scrollTop = e.detail.scrollTop;
    }
    

    您的错误是使用'onscroll' 作为事件名称。


    我看到您还尝试将滚动元素的引用传递给子元素。 ion-content 元素并不是真正滚动的元素,因此它提供了一种 getScrollElement 方法,如果您想要执行“原生”操作(例如访问底层 scroll 事件或手动访问 scrollTop),它会为您提供正确的元素那个元素(我假设这是你试图从你的孩子身上做的)。

    customElements.whenDefined('ion-content').then(() => {
      document.querySelector('ion-content').getScrollElement().then(el => {
        el.addEventListener('scroll', () => console.log(el.scrollTop));
      });
    });
    

    【讨论】:

    • 谢谢,我在之前的尝试中使用过“滚动”。我不知道为什么我使用 onscroll,因为我知道其中的区别,但我感谢您花时间解释它。你会说最干净的方法是什么?我正在使用的方法目前正在按预期工作,最终他们最终会做同样的工作,但从“最佳实践”的角度来看,您的看法是什么?
    • 从“最佳实践”的角度来看,我在此处发布的解决方案是:监听父级中的事件,然后将特别需要的数据作为道具传递给子级。这是 Stencil 最佳实践,请参阅 e。 G。 stenciljs.com/docs/properties:“子组件不应该知道或引用父组件,所以应该使用 Props 将数据从父组件向下传递给子组件。”,或stenciljs.com/docs/methods 的第一个信息框。
    • 我以前从来没有看过方法装饰器,那会使第 1 项也可以工作。我尝试将子方法公开,但没有意识到我也必须装饰它。那将是另一条潜在的下跌路线。谢谢
    • 是的,这也是可能的,但不是最佳实践……到目前为止,在我们的应用程序中只有一次我发现它比“事件增加,道具减少”更清楚。从某种意义上说,该方法被孩子和父母都使用,但它属于孩子的代码。
    【解决方案3】:

    我个人会选择第一个,因为它最接近中断/推送。因此,如果状态发生变化,子组件会立即获得状态变化的信息。

    您真的很接近,但您只需将 @Method 装饰器添加到您的子函数。这个装饰器使函数公开可用,您可以从父级调用它。在我看来,这是实现这一点的最简单和最好的方法。

    这里只是对您的第二个和第三个解决方案的附带说明:这两个概念都开始起作用了,因为 stencil 监视属性并在应用更改时重新呈现组件。在某些情况下,这不是必需的——这会产生不必要的开销。另外我认为手表实际上是设计为投票的,但我不确定。

    此外,您无法获取父事件,因为事件的行为就像 - 它从根开始并“向下”穿过树,直到到达交互元素。这称为capturing phase。一旦它到达元素,它就会冒泡。因此,理论上事件会下降,您可以捕获它们,但前提是元素在层次结构中较高,然后交互才是。

    【讨论】:

    • 谢谢克里斯蒂安。我今天再次注意到奇怪的行为。我删除了所有不再需要的代码并添加了侦听器事件,而不是隐式调用 onscroll,这一切都停止了工作。我不得不终止该过程并再次运行构建以使其开始工作。非常令人沮丧,因为我最终花了大约 2 个小时试图弄清楚我的第一种方法何时可能有效,但由于某种原因构建没有更新
    猜你喜欢
    • 2021-03-19
    • 1970-01-01
    • 1970-01-01
    • 2020-10-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多