Zend Framework 2: автоматическое отключение макета для вызовов ajax
Запрос AJAX одному из моих действий с контроллером в настоящее время возвращает HTML-код полной страницы.
Я хочу, чтобы он возвращал HTML (содержимое .phtml) для этого конкретного действия.
Следующий код плохо решает проблему, вручную отключив макет для конкретного действия:
$viewModel = new ViewModel();
$viewModel->setTerminal(true);
return $viewModel;
Как я могу заставить мое приложение автоматически отключать макет при обнаружении AJAX-запроса? Нужно ли мне писать для этого специальную стратегию? Любые советы о том, как это сделать, очень ценятся.
Кроме того, я пробовал следующий код в своем приложении Module.php - он правильно определяет AJAX, но setTerminal() не отключает макет.
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);
}
}
Мысли?
Ответы
Ответ 1
Действительно, лучше всего написать другую Стратегию. Существует JsonStrategy, которая может автоматически обнаруживать заголовок accept, чтобы автоматически возвращать Json-Format, но, как и в случае Ajax-Calls для полных страниц, там хорошо, что он не выполняет автоматически, потому что вы МОЖЕТЕ получить полную страницу. Вышеупомянутое решение, о котором вы говорили, было бы быстрым способом.
При переходе на полную скорость у вас будет только одна дополнительная строка. Лучше всего всегда возвращать полностью квалифицированные ViewModels из вашего контроллера. Как:
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
Я думаю, проблема в том, что вы вызываете setTerminal()
в модели представления $e->getViewModel()
, которая отвечает за рендеринг макета, а не за действие. Вам нужно будет создать новую модель представления, вызвать setTerminal(true)
и вернуть ее. Я использую выделенный контроллер ajax, поэтому нет необходимости определять, является ли действие ajax или нет:
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;
}
}
и в ajax/response.phtml просто следующее:
<?= $this->response ?>
Ответ 3
Вот лучшее решение (по моему скромному мнению). Я потратил почти два дня, чтобы понять это. Об этом пока никто не сообщил в Интернете.
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);
}
Я все еще не удовлетворен. Я бы создал плагин в качестве слушателя и настроил его через файл конфигурации вместо метода onBootstrap. Но я позволю этому в следующий раз = P
Ответ 4
Я ответил на этот вопрос и, похоже, похоже на него - Переменные Access ViewModel в событии отправки
Прикрепите обратный вызов события к триггеру события dispatch
. После запуска этого события вы должны получить результат действия метода, вызвав $e->getResult()
. В случае действия, возвращающего ViewModel, оно должно позволить вам выполнить модификацию setTerminal().
Ответ 5
Решение aimfeld работает для меня, но если некоторые из вас экспериментируют с расположением шаблона, попробуйте указать модуль:
$this->viewModel->setTemplate('application/ajax/response');
Ответ 6
Лучше всего использовать JsonModel, который возвращает приятный json и отключает макет и view для вас.
public function ajaxCallAction()
{
return new JsonModel(
[
'success' => true
]
);
}
Ответ 7
У меня была эта проблема до этого и вот трюк quikc, чтобы решить это.
Прежде всего, создайте пустой макет в папке макета module/YourModule/view/layout/empty.phtml
Вы должны только эхо-изображения содержимого в этом макете <?php echo $this->content; ?>
Теперь в вашем Module.php
установите макет контроллера для макета/пустой для запроса ajax
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;
}