diff --git a/src/addons/calendar/components/calendar/calendar.ts b/src/addons/calendar/components/calendar/calendar.ts index 3561366a8..6fac088f5 100644 --- a/src/addons/calendar/components/calendar/calendar.ts +++ b/src/addons/calendar/components/calendar/calendar.ts @@ -142,7 +142,7 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro } /** - * Component loaded. + * @inheritdoc */ ngOnInit(): void { this.canNavigate = typeof this.canNavigate == 'undefined' ? true : CoreUtils.isTrueOrOne(this.canNavigate); @@ -164,7 +164,7 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro } /** - * Detect and act upon changes that Angular can’t or won’t detect on its own (objects and arrays). + * @inheritdoc */ ngDoCheck(): void { const items = this.manager?.getSource().getItems(); @@ -368,7 +368,7 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro } /** - * Component destroyed. + * @inheritdoc */ ngOnDestroy(): void { this.undeleteEventObserver?.off(); diff --git a/src/core/components/swipe-slides/swipe-slides.ts b/src/core/components/swipe-slides/swipe-slides.ts index f063ffd72..82c856af3 100644 --- a/src/core/components/swipe-slides/swipe-slides.ts +++ b/src/core/components/swipe-slides/swipe-slides.ts @@ -15,7 +15,9 @@ import { Component, ContentChild, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, Output, SimpleChange, TemplateRef, ViewChild, } from '@angular/core'; +import { AsyncDirective } from '@classes/async-directive'; import { CoreSwipeSlidesItemsManager } from '@classes/items-management/swipe-slides-items-manager'; +import { CorePromisedValue } from '@classes/promised-value'; import { IonContent } from '@ionic/angular'; import { CoreDomUtils, VerticalPoint } from '@services/utils/dom'; import { CoreUtils } from '@services/utils/utils'; @@ -32,7 +34,7 @@ import { SwiperOptions } from 'swiper/types'; templateUrl: 'swipe-slides.html', styleUrls: ['swipe-slides.scss'], }) -export class CoreSwipeSlidesComponent implements OnChanges, OnDestroy { +export class CoreSwipeSlidesComponent implements OnChanges, OnDestroy, AsyncDirective { @Input() manager?: CoreSwipeSlidesItemsManager; @Input() options: CoreSwipeSlidesOptions = {}; @@ -46,22 +48,21 @@ export class CoreSwipeSlidesComponent implements OnChanges, OnDe * This setTimeout waits for Ionic's async initialization to complete. * Otherwise, an outdated swiper reference will be used. */ - setTimeout(() => { + setTimeout(async () => { if (swiperRef?.nativeElement?.swiper) { this.swiper = swiperRef.nativeElement.swiper as Swiper; + await this.initialize(); + if (this.options.initialSlide) { this.swiper.slideTo(this.options.initialSlide, 0, this.options.runCallbacksOnInit); } + this.updateOptions(); + this.swiper.on('slideChangeTransitionStart', () => this.slideWillChange()); this.swiper.on('slideChangeTransitionEnd', () => this.slideDidChange()); - Object.keys(this.options).forEach((key) => { - if (this.swiper) { - this.swiper.params[key] = this.options[key]; - } - }); } }, 0); } @@ -72,6 +73,7 @@ export class CoreSwipeSlidesComponent implements OnChanges, OnDe protected unsubscribe?: () => void; protected resizeListener: CoreEventObserver; protected activeSlideIndexes: number[] = []; + protected onReadyPromise = new CorePromisedValue(); constructor( elementRef: ElementRef, @@ -87,17 +89,11 @@ export class CoreSwipeSlidesComponent implements OnChanges, OnDe /** * @inheritdoc */ - ngOnChanges(changes: { [name: string]: SimpleChange }): void { - if (!this.unsubscribe && this.manager) { - this.initialize(this.manager); - } + async ngOnChanges(changes: { [name: string]: SimpleChange }): Promise { + await this.initialize(); if (changes.options) { - Object.keys(this.options).forEach((key) => { - if (this.swiper) { - this.swiper.params[key] = this.options[key]; - } - }); + this.updateOptions(); } } @@ -122,8 +118,12 @@ export class CoreSwipeSlidesComponent implements OnChanges, OnDe /** * Initialize some properties based on the manager. */ - protected async initialize(manager: CoreSwipeSlidesItemsManager): Promise { - this.unsubscribe = manager.getSource().addListener({ + protected async initialize(): Promise { + if (this.unsubscribe || !this.swiper || !this.manager) { + return; + } + + this.unsubscribe = this.manager.getSource().addListener({ onItemsUpdated: () => this.onItemsUpdated(), }); @@ -131,16 +131,16 @@ export class CoreSwipeSlidesComponent implements OnChanges, OnDe // This is because default callbacks aren't triggered for index 0, and to prevent auto scroll on init. this.options.runCallbacksOnInit = false; - await manager.getSource().waitForLoaded(); + await this.manager.getSource().waitForLoaded(); if (this.options.initialSlide === undefined) { // Calculate the initial slide. - const index = manager.getSource().getInitialItemIndex(); + const index = this.manager.getSource().getInitialItemIndex(); this.options.initialSlide = Math.max(index, 0); } // Emit change events with the initial item. - const items = manager.getSource().getItems(); + const items = this.manager.getSource().getItems(); if (!items || !items.length) { return; } @@ -155,9 +155,11 @@ export class CoreSwipeSlidesComponent implements OnChanges, OnDe this.activeSlideIndexes = [initialIndex]; - manager.setSelectedItem(items[initialIndex]); + this.manager.setSelectedItem(items[initialIndex]); this.onWillChange.emit(initialItemData); this.onDidChange.emit(initialItemData); + + this.onReadyPromise.resolve(); } /** @@ -167,19 +169,20 @@ export class CoreSwipeSlidesComponent implements OnChanges, OnDe * @param speed Animation speed. * @param runCallbacks Whether to run callbacks. */ - slideToIndex(index: number, speed?: number, runCallbacks?: boolean): void { + async slideToIndex(index: number, speed?: number, runCallbacks?: boolean): Promise { // If slides are being updated, wait for the update to finish. - if (!this.swiper) { - return; - } + await this.ready(); // Verify that the number of slides matches the number of items. - const slidesLength = this.swiper.slides.length; + const slidesLength = this.swiper?.slides?.length || 0; if (slidesLength !== this.items.length) { // Number doesn't match, do a new update to try to match them. - this.updateSlidesComponent(); + await this.updateSlidesComponent(); } + if (!this.swiper?.slides) { + return; + } this.swiper?.slideTo(index, speed, runCallbacks); } @@ -190,10 +193,10 @@ export class CoreSwipeSlidesComponent implements OnChanges, OnDe * @param speed Animation speed. * @param runCallbacks Whether to run callbacks. */ - slideToItem(item: Item, speed?: number, runCallbacks?: boolean): void { + async slideToItem(item: Item, speed?: number, runCallbacks?: boolean): Promise { const index = this.manager?.getSource().getItemIndex(item) ?? -1; if (index != -1) { - this.slideToIndex(index, speed, runCallbacks); + await this.slideToIndex(index, speed, runCallbacks); } } @@ -225,7 +228,7 @@ export class CoreSwipeSlidesComponent implements OnChanges, OnDe await CoreUtils.nextTick(); // Update the slides component so the slides list reflects the new items. - this.updateSlidesComponent(); + await this.updateSlidesComponent(); const currentItem = this.manager?.getSelectedItem(); @@ -234,10 +237,26 @@ export class CoreSwipeSlidesComponent implements OnChanges, OnDe } // Keep the same slide in case the list has changed. - const newIndex = this.manager.getSource().getItemIndex(currentItem) ?? -1; - if (newIndex != -1) { - this.swiper?.slideTo(newIndex, 0, false); + this.slideToItem(currentItem, 0, false); + } + + /** + * Update Swiper params from options. + */ + protected updateOptions(): void { + if (!this.swiper) { + return; } + + if (this.swiper.params === undefined) { + this.swiper.params = {}; + } + + Object.keys(this.options).forEach((key) => { + if (this.swiper) { + this.swiper.params[key] = this.options[key]; + } + }); } /** @@ -322,8 +341,24 @@ export class CoreSwipeSlidesComponent implements OnChanges, OnDe /** * Update slides component. */ - updateSlidesComponent(): void { + async updateSlidesComponent(): Promise { + await this.ready(); + + if (!this.swiper) { + return; + } + this.swiper?.update(); + + // We need to ensure the slides are updated before continuing. + await CoreUtils.nextTicks(2); + } + + /** + * @inheritdoc + */ + async ready(): Promise { + return this.onReadyPromise; } /**