Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure the afterLoad method gets called bound to controller #658

Merged
merged 1 commit into from
Feb 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions docs/reference/controllers.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
}
Expand All @@ -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

Expand Down
2 changes: 1 addition & 1 deletion src/core/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand Down
20 changes: 16 additions & 4 deletions src/tests/modules/core/loading_tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
})
)
}
}

Expand All @@ -37,22 +45,26 @@ 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)

// check the DOM element has been added based on params provided
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() {
Expand Down