Closed djozsef closed 8 years ago
There are a few ways to resolve this.
First, and easiest: set a default layout in your configuration:
return [
/* ... */
'templates' => [
/* ... */
'layout' => 'layout::default',
],
];
Doing so ensures that the layout view model is injected in the renderer at instantiation.
The next approach is to provide a layout at the time you call render()
; the documentation details two ways to do this.
Finally, within your template, you can inject a root view model manually prior to calling layout()
in your template:
use Zend\View\Model\ViewModel;
$this->viewModel()->setRoot(new ViewModel());
$this->layout(/* ... */;
// OR, in one go:
$this->viewModel()->setRoot(
(new ViewModel())->etTemplate(/* layout you want to use */)
);
Yes, outside the template these are clearly working.
But do not actually work within the template neither with your last two solutions.
These solutions only mitigate the exception but do not succeed in modifying the parent template, that is the layout. What you are injecting is only valid in the PhpRenderer::render
scope, but it gets lost when the ZendViewRenderer::renderModel
loop iterates, eg. when it bubbles up to render the layout. So setting the layout in the template / PhpRenderer::render
scope is not successful. Could you please validate this scenario?
Obviously this is not a blocking issue, but would be very elegant to be able to use it just like with the regular ZF2 MVC.
@djozsef Ah, right; once you've called render()
, if there is no root view model in place, then the current view model is root, which means no layout. As such, my first two were what you need to do. To recap:
provide a layout when you call render()
; this can be done by providing a layout
variable, which can either be the string name of the layout to use, or the layout view model to use:
$renderer->render('template::name', ['layout' => 'use::this::layout', /* other variables */]);
$renderer->render('template::name', ['layout' => $layoutViewModel, /* other variables */]);
The takeaway is: you must have a layout in place before you call the helper in the template. Otherwise, it will raise an exception, as you've already noted.
Now, for the long explanation.
The reason is because once we pass off rendering to the PhpRenderer
, all it does is look at the view model it has received. If that view model has children, it does a depth-first approach, rendering from the deepest child on out to the root view model. When you call layout()
from any of the descendants, what the helper does is traverse back up the tree until it gets to the root view model, and then changes the template name on it.
In the case you've indicated, no layout was provided. This means that the current view model is the root, and when you call layout()
, it cannot traverse upwards; there is no root view model at this time. As such, you've created an exceptional situation, and it properly errors.
Again, the takeaway: set a default layout in your configuration or provide a layout when you render.
Believe it or not, the only difference between this and the zend-mvc workflow is that in Expressive, we're not providing a default layout view model. The reason is simple: if we make that the default, then there's no way to render layout-less templates. A number of developers were quite interested in being able to do exactly that (e.g., to deliver rendered HTML snippets over XHR), which led to this implementation.
@weierophinney Thanks for summing up the full story. Given a deeper thought this is absolutely fine like this. Maybe we should include it in the docs.
Sotty, i try the expressive now, and this error occurred for me, but your informations and the readdocs not are clear for my, where make this modification? which files?
@IgorDePaula Not sure if it helps you but the docs have moved to their final location at https://zendframework.github.io/zend-expressive/features/template/zend-view/#layouts
@IgorDePaula You cannot set the layout from within a template when using ZendViewRenderer. You have to pass the layout's name to the render()
method along with the view data in the same structure (array
or ViewModel
). So in your middleware where you render the HTML output you would do something like this:
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next = null)
{
$viewData = array(
"layout" => "templates/app/layout.phtml",
"title" => "Hello World Title",
"text" => "Lorem ipsum dolor sit amet ..."
);
$html = $renderer->render("templates/app/home.phtml", $viewData)
return new HtmlResponse($html);
}
I written this form, but thw getting start recommend the sue of layout view helper. This hour occurred thw error. Em 19/02/2016 19:26, "József Dubravszky" notifications@github.com escreveu:
@IgorDePaula https://github.com/IgorDePaula You cannot set the layout from within a template when using ZendViewRenderer. You have to pass the layout's name to the render() method along with the view data in the same structure (array or ViewModel). So in your middleware where you render the HTML output you would do something like this:
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next = null){ $viewData = array( "layout" => "templates/app/layout.phtml", "title" => "Hello World Title", "text" => "Lorem ipsum dolor sit amet ..." ); $html = $renderer->render("templates/app/home.phtml", $viewData) return new HtmlResponse($html);}
— Reply to this email directly or view it on GitHub https://github.com/zendframework/zend-expressive-skeleton/issues/68#issuecomment-186413497 .
I do all on this page
https://zendframework.github.io/zend-expressive/getting-started/skeleton
and one hour I received error for getRoot not found for layout, and another hour I received error that view helper e is not registred.
I got now. I needed disable many settings.
When Zend\View\Helper\Layout helper called from inside a .phtml scope throws "getRoot: no view model currently registered as root in renderer". Error is thrown at "../vendor/zendframework/zend-view/src/Helper/Layout.php:64" because root is not set so setTemplate() fails.
REPRO:
put this line into any non-layout template script:
Obviously the layout path/map has to be set in config/autoload/templates.global.php
RESEARCH DONE:
The root ViewModel present is null when the helper tries to set template thus it fails. I suspect, that the root cause is that the ViewModel is fetched through DIC/ServiceManager, and at this point it it is already cached with null root. It was probably created in an other run.
I have no idea how to resolve this, sorry.
Could anybody with broader conceptual overview check this?
Thanks.