diff --git a/local_moodleappbehat/js/mobile/index.js b/local_moodleappbehat/js/mobile/index.js index 4576f5024..cfd91805f 100644 --- a/local_moodleappbehat/js/mobile/index.js +++ b/local_moodleappbehat/js/mobile/index.js @@ -3,3 +3,5 @@ this.CoreSitesProvider.getSite().then(site => { document.getElementById('username').innerText = `, ${username}`; }); + +this.ngAfterViewInit = () => this.CoreDomUtilsProvider.showToast('Lifecycle hook called'); diff --git a/src/core/features/compile/components/compile-html/compile-html.ts b/src/core/features/compile/components/compile-html/compile-html.ts index 845825aad..12a1eefc5 100644 --- a/src/core/features/compile/components/compile-html/compile-html.ts +++ b/src/core/features/compile/components/compile-html/compile-html.ts @@ -173,6 +173,8 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck { // Create the component, using the text as the template. return class CoreCompileHtmlFakeComponent implements OnInit, AfterContentInit, AfterViewInit, OnDestroy { + private ongoingLifecycleHooks: Set = new Set(); + constructor() { // Store this instance so it can be accessed by the outer component. compileInstance.componentInstance = this; @@ -222,21 +224,41 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy, DoCheck { * Content has been initialized. */ ngAfterContentInit(): void { - // To be overridden. + this.callLifecycleHookOverride('ngAfterContentInit'); } /** * View has been initialized. */ ngAfterViewInit(): void { - // To be overridden. + this.callLifecycleHookOverride('ngAfterViewInit'); } /** * Component destroyed. */ ngOnDestroy(): void { - // To be overridden. + this.callLifecycleHookOverride('ngOnDestroy'); + } + + /** + * Call a lifecycle method that can be overriden in plugins. + * + * This is necessary because overriding lifecycle hooks at runtime does not work in Angular. This may be happening + * because lifecycle hooks are special methods treated by the Angular compiler, so it is possible that it's storing + * a reference to the method defined during compilation. In order to work around that, this will call the actual method + * from the plugin without causing infinite loops in case it wasn't overriden. + * + * @param method Lifecycle hook method name. + */ + private callLifecycleHookOverride(method: keyof AfterViewInit | keyof AfterContentInit | keyof OnDestroy): void { + if (this.ongoingLifecycleHooks.has(method)) { + return; + } + + this.ongoingLifecycleHooks.add(method); + this[method](); + this.ongoingLifecycleHooks.delete(method); } }; diff --git a/src/core/features/siteplugins/tests/behat/plugins.feature b/src/core/features/siteplugins/tests/behat/plugins.feature index 1c5dca648..80c60f2d0 100644 --- a/src/core/features/siteplugins/tests/behat/plugins.feature +++ b/src/core/features/siteplugins/tests/behat/plugins.feature @@ -13,3 +13,9 @@ Feature: Plugins work properly. When I press "Moodle App Behat (auto-generated)" in the app Then I should find "studentusername" in the app + + Scenario: Use lifecycle hooks + Given I entered the app as "studentusername" + When I press the more menu button in the app + And I press "Moodle App Behat (auto-generated)" in the app + Then I should find "Lifecycle hook called" in the app