From f7bfc358e5df2cd4623ae47325254e4076d61148 Mon Sep 17 00:00:00 2001 From: "LB (Ben Johnston)" Date: Mon, 13 Feb 2023 22:41:59 +1000 Subject: [PATCH] Ensure the `afterLoad` method gets called bound to controller (#658) - Fixes #652 Co-authored-by: LB Johnston --- docs/reference/controllers.md | 6 ++++-- src/core/router.ts | 2 +- src/tests/modules/core/loading_tests.ts | 20 ++++++++++++++++---- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/docs/reference/controllers.md b/docs/reference/controllers.md index 9fd572af..3a5cd966 100644 --- a/docs/reference/controllers.md +++ b/docs/reference/controllers.md @@ -169,13 +169,15 @@ If you want to trigger some behaviour once a controller has been registered you ```js class SpinnerButton extends Controller { + static legacySelector = ".legacy-spinner-button" + static afterLoad(identifier, application) { // use the application instance to read the configured 'data-controller' attribute const { controllerAttribute } = application.schema // update any legacy buttons with the controller's registered identifier const updateLegacySpinners = () => { - document.querySelector(".legacy-spinner-button").forEach((element) => { + document.querySelector(this.legacySelector).forEach((element) => { element.setAttribute(controllerAttribute, identifier) }) } @@ -193,7 +195,7 @@ class SpinnerButton extends Controller { application.register("spinner-button", SpinnerButton) ``` -The `afterLoad` method will get called as soon as the controller has been registered, even if no controlled elements exist in the DOM. It gets called with the `identifier` that was used when registering the controller and the Stimulus application instance. +The `afterLoad` method will get called as soon as the controller has been registered, even if no controlled elements exist in the DOM. The function will be called bound to the original controller constructor along with two arguments; the `identifier` that was used when registering the controller and the Stimulus application instance. ## Cross-Controller Coordination With Events diff --git a/src/core/router.ts b/src/core/router.ts index c06e5e57..6b63318d 100644 --- a/src/core/router.ts +++ b/src/core/router.ts @@ -57,7 +57,7 @@ export class Router implements ScopeObserverDelegate { this.connectModule(module) const afterLoad = (definition.controllerConstructor as any).afterLoad if (afterLoad) { - afterLoad(definition.identifier, this.application) + afterLoad.call(definition.controllerConstructor, definition.identifier, this.application) } } diff --git a/src/tests/modules/core/loading_tests.ts b/src/tests/modules/core/loading_tests.ts index 1190dcdb..20ed5c92 100644 --- a/src/tests/modules/core/loading_tests.ts +++ b/src/tests/modules/core/loading_tests.ts @@ -13,12 +13,20 @@ class LoadableController extends LogController { } class AfterLoadController extends LogController { + static values = { + example: { default: "demo", type: String }, + } + static afterLoad(identifier: string, application: any) { const newElement = document.createElement("div") newElement.classList.add("after-load-test") newElement.setAttribute(application.schema.controllerAttribute, identifier) application.element.append(newElement) - document.dispatchEvent(new CustomEvent("test", { detail: { identifier, application } })) + document.dispatchEvent( + new CustomEvent("test", { + detail: { identifier, application, exampleDefault: this.values.example.default, controller: this }, + }) + ) } } @@ -37,13 +45,15 @@ export default class ApplicationTests extends ApplicationTestCase { "test module with afterLoad method should be triggered when registered"() { // set up an event listener to track the params passed into the AfterLoadController - let data: { application?: any; identifier?: string } = {} + let data: { application?: any; identifier?: string; exampleDefault?: string; controller?: any } = {} document.addEventListener("test", (({ detail }: CustomEvent) => { data = detail }) as EventListener) - this.assert.equal(data.identifier, undefined) this.assert.equal(data.application, undefined) + this.assert.equal(data.controller, undefined) + this.assert.equal(data.exampleDefault, undefined) + this.assert.equal(data.identifier, undefined) this.application.register("after-load", AfterLoadController) @@ -51,8 +61,10 @@ export default class ApplicationTests extends ApplicationTestCase { this.assert.equal(this.findElements('[data-controller="after-load"]').length, 1) // check that static method was correctly called with the params - this.assert.equal(data.identifier, "after-load") this.assert.equal(data.application, this.application) + this.assert.equal(data.controller, AfterLoadController) + this.assert.equal(data.exampleDefault, "demo") + this.assert.equal(data.identifier, "after-load") } get controllers() {