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

Fixed: Livewire subcomponent clears render context after it is finished #2786

Merged

Conversation

joshhanley
Copy link
Member

Issue

This PR adds a failing test for the issue raised in #2313 and #2380.

If you make use of $this within a blade component to access the main Livewire component, it works fine.

But when you add a Livewire subcomponent to the page and then try to add a blade component after the subcomponent that tries to access $this to access the main Livewire component, you get the following error

Using $this when not in object context

This test demonstrates the issue.

Cause

I have dug into what causes the issue, and the problem exists in Livewire's blade compiler engine.

What happens is in ComponentConcerns/RendersLivewireComponents.php the Livewire component is bound to the $this context.

At the top of the same function it does a check for isRenderingLivewireComponent, which if true, enables the render context to be set to the Livewire component, which is also applied to blade components.

if (! $this->isRenderingLivewireComponent) {
return parent::evaluatePath($__path, $__data);
}
$obLevel = ob_get_level();
ob_start();
// We'll evaluate the contents of the view inside a try/catch block so we can
// flush out any stray output that might get out before an error occurs or
// an exception is thrown. This prevents any partial views from leaking.
try {
\Closure::bind(function () use ($__path, $__data) {
extract($__data, EXTR_SKIP);
include $__path;
}, $this->livewireComponent ? $this->livewireComponent : $this)();

The issue is though, that after a sub component is finished rendering it calls the endLivewireRendering method which sets isRenderingLivewireComponent to false.

public function endLivewireRendering()
{
$this->isRenderingLivewireComponent = false;
}

This then stops the blade components from having this context set, throwing the above error as there is no $this set at all.

Temporary Fix

As a temporary solution (note this is a hack as a word around for the moment), I have added a blade directive to my AppServiceProvider

Blade::directive('livewireResetContext', function() {
  return '<?php app("view.engine.resolver")->resolve("blade")->startLivewireRendering($this); ?>';
});

Then in my Livewire component blade view, I add the call to that blade directive after any sub component

<livewire:subcomponent />
@livewireResetContext

Possible Solution

To fix this, I think we need to change the boolean to an array and "stack" each components name into the array and use the last one as the context. Then when a component has finished it can pop itself off the end of the stack. The check can then see if there is anything left in the stack as to whether to bind a Livewire context or not.

The regular blade compiler makes use of a similar concept with lastCompiled where each view is pushed on to the "stack" (array) and removed once it has finished compiling.

Happy to give it a crack if you think it is suitable.

Hope this helps!

…to the parent component when using blade components
@calebporzio
Copy link
Collaborator

Thanks @joshhanley , all of the above makes sense. I think you're right on with needing to convert to a stack. Of you're up for it, I'd put course love a PR. Thanks man!

@joshhanley
Copy link
Member Author

@calebporzio have pushed up the fix 🙂

@joshhanley joshhanley changed the title Failing test: Livewire subcomponent clears render context after it is finished Fixed: Livewire subcomponent clears render context after it is finished Apr 29, 2021
@tanthammar
Copy link

tanthammar commented Apr 30, 2021

🎉 Confirmed!
This solves #1653, #1401, #2380, #2313

@joshhanley
🌟 You are a lifesaver!!!!!!
Amazed by your perseverance to fix this and other issues with Livewire as well as your politeness to the community.

@calebporzio
This fix is actually a game changer for Livewire.
I hope you give @joshhanley a real treat for solving this, (and other) issues :)
It held me and others back to create more advanced Livewire components that requires nesting mixed with blade components.

Now I'll go out on my balcony and do some crazy jumping while shouting out to the world, that the lid finally popped off the Livewire jar 💖 (Then I'll remove some nasty hacks from the tall-forms package that worked around this issue.)

Sending LOVE!

{
$this->livewireComponent = $component;
return ! empty($this->livewireComponents);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it's just me but I prefer:

return filled($this->livewireComponents);

My brain get's confused by negations.

@calebporzio calebporzio merged commit a909b1b into livewire:master Apr 30, 2021
@calebporzio
Copy link
Collaborator

Thanks Josh! You're the man!

@joshhanley
Copy link
Member Author

No worries 🙂 I'm glad it helps!

@joshhanley joshhanley deleted the render-context-after-subcomponent branch April 30, 2021 21:16
@SalatielMG
Copy link

SalatielMG commented May 26, 2021

Thanks @joshhanley!, that solved my problem

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants