From 02b908bd9c85520683d2cbfa98b8aad24f4724a2 Mon Sep 17 00:00:00 2001
From: Qiang Xue <qiang.xue@gmail.com>
Date: Tue, 28 Jan 2014 14:03:39 -0500
Subject: [PATCH] Fixes #938: added `yii\web\View::renderAjax` and `yii\web\Controller::renderAjax`.

---
 framework/web/Controller.php | 20 ++++++++++++++++++++
 framework/web/View.php       | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
 2 files changed, 90 insertions(+), 13 deletions(-)

diff --git a/framework/web/Controller.php b/framework/web/Controller.php
index 842028e..6923883 100644
--- a/framework/web/Controller.php
+++ b/framework/web/Controller.php
@@ -16,6 +16,8 @@ use yii\helpers\Html;
  *
  * @property string $canonicalUrl The canonical URL of the currently requested page. This property is
  * read-only.
+ * @property View $view The view object that can be used to render views or view files. This property is
+ * read-only.
  *
  * @author Qiang Xue <qiang.xue@gmail.com>
  * @since 2.0
@@ -32,6 +34,24 @@ class Controller extends \yii\base\Controller
 	 */
 	public $actionParams = [];
 
+
+	/**
+	 * Renders a view in response to an AJAX request.
+	 *
+	 * This method is similar to [[renderPartial()]] except that it will inject into
+	 * the rendering result with JS/CSS scripts and files which are registered with the view.
+	 * For this reason, you should use this method instead of [[renderPartial()]] to render
+	 * a view to respond to an AJAX request.
+	 *
+	 * @param string $view the view name. Please refer to [[render()]] on how to specify a view name.
+	 * @param array $params the parameters (name-value pairs) that should be made available in the view.
+	 * @return string the rendering result.
+	 */
+	public function renderAjax($view, $params = [])
+	{
+		return $this->getView()->renderAjax($view, $params, $this);
+	}
+
 	/**
 	 * Binds the parameters to the action.
 	 * This method is invoked by [[Action]] when it begins to run with the given parameters.
diff --git a/framework/web/View.php b/framework/web/View.php
index 22fd3e3..75854aa 100644
--- a/framework/web/View.php
+++ b/framework/web/View.php
@@ -128,6 +128,38 @@ class View extends \yii\base\View
 	private $_assetManager;
 
 	/**
+	 * Renders a view in response to an AJAX request.
+	 *
+	 * This method is similar to [[render()]] except that it will surround the view being rendered
+	 * with the calls of [[beginPage()]], [[head()]], [[beginBody()]], [[endBody()]] and [[endPage()]].
+	 * By doing so, the method is able to inject into the rendering result with JS/CSS scripts and files
+	 * that are registered with the view.
+	 *
+	 * @param string $view the view name. Please refer to [[render()]] on how to specify this parameter.
+	 * @param array $params the parameters (name-value pairs) that will be extracted and made available in the view file.
+	 * @param object $context the context that the view should use for rendering the view. If null,
+	 * existing [[context]] will be used.
+	 * @return string the rendering result
+	 * @see render()
+	 */
+	public function renderAjax($view, $params = [], $context = null)
+	{
+		$viewFile = $this->findViewFile($view, $context);
+
+		ob_start();
+		ob_implicit_flush(false);
+
+		$this->beginPage();
+		$this->head();
+		$this->beginBody();
+		echo $this->renderFile($viewFile, $params, $context);
+		$this->endBody();
+		$this->endPage(true);
+
+		return ob_get_clean();
+	}
+
+	/**
 	 * Registers the asset manager being used by this view object.
 	 * @return \yii\web\AssetManager the asset manager. Defaults to the "assetManager" application component.
 	 */
@@ -147,8 +179,11 @@ class View extends \yii\base\View
 
 	/**
 	 * Marks the ending of an HTML page.
+	 * @param boolean $ajaxMode whether the view is rendering in AJAX mode.
+	 * If true, the JS scripts registered at [[POS_READY]] and [[POS_LOAD]] positions
+	 * will be rendered at the end of the view like normal scripts.
 	 */
-	public function endPage()
+	public function endPage($ajaxMode = false)
 	{
 		$this->trigger(self::EVENT_END_PAGE);
 
@@ -159,7 +194,7 @@ class View extends \yii\base\View
 		echo strtr($content, [
 			self::PH_HEAD => $this->renderHeadHtml(),
 			self::PH_BODY_BEGIN => $this->renderBodyBeginHtml(),
-			self::PH_BODY_END => $this->renderBodyEndHtml(),
+			self::PH_BODY_END => $this->renderBodyEndHtml($ajaxMode),
 		]);
 
 		$this->metaTags = null;
@@ -450,25 +485,47 @@ class View extends \yii\base\View
 	/**
 	 * Renders the content to be inserted at the end of the body section.
 	 * The content is rendered using the registered JS code blocks and files.
+	 * @param boolean $ajaxMode whether the view is rendering in AJAX mode.
+	 * If true, the JS scripts registered at [[POS_READY]] and [[POS_LOAD]] positions
+	 * will be rendered at the end of the view like normal scripts.
 	 * @return string the rendered content
 	 */
-	protected function renderBodyEndHtml()
+	protected function renderBodyEndHtml($ajaxMode)
 	{
 		$lines = [];
+
 		if (!empty($this->jsFiles[self::POS_END])) {
 			$lines[] = implode("\n", $this->jsFiles[self::POS_END]);
 		}
-		if (!empty($this->js[self::POS_END])) {
-			$lines[] = Html::script(implode("\n", $this->js[self::POS_END]), ['type' => 'text/javascript']);
-		}
-		if (!empty($this->js[self::POS_READY])) {
-			$js = "jQuery(document).ready(function(){\n" . implode("\n", $this->js[self::POS_READY]) . "\n});";
-			$lines[] = Html::script($js, ['type' => 'text/javascript']);
-		}
-		if (!empty($this->js[self::POS_LOAD])) {
-			$js = "jQuery(window).load(function(){\n" . implode("\n", $this->js[self::POS_LOAD]) . "\n});";
-			$lines[] = Html::script($js, ['type' => 'text/javascript']);
+
+		if ($ajaxMode) {
+			$scripts = [];
+			if (!empty($this->js[self::POS_END])) {
+				$scripts[] = implode("\n", $this->js[self::POS_END]);
+			}
+			if (!empty($this->js[self::POS_READY])) {
+				$scripts[] = implode("\n", $this->js[self::POS_READY]);
+			}
+			if (!empty($this->js[self::POS_LOAD])) {
+				$scripts[] = implode("\n", $this->js[self::POS_LOAD]);
+			}
+			if (!empty($scripts)) {
+				$lines[] = Html::script(implode("\n", $scripts), ['type' => 'text/javascript']);
+			}
+		} else {
+			if (!empty($this->js[self::POS_END])) {
+				$lines[] = Html::script(implode("\n", $this->js[self::POS_END]), ['type' => 'text/javascript']);
+			}
+			if (!empty($this->js[self::POS_READY])) {
+				$js = "jQuery(document).ready(function(){\n" . implode("\n", $this->js[self::POS_READY]) . "\n});";
+				$lines[] = Html::script($js, ['type' => 'text/javascript']);
+			}
+			if (!empty($this->js[self::POS_LOAD])) {
+				$js = "jQuery(window).load(function(){\n" . implode("\n", $this->js[self::POS_LOAD]) . "\n});";
+				$lines[] = Html::script($js, ['type' => 'text/javascript']);
+			}
 		}
+
 		return empty($lines) ? '' : implode("\n", $lines);
 	}
 }
--
libgit2 0.27.1