Already a fan of your new concept.
I’m wondering is there way to call an another controller action from a controller:
Let’s say we have a list of todos, each todo have a controller: todo_controller with actions like toggleState, Rename or set estimate.
We have a project controller, it wraps these items and renders them, when change one todo’s estimate value to something else I want to recalculate the total estimate of the todos, so is there a way to call the project controller’s calculateEstimate action somehow from todo controller?
Can I ask a related question from my toy application.
I have a project controller, inside it are a bunch of to do items that have an up and a down button (to reorder the items). Clicking the up or down triggers a state change, and calls the server to get the new list for display.
I’ve made each individual to do item an instance of a todo controller, and the project controller around it is an instance of a project controller.
When the todo up controller is clicked, is there a stimulus idiomatic way to get the parent project controller’s element to handle the refresh.
I tried making that element a target of the todo controller, but it’s not finding it, presumably because the target needs to be inside the scope of the controller element. I can find the correct element using the DOM, but that seems like I’m duplicating functionality. Is there a way to communicate between controllers?
var controlled_element = $("*[data-controller='edit-quotation']").first()
if (controlled_element != undefined) {
if (this.application.getControllerForElementAndIdentifier(controlled_element, "edit-quotation") == undefined) {
console.log("FAILED: could not find the controller");
}
}
The controller file name is edit_quotation_controller.js.
I tried alternative spellings to no avail (edit_quotation and editQuotation)
Just to add to this. I’ve had much success with a similar approach of @nowhereman but instead I extend upon the Main controller class so grabbing controller instances are applicable from any controller. For example:
import { Controller as BaseController } from 'stimulus'
export class Controller extends BaseController {
get isPreview () {
return this.application.element.hasAttribute('data-turbolinks-preview')
}
controller ($identifier, $id = false) {
return this.application.controllers.find(({
context: {
identifier,
element: { id }
}
}) => $id ? (
identifier === $identifier && id === $id
) : (
identifier === $identifier
))
}
}
Then from within your controllers, you’d extend that base class and you’d just do the following:
this.controller('identifier')
If you have more than 1 controller, you can easily query that by passing in an optional id parameter which will reference the element id associated to the controller. Just be wary and don’t repeat element ids, so for example:
Here you’ve got multiple something controllers in the dom, lets say you want the controller with an id of some-id-2 you can just pass this.controller('something', 'some-id-2').
Personally, it’s a code smell when you’re wrapping outer controller within inner controllers (as per the linked github discussion). Keeping logic separate is a much cleaner approach.
<div data-controller="list-filter">
<div data-controller="list" data-list-index-value="0">
</div>
</div>
import { Controller } from "stimulus"
export default class ListFilterController extends Controller {
connect(){
// how do I get the indexValue that is located in the list_controller?
this.listController.indexValue // doesn't work!? - I want to get the value 0
}
reloadList() {
this.listController.reload()
}
get listController() {
return this.application.getControllerForElementAndIdentifier(this.element, "list")
}
}
// list_controller.js
export default class ListController extends Controller {
static values = {"index": Number}
}
You’ll need to pass the the element with data-controller="list" instead of this.element. By making it a target of the list-filter controller, for example: