-
Notifications
You must be signed in to change notification settings - Fork 424
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
Outlets not available in connect/disconnect #618
Comments
This seems to be a timing issue. |
Thanks for pointing out the workaround. I'd argue that this is a bug in the implementation. The values/targets/classes APIs don't require waits, so this is hugely surprising. The error message also seems totally buggy since its message complains about the controller missing, when the very message points out that the At the very least, the timing issue should be documented. |
cc @marcoroth |
I will check out what might be wrong here, thanks for opening this issue @drjayvee! |
Same thing is happening to us in the
|
I guess I'm running into the same or something similar? I have a huge form with a stimulus-controller. That controller has an outlet for another controller somewhere in the middle of the form. When the page is loaded, I get the error 'missing data-controller attribute on outlet element'. When I look at the debugging info in the console, the outlet controller hasn't one it's connect() yet. So, in a way, the controller isn't there yet. So I understand the message. But it shows up as a big red blob. My code uses the foobarOutletConnected() callback, and this works fine! (Note, there is nothing really dynamic about how the outlet-controller gets inserted into the DOM. It's the same static HTML, it's just a big form, and by the time my form-controller is doing stuff, the DOM isn't loaded completely and the outlet-controller isn't there yet apparently.). |
I am encountering the same problem. Adding in a delay such as the sample code provided by @NakajimaTakuya does not appear to resolve it for me. |
A delay would only be an ugly hack. Basically, you are fighting a race condition. Instead of delaying, do it the proper way. Use the xxxxOutletConnected() callback. Then you know it's there. There is still the weird case that 'hasxxxxOutlet' can return true, while 'xxxOutlets.length' will still return 0. So, the code that wants to use the outlet (for example the foobar outlet), wrap it in something like if (this.foobarOutlets.length > 0) {
// access this.foobarOutlet or this.foobarOutlets
} and I add another method for when the outlet comes in later when the DOM is fully parsed. foobarOutletConnected() {
this.updateStuffWithOutlet();
} So, to prevent the error in the console, do not use |
We are experiencing the same issue. There are two controllers, each with outlets linking to the other - the
The |
The problem is the order how Stimulus connects controllers. So this issue is dependent how the elements are found in the DOM and in which order the MutationObserver processes them. So if you try to access the outlets in the This won't work: <div data-controller="search" data-search-result-outlet=".result">Search</div>
<div class="result" data-controller="result">Result</div>
<div class="result" data-controller="result">Result</div> However, this works as intended. <div class="result" data-controller="result">Result</div>
<div class="result" data-controller="result">Result</div>
<div data-controller="search" data-search-result-outlet=".result">Search</div> But in both scenarios you can access the outlets after the dependent outlet controllers have connected. Also the outlet callbacks work as expected. There are few options how we can solve that you can access outlets in any case, independent of DOM order appearance:
I'll investigate which option is the best solution here. Personally my vote would go for option ii. But happy to hear what others think. |
How would it work if that controller is lazy loaded and still needed to be
fetched ?
I think putting it into a promise is 'weird' from the standpoint of normal
stimulus api, but this would allow for most weird cases.
The dep-tree would seem hard ro implement and would introduce a bit of
internal magic that can sometimes do weird stuff vs what is expected ?
Basically what happens now is that you can't assume that the outlets are
there _yet_. Using an async api would make it easier to code with this ,
and also imply to programmers that there is a bit of waiting or delay .
…On Fri, Jan 27, 2023, 18:13 Marco Roth ***@***.***> wrote:
The problem is the order how Stimulus connects controllers. So this issue
is dependent how the elements are found in the DOM and in which order the
MutationObserver processes them.
So if you try to access the outlets in the connect() action of the "host
controller" and the dependent outlets haven't connected yet you will get
the The provided outlet element is missing the outlet controller "result"
for "search" error.
This won't work:
<div data-controller="search" data-search-result-outlet=".result">Search</div><div class="result" data-controller="result">Result</div><div class="result" data-controller="result">Result</div>
However, this works as intended.
<div class="result" data-controller="result">Result</div><div class="result" data-controller="result">Result</div><div data-controller="search" data-search-result-outlet=".result">Search</div>
But in both scenarios you can access the outlets after the depended outlet
controllers have connected. Also the outlet callbacks work as expected.
There are few options how we can solve that you can access outlets in any
case, independent of DOM order appearance:
-
1. We try to scan all element instances on the page which have a
data-controller attribute attached, build a "dependency tree", sort
the elements to be connected accordingly and then connect them in that
order. This works if we disallow circular outlet dependencies.
-
1. When the outlets are accessed and we can't find them (this is
where the error message appears now) we can try to find the element with
the provided selector and see if it would have a matching
data-controller attribute. If it does, we could connect that
controller on-the-fly and enforce it that way. However, there is the
downside that it might try to connect that controller again at a later
point, because it was scheduled to be connected. But maybe we can also
enhance the "connect logic" in that step to make sure there isn't a
connected controller already and just disregard the second connect attempt.
-
1. We don't allow outlets to be accessed in the connect(), but this
feels like a weird limitation.
-
1. We make the accessor for this.[name]Outlet and this.[name]Outlets
async and make them return a promise which resolves as soon as the
dependent controllers are connected. But this would probably lead to a
breaking change.
I'll investigate which option is the best solution here. Personally my
vote would go for option ii. But happy to hear what others think.
—
Reply to this email directly, view it on GitHub
<#618 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ALMD2DU2WZCYK2BOZ2AUW2TWUP63BANCNFSM6AAAAAAST7PQFI>
.
You are receiving this because you commented.Message ID:
***@***.***>
|
I'd very much prefer any solution which is transparent to users and consistent with other features. Therefore, I'd rate solutions Solution
Joris makes a good point re: lazy loading. Ideally, the solution should support that transparently as well! [Edit: |
To note on this: this calls a warning |
Stumbled over this today. In my case, it was with a multi-entry Since fixing the root cause seems non-trivial, maybe changing the error message could be a good quickfix? |
@promisedlandt I'm open to update the error message if you have an idea on how to improve it. Luckily, the root cause is fixed and already implemented with #648, but I haven't had the time to finish it up yet. I'm looking to finish that up soon! |
I think the error message would be slightly improved by: "Unable to find a controller when searching for the "result" outlet of the "search" controller. Ensure CSS selector "#result" is correct and target element has a data-controller attribute that includes "result", e.g. "data-controller=result"." (Not sure how easy including the CSS selector woud be, it's not particularily important) Speaking to my original point: if you're planning a release soon-ish without #648 I would suggest adding another senctence to the error message with an explicit reference to this issue , i.e. |
By wrapping codes in a setTimeout avoid this warning for me:
|
I'm getting this error also, and have gone to the Happy to PR those docs if someone can point me at them. |
Thanks @marcoroth - even better! |
@dhh could you please create a new release which includes the fix for this issue? Thanks in advance. |
When I try to test Outlets using the example from the docs, I get the following error:
I've prepared a JSFiddle to demonstrate. Here's the full code
I've done a little debugging and found that
Router.getContextForElementAndIdentifier
finds amodule
with no contexts (emptyArray
), which quickly leads to the error above.I've tried versions 3.2.0 and 3.2.1. Neither works.
The text was updated successfully, but these errors were encountered: