可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
An AJAX request to one of my controller actions currently returns the full page HTML.
I only want it to return the HTML (.phtml contents) for that particular action.
The following code poorly solves the problem by manually disabling the layout for the particular action:
$viewModel = new ViewModel(); $viewModel->setTerminal(true); return $viewModel;
How can I make my application automatically disable the layout when an AJAX request is detected? Do I need to write a custom strategy for this? Any advice on how to do this is much appreciated.
Additionally, I've tried the following code in my app Module.php - it is detecting AJAX correctly but the setTerminal() is not disabling the layout.
public function onBootstrap(EventInterface $e) { $application = $e->getApplication(); $application->getEventManager()->attach('route', array($this, 'setLayout'), 100); $this->setApplication($application); $this->initPhpSettings($e); $this->initSession($e); $this->initTranslator($e); $this->initAppDi($e); } public function setLayout(EventInterface $e) { $request = $e->getRequest(); $server = $request->getServer(); if ($request->isXmlHttpRequest()) { $view_model = $e->getViewModel(); $view_model->setTerminal(true); } }
Thoughts?
回答1:
Indeed the best thing would be to write another Strategy. There is a JsonStrategy which can auto-detect the accept header to automatically return Json-Format, but as with Ajax-Calls for fullpages, there it's good that it doesn't automatically do things, because you MAY want to get a full page. Above mentioned solution you mentioned would be the quick way to go.
When going for full speed, you'd only have one additional line. It's a best practice to always return fully qualified ViewModels from within your controller. Like:
public function indexAction() { $request = $this->getRequest(); $viewModel = new ViewModel(); $viewModel->setTemplate('module/controller/action'); $viewModel->setTerminal($request->isXmlHttpRequest()); return $viewModel->setVariables(array( //list of vars )); }
回答2:
I think the problem is that you're calling setTerminal()
on the view model $e->getViewModel()
that is responsible for rendering the layout, not the action. You'll have to create a new view model, call setTerminal(true)
, and return it. I use a dedicated ajax controller so there's no need of determining whether the action is ajax or not:
use Zend\View\Model\ViewModel; use Zend\Mvc\MvcEvent; use Zend\Mvc\Controller\AbstractActionController; class AjaxController extends AbstractActionController { protected $viewModel; public function onDispatch(MvcEvent $mvcEvent) { $this->viewModel = new ViewModel; // Don't use $mvcEvent->getViewModel()! $this->viewModel->setTemplate('ajax/response'); $this->viewModel->setTerminal(true); // Layout won't be rendered return parent::onDispatch($mvcEvent); } public function someAjaxAction() { $this->viewModel->setVariable('response', 'success'); return $this->viewModel; } }
and in ajax/response.phtml simply the following:
<?= $this->response ?>
回答3:
Here's the best solution (in my humble opinion). I've spent almost two days to figure it out. No one on the Internet posted about it so far I think.
public function onBootstrap(MvcEvent $e) { $eventManager= $e->getApplication()->getEventManager(); // The next two lines are from the Zend Skeleton Application found on git $moduleRouteListener = new ModuleRouteListener(); $moduleRouteListener->attach($eventManager); // Hybrid view for ajax calls (disable layout for xmlHttpRequests) $eventManager->getSharedManager()->attach('Zend\Mvc\Controller\AbstractController', MvcEvent::EVENT_DISPATCH, function(MvcEvent $event){ /** * @var Request $request */ $request = $event->getRequest(); $viewModel = $event->getResult(); if($request->isXmlHttpRequest()) { $viewModel->setTerminal(true); } return $viewModel; }, -95); }
I'm still not satisfied though. I would create a plugin as a listener and configure it via configuration file instead of onBootstrap method. But I'll let this for the next time =P
回答4:
I replied to this question and seems it maybe similar - Access ViewModel variables on dispatch event
Attach an event callback to the dispatch
event trigger. Once this event triggers it should allow you to obtain the result of the action method by calling $e->getResult()
. In the case of an action returning a ViewModel it should allow you to do the setTerminal() modification.
回答5:
aimfeld solution works for me, but in case some of you experiment issues with the location of the template, try to specify the module:
$this->viewModel->setTemplate('application/ajax/response');
回答6:
The best is to use JsonModel which returns nice json and disable layout&view for you.
public function ajaxCallAction() { return new JsonModel( [ 'success' => true ] ); }
回答7:
I had this problem before and here is a quikc trick to solved that.
First of all, create an empty layout in your layout folder module/YourModule/view/layout/empty.phtml
You should only echo the view content in this layout this way <?php echo $this->content; ?>
Now In your Module.php
set the controller layout to layout/empty for ajax request
namespace YourModule; use Zend\Mvc\MvcEvent; class Module { public function onBootstrap(MvcEvent $e) { $sharedEvents = $e->getApplication()->getEventManager()->getSharedManager(); $sharedEvents->attach(__NAMESPACE__, 'dispatch', function($e) { if ($e->getRequest()->isXmlHttpRequest()) { $controller = $e->getTarget(); $controller->layout('layout/empty'); } }); } }
回答8:
public function myAjaxAction() { .... // View - stuff that you returning usually in a case of non-ajax requests View->setTerminal(true); return View; }