Zend Framework 2 set custom layout and setTerminat

2019-08-03 06:39发布

问题:

I dont know if this is a bug of ZF2 or I just dont understand it well, but Im very excited why is this happening.

I'm using a solution to change layout of each module globaly by attaching a Dispatch event. (for example from http://framework.zend.com/manual/2.1/en/modules/zend.view.quick-start.html#dealing-with-layouts, last example)

It's working well, but problem is, when in some action I want to setTerminate(true); (for Ajax call) it will not display only content of controller/action template, but only layout template without content! And that is what I'm not expecting.

This is how to simulate this, set layout in dispatch function (instead of attaching event, to make it cleaner) and then setTerminate in controller's action.

public function dispatch(Request $request, Response $response = null)
{
    parent::dispatch($request, $response);
    $this->layout('layout/new');
}   

public function indexAction()
{
    $model = new ViewModel();
    $model->setTerminal(true);
    return $model;
}

Again, I'm expecting that this will display only content of controler/index template, but instead of that, it display only content of layout/new without content.

I tried to set layout in action, and it working how I'm expecting.

public function indexAction()
{
    $this->layout('layout/new');

    $model = new ViewModel();
    $model->setTerminal(true);

    return $model;
}

This is working, it display only content of controller/index template and not layout.

So if I'm changing layout globaly (by attaching dispatch event) for each controller it working until I want to use one of these controllers for Ajax call and use setTerminate.

Thanks for help with that.

回答1:

When you mark your view model as terminal, listener on dispatch event replaces layout view model with view model you returned.

So it is too late to do $this->layout('layout/new'); after dispatch, you are changing template of your view model.

What you should do is attach listener. For example, from controller itself:

protected function attachDefaultListeners()
{
    //do not forget to call parent
    parent::attachDefaultListeners();

    $events = $this->getEventManager();
    //attach before action
    $events->attach(MvcEvent::EVENT_DISPATCH, array($this, 'changeLayout'), 10);
}

public function changeLayout(MvcEvent $event)
{
    $this->layout('layout/new');
}

That will set layout for your controller, but you will be able to change it from action and setTerminal() will work as expected



回答2:

If you want to only show the content from the actions view file you can use this approach without an issue:

1) Make a new layout, for ajax calls. This will replace layout.phtml

application/layout/ajax-layout.phtml

<?php echo $this->content ?>

2) Modify your action to ovverride the default layout during Ajax calls

Inside your controller/action :

// Don't render base layout if Ajax call
if($this->getRequest()->isXmlHttpRequest()) {
    $this->layout('application/layout/ajax-layout');
}

$model = new ViewModel();
return $model;

this will just render your actions content, and override your base layout :)