簡體   English   中英

StencilJS IonContent 滾動事件觸發到子組件

[英]StencilJS IonContent scroll event firing to child component

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 發生滾動時,我嘗試從父組件調用子組件“windowScroll”方法
  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;

在滾動時更新狀態屬性

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");
    }
  }

在所有情況下,都不會調用該方法。

我不是 100% 確定為什么您的方法 3 已經開始工作,但我認為這是因為onIonScroll處理程序更新了scrollOffset狀態,然后觸發了@Watch()裝飾方法。


你在這里混淆了一些東西,所以我會先試着澄清一下:如果一個元素發出一個事件<EventName> ,那么你可以通過添加一個on<EventName>屬性來綁定一個監聽器到那個元素到它,例如:

<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作為<ion-content>的屬性之外,您還可以像這樣使用@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));
  });
});

出於某種原因,方法 3 已經開始工作。

我不確定是否是因為我在發布更新后對項目進行了干凈的構建,但現在正在調用該方法並且一切都按預期工作。

不過有一件事,我仍然想知道這是否是最好的方法? 理想情況下,我想在子組件中收聽父組件的滾動事件,但仍然無法正常工作。

如果有人有更清潔的解決方案,請告訴我

我個人會選擇第一個,因為它最接近中斷/推送。 因此,如果狀態發生變化,子組件會立即獲得狀態變化的信息。

你真的很接近,但你只需要將@Method裝飾器添加到你的子函數中 這個裝飾器使函數公開可用,你可以從父級調用它。 從我的角度來看,這是實現這一點的最簡單和最好的方法。

這里只是你的第二個和第三個解決方案的旁注:這兩個概念開始工作是因為 stencil監視屬性並在應用更改時重新渲染組件。 在某些情況下,這不是必需的 - 這會產生不必要的開銷。 此外,我認為手表實際上是作為投票設計的,但我不確定這一點。

此外,您無法獲得父事件,因為事件的行為就像 - 它從根開始並“向下”穿過樹,直到到達交互元素。 這稱為捕獲階段 一旦它到達元素,它就會冒泡。 所以理論上事件會下降,你可以捕獲它們,但前提是元素在層次結構中高於交互。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM