Commit 51f44243 by Qiang Xue

Fixes issue #172: Implemented new usage of widgets.

parent 98d2e183
<?php <?php
use yii\helpers\Html; use yii\helpers\Html;
use yii\widgets\Menu; use yii\widgets\Menu;
use yii\widgets\Breadcrumbs;
use yii\debug\Toolbar;
/** /**
* @var $this \yii\base\View * @var $this \yii\base\View
...@@ -25,7 +27,7 @@ $this->registerAssetBundle('app'); ...@@ -25,7 +27,7 @@ $this->registerAssetBundle('app');
<div class="navbar"> <div class="navbar">
<div class="navbar-inner"> <div class="navbar-inner">
<div class="container"> <div class="container">
<?php $this->widget(Menu::className(), array( <?php echo Menu::widget($this, array(
'options' => array('class' => 'nav'), 'options' => array('class' => 'nav'),
'items' => array( 'items' => array(
array('label' => 'Home', 'url' => array('/site/index')), array('label' => 'Home', 'url' => array('/site/index')),
...@@ -42,7 +44,7 @@ $this->registerAssetBundle('app'); ...@@ -42,7 +44,7 @@ $this->registerAssetBundle('app');
<!-- /.navbar --> <!-- /.navbar -->
</div> </div>
<?php $this->widget('yii\widgets\Breadcrumbs', array( <?php echo Breadcrumbs::widget($this, array(
'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : array(), 'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : array(),
)); ?> )); ?>
<?php echo $content; ?> <?php echo $content; ?>
...@@ -58,7 +60,7 @@ $this->registerAssetBundle('app'); ...@@ -58,7 +60,7 @@ $this->registerAssetBundle('app');
</div> </div>
<?php $this->endBody(); ?> <?php $this->endBody(); ?>
</div> </div>
<?php $this->widget('yii\debug\Toolbar'); ?> <?php echo Toolbar::widget($this); ?>
</body> </body>
</html> </html>
<?php $this->endPage(); ?> <?php $this->endPage(); ?>
...@@ -23,7 +23,7 @@ $this->params['breadcrumbs'][] = $this->title; ...@@ -23,7 +23,7 @@ $this->params['breadcrumbs'][] = $this->title;
If you have business inquiries or other questions, please fill out the following form to contact us. Thank you. If you have business inquiries or other questions, please fill out the following form to contact us. Thank you.
</p> </p>
<?php $form = $this->beginWidget(ActiveForm::className(), array( <?php $form = ActiveForm::begin($this, array(
'options' => array('class' => 'form-horizontal'), 'options' => array('class' => 'form-horizontal'),
'fieldConfig' => array('inputOptions' => array('class' => 'input-xlarge')), 'fieldConfig' => array('inputOptions' => array('class' => 'input-xlarge')),
)); ?> )); ?>
...@@ -33,14 +33,14 @@ $this->params['breadcrumbs'][] = $this->title; ...@@ -33,14 +33,14 @@ $this->params['breadcrumbs'][] = $this->title;
<?php echo $form->field($model, 'body')->textArea(array('rows' => 6)); ?> <?php echo $form->field($model, 'body')->textArea(array('rows' => 6)); ?>
<?php <?php
$field = $form->field($model, 'verifyCode'); $field = $form->field($model, 'verifyCode');
echo $field->begin(); echo $field->begin()
echo $field->label(); . $field->label()
$this->widget(Captcha::className()); . Captcha::widget($this)
echo Html::activeTextInput($model, 'verifyCode', array('class' => 'input-medium')); . Html::activeTextInput($model, 'verifyCode', array('class' => 'input-medium'))
echo $field->error(); . $field->error()
echo $field->end(); . $field->end();
?> ?>
<div class="form-actions"> <div class="form-actions">
<?php echo Html::submitButton('Submit', null, null, array('class' => 'btn btn-primary')); ?> <?php echo Html::submitButton('Submit', null, null, array('class' => 'btn btn-primary')); ?>
</div> </div>
<?php $this->endWidget(); ?> <?php ActiveForm::end(); ?>
...@@ -14,11 +14,11 @@ $this->params['breadcrumbs'][] = $this->title; ...@@ -14,11 +14,11 @@ $this->params['breadcrumbs'][] = $this->title;
<p>Please fill out the following fields to login:</p> <p>Please fill out the following fields to login:</p>
<?php $form = $this->beginWidget(ActiveForm::className(), array('options' => array('class' => 'form-horizontal'))); ?> <?php $form = ActiveForm::begin($this, array('options' => array('class' => 'form-horizontal'))); ?>
<?php echo $form->field($model, 'username')->textInput(); ?> <?php echo $form->field($model, 'username')->textInput(); ?>
<?php echo $form->field($model, 'password')->passwordInput(); ?> <?php echo $form->field($model, 'password')->passwordInput(); ?>
<?php echo $form->field($model, 'rememberMe')->checkbox(); ?> <?php echo $form->field($model, 'rememberMe')->checkbox(); ?>
<div class="form-actions"> <div class="form-actions">
<?php echo Html::submitButton('Login', null, null, array('class' => 'btn btn-primary')); ?> <?php echo Html::submitButton('Login', null, null, array('class' => 'btn btn-primary')); ?>
</div> </div>
<?php $this->endWidget(); ?> <?php ActiveForm::end(); ?>
...@@ -209,6 +209,26 @@ if (isset($_POST['Post'])) { ...@@ -209,6 +209,26 @@ if (isset($_POST['Post'])) {
``` ```
Widgets
-------
Using a widget is more straightforward in 2.0. You mainly use the `begin()`, `end()` and `widget()`
methods of the `Widget` class. For example,
```php
// $this refers to the View object
// Note that you have to "echo" the result to display it
echo \yii\widgets\Menu::widget($this, array('items' => $items));
// $this refers to the View object
$form = \yii\widgets\ActiveForm::begin($this);
... form inputs here ...
\yii\widgets\ActiveForm::end();
```
Previously in 1.1, you would have to enter the widget class names as strings via the `beginWidget()`,
`endWidget()` and `widget()` methods of `CBaseController`. The approach above gets better IDE support.
Themes Themes
------ ------
...@@ -309,13 +329,13 @@ is a container consisting of a label, an input, and an error message. It is repr ...@@ -309,13 +329,13 @@ is a container consisting of a label, an input, and an error message. It is repr
as an `ActiveField` object. Using fields, you can build a form more cleanly than before: as an `ActiveField` object. Using fields, you can build a form more cleanly than before:
```php ```php
<?php $form = $this->beginWidget('yii\widgets\ActiveForm'); ?> <?php $form = yii\widgets\ActiveForm::begin(); ?>
<?php echo $form->field($model, 'username')->textInput(); ?> <?php echo $form->field($model, 'username')->textInput(); ?>
<?php echo $form->field($model, 'password')->passwordInput(); ?> <?php echo $form->field($model, 'password')->passwordInput(); ?>
<div class="form-actions"> <div class="form-actions">
<?php echo Html::submitButton('Login'); ?> <?php echo Html::submitButton('Login'); ?>
</div> </div>
<?php $this->endWidget(); ?> <?php yii\widgets\ActiveForm::end(); ?>
``` ```
......
...@@ -11,6 +11,9 @@ use Yii; ...@@ -11,6 +11,9 @@ use Yii;
use yii\base\Application; use yii\base\Application;
use yii\helpers\FileHelper; use yii\helpers\FileHelper;
use yii\helpers\Html; use yii\helpers\Html;
use yii\widgets\Block;
use yii\widgets\ContentDecorator;
use yii\widgets\FragmentCache;
/** /**
* View represents a view object in the MVC pattern. * View represents a view object in the MVC pattern.
...@@ -108,12 +111,6 @@ class View extends Component ...@@ -108,12 +111,6 @@ class View extends Component
*/ */
public $blocks; public $blocks;
/** /**
* @var Widget[] the widgets that are currently being rendered (not ended). This property
* is maintained by [[beginWidget()]] and [[endWidget()]] methods. Do not modify it directly.
* @internal
*/
public $widgetStack = array();
/**
* @var array a list of currently active fragment cache widgets. This property * @var array a list of currently active fragment cache widgets. This property
* is used internally to implement the content caching feature. Do not modify it directly. * is used internally to implement the content caching feature. Do not modify it directly.
* @internal * @internal
...@@ -364,91 +361,16 @@ class View extends Component ...@@ -364,91 +361,16 @@ class View extends Component
} }
/** /**
* Creates a widget.
* This method will use [[Yii::createObject()]] to create the widget.
* @param string $class the widget class name or path alias
* @param array $properties the initial property values of the widget.
* @return Widget the newly created widget instance
*/
public function createWidget($class, $properties = array())
{
$properties['class'] = $class;
if (!isset($properties['view'])) {
$properties['view'] = $this;
}
return Yii::createObject($properties);
}
/**
* Creates and runs a widget.
* Compared with [[createWidget()]], this method does one more thing: it will
* run the widget after it is created.
* @param string $class the widget class name or path alias
* @param array $properties the initial property values of the widget.
* @param boolean $captureOutput whether to capture the output of the widget and return it as a string
* @return string|Widget if $captureOutput is true, the output of the widget will be returned;
* otherwise the widget object will be returned.
*/
public function widget($class, $properties = array(), $captureOutput = false)
{
if ($captureOutput) {
ob_start();
ob_implicit_flush(false);
$widget = $this->createWidget($class, $properties);
$widget->run();
return ob_get_clean();
} else {
$widget = $this->createWidget($class, $properties);
$widget->run();
return $widget;
}
}
/**
* Begins a widget.
* This method is similar to [[createWidget()]] except that it will expect a matching
* [[endWidget()]] call after this.
* @param string $class the widget class name or path alias
* @param array $properties the initial property values of the widget.
* @return Widget the widget instance
*/
public function beginWidget($class, $properties = array())
{
$widget = $this->createWidget($class, $properties);
$this->widgetStack[] = $widget;
return $widget;
}
/**
* Ends a widget.
* Note that the rendering result of the widget is directly echoed out.
* If you want to capture the rendering result of a widget, you may use
* [[createWidget()]] and [[Widget::run()]].
* @return Widget the widget instance
* @throws InvalidCallException if [[beginWidget()]] and [[endWidget()]] calls are not properly nested
*/
public function endWidget()
{
$widget = array_pop($this->widgetStack);
if ($widget instanceof Widget) {
$widget->run();
return $widget;
} else {
throw new InvalidCallException("Unmatched beginWidget() and endWidget() calls.");
}
}
/**
* Begins recording a block. * Begins recording a block.
* This method is a shortcut to beginning [[yii\widgets\Block]] * This method is a shortcut to beginning [[Block]]
* @param string $id the block ID. * @param string $id the block ID.
* @param boolean $renderInPlace whether to render the block content in place. * @param boolean $renderInPlace whether to render the block content in place.
* Defaults to false, meaning the captured block will not be displayed. * Defaults to false, meaning the captured block will not be displayed.
* @return \yii\widgets\Block the Block widget instance * @return Block the Block widget instance
*/ */
public function beginBlock($id, $renderInPlace = false) public function beginBlock($id, $renderInPlace = false)
{ {
return $this->beginWidget('yii\widgets\Block', array( return Block::begin($this, array(
'id' => $id, 'id' => $id,
'renderInPlace' => $renderInPlace, 'renderInPlace' => $renderInPlace,
)); ));
...@@ -459,7 +381,7 @@ class View extends Component ...@@ -459,7 +381,7 @@ class View extends Component
*/ */
public function endBlock() public function endBlock()
{ {
$this->endWidget(); Block::end();
} }
/** /**
...@@ -476,12 +398,12 @@ class View extends Component ...@@ -476,12 +398,12 @@ class View extends Component
* @param string $viewFile the view file that will be used to decorate the content enclosed by this widget. * @param string $viewFile the view file that will be used to decorate the content enclosed by this widget.
* This can be specified as either the view file path or path alias. * This can be specified as either the view file path or path alias.
* @param array $params the variables (name => value) to be extracted and made available in the decorative view. * @param array $params the variables (name => value) to be extracted and made available in the decorative view.
* @return \yii\widgets\ContentDecorator the ContentDecorator widget instance * @return ContentDecorator the ContentDecorator widget instance
* @see \yii\widgets\ContentDecorator * @see ContentDecorator
*/ */
public function beginContent($viewFile, $params = array()) public function beginContent($viewFile, $params = array())
{ {
return $this->beginWidget('yii\widgets\ContentDecorator', array( return ContentDecorator::begin($this, array(
'viewFile' => $viewFile, 'viewFile' => $viewFile,
'params' => $params, 'params' => $params,
)); ));
...@@ -492,7 +414,7 @@ class View extends Component ...@@ -492,7 +414,7 @@ class View extends Component
*/ */
public function endContent() public function endContent()
{ {
$this->endWidget(); ContentDecorator::end();
} }
/** /**
...@@ -510,15 +432,15 @@ class View extends Component ...@@ -510,15 +432,15 @@ class View extends Component
* ~~~ * ~~~
* *
* @param string $id a unique ID identifying the fragment to be cached. * @param string $id a unique ID identifying the fragment to be cached.
* @param array $properties initial property values for [[\yii\widgets\FragmentCache]] * @param array $properties initial property values for [[FragmentCache]]
* @return boolean whether you should generate the content for caching. * @return boolean whether you should generate the content for caching.
* False if the cached version is available. * False if the cached version is available.
*/ */
public function beginCache($id, $properties = array()) public function beginCache($id, $properties = array())
{ {
$properties['id'] = $id; $properties['id'] = $id;
/** @var $cache \yii\widgets\FragmentCache */ /** @var $cache FragmentCache */
$cache = $this->beginWidget('yii\widgets\FragmentCache', $properties); $cache = FragmentCache::begin($this, $properties);
if ($cache->getCachedContent() !== false) { if ($cache->getCachedContent() !== false) {
$this->endCache(); $this->endCache();
return false; return false;
...@@ -532,7 +454,7 @@ class View extends Component ...@@ -532,7 +454,7 @@ class View extends Component
*/ */
public function endCache() public function endCache()
{ {
$this->endWidget(); FragmentCache::end();
} }
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
namespace yii\base; namespace yii\base;
use Yii; use Yii;
use yii\helpers\FileHelper;
/** /**
* Widget is the base class for widgets. * Widget is the base class for widgets.
...@@ -19,9 +18,9 @@ use yii\helpers\FileHelper; ...@@ -19,9 +18,9 @@ use yii\helpers\FileHelper;
class Widget extends Component class Widget extends Component
{ {
/** /**
* @var View the view object that is used to create this widget. * @var View the view object that this widget is associated with.
* This property is automatically set by [[View::createWidget()]]. * The widget will use this view object to register any needed assets.
* This property is required by [[render()]] and [[renderFile()]]. * This property is also required by [[render()]] and [[renderFile()]].
*/ */
public $view; public $view;
/** /**
...@@ -29,9 +28,85 @@ class Widget extends Component ...@@ -29,9 +28,85 @@ class Widget extends Component
*/ */
private $_id; private $_id;
/** /**
* @var integer a counter used to generate IDs for widgets. * @var integer a counter used to generate [[id]] for widgets.
* @internal
*/ */
private static $_counter = 0; public static $_counter = 0;
/**
* @var Widget[] the widgets that are currently being rendered (not ended). This property
* is maintained by [[begin()]] and [[end()]] methods.
* @internal
*/
public static $_stack = array();
/**
* Constructor.
* @param View $view the view object that this widget is associated with.
* The widget will use this view object to register any needed assets.
* It is also required by [[render()]] and [[renderFile()]].
* @param array $config name-value pairs that will be used to initialize the object properties
*/
public function __construct($view, $config = array())
{
$this->view = $view;
parent::__construct($config);
}
/**
* Begins a widget.
* This method creates an instance of the calling class. It will apply the configuration
* to the created instance. A matching [[end()]] call should be called later.
* @param View $view the view object that the newly created widget is associated with.
* @param array $config name-value pairs that will be used to initialize the object properties
* @return Widget the newly created widget instance
*/
public static function begin($view, $config = array())
{
$config['class'] = get_called_class();
/** @var Widget $widget */
$widget = Yii::createObject($config, $view);
self::$_stack[] = $widget;
return $widget;
}
/**
* Ends a widget.
* Note that the rendering result of the widget is directly echoed out.
* @return Widget the widget instance that is ended.
* @throws InvalidCallException if [[begin()]] and [[end()]] calls are not properly nested
*/
public static function end()
{
if (!empty(self::$_stack)) {
$widget = array_pop(self::$_stack);
if (get_class($widget) === get_called_class()) {
$widget->run();
return $widget;
} else {
throw new InvalidCallException("Expecting end() of " . get_class($widget) . ", found " . get_called_class());
}
} else {
throw new InvalidCallException("Unexpected " . get_called_class() . '::end() call. A matching begin() is not found.');
}
}
/**
* Creates a widget instance and runs it.
* The widget rendering result is returned by this method.
* @param View $view the view object that the newly created widget is associated with.
* @param array $config name-value pairs that will be used to initialize the object properties
* @return string the rendering result of the widget.
*/
public static function widget($view, $config = array())
{
ob_start();
ob_implicit_flush(false);
/** @var Widget $widget */
$config['class'] = get_called_class();
$widget = Yii::createObject($config, $view);
$widget->run();
return ob_get_clean();
}
/** /**
* Returns the ID of the widget. * Returns the ID of the widget.
......
...@@ -47,7 +47,7 @@ use Yii; ...@@ -47,7 +47,7 @@ use Yii;
* } * }
* *
* // display pagination * // display pagination
* $this->widget('yii\widgets\LinkPager', array( * LinkPager::widget($this, array(
* 'pages' => $pages, * 'pages' => $pages,
* )); * ));
* ~~~ * ~~~
......
...@@ -19,10 +19,11 @@ use yii\helpers\Html; ...@@ -19,10 +19,11 @@ use yii\helpers\Html;
* for the "Sample Post". He can click on "Sample Post" to view that page, or he can click on "Home" * for the "Sample Post". He can click on "Sample Post" to view that page, or he can click on "Home"
* to return to the homepage. * to return to the homepage.
* *
* To use Breadcrumbs, you need to configure its [[links]] property, which specifiesthe links to be displayed. For example, * To use Breadcrumbs, you need to configure its [[links]] property, which specifies the links to be displayed. For example,
* *
* ~~~ * ~~~
* $this->widget('yii\widgets\Breadcrumbs', array( * // $this is the view object currently being used
* echo Breadcrumbs::widget($this, array(
* 'links' => array( * 'links' => array(
* array('label' => 'Sample Post', 'url' => array('post/edit', 'id' => 1)), * array('label' => 'Sample Post', 'url' => array('post/edit', 'id' => 1)),
* 'Edit', * 'Edit',
...@@ -30,12 +31,13 @@ use yii\helpers\Html; ...@@ -30,12 +31,13 @@ use yii\helpers\Html;
* )); * ));
* ~~~ * ~~~
* *
* Because breadcrumbs usually appears in nearly every page of a website, you may consider place it in a layout view. * Because breadcrumbs usually appears in nearly every page of a website, you may consider placing it in a layout view.
* You can then use a view parameter (e.g. `$this->params['breadcrumbs']`) to configure the links in different * You can use a view parameter (e.g. `$this->params['breadcrumbs']`) to configure the links in different
* views. In the layout view, you assign this view parameter to the [[links]] property like the following: * views. In the layout view, you assign this view parameter to the [[links]] property like the following:
* *
* ~~~ * ~~~
* $this->widget('yii\widgets\Breadcrumbs', array( * // $this is the view object currently being used
* echo Breadcrumbs::widget($this, array(
* 'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : array(), * 'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : array(),
* )); * ));
* ~~~ * ~~~
......
...@@ -26,10 +26,11 @@ use yii\helpers\Html; ...@@ -26,10 +26,11 @@ use yii\helpers\Html;
* The following example shows how to use Menu: * The following example shows how to use Menu:
* *
* ~~~ * ~~~
* $this->widget('yii\widgets\Menu', array( * // $this is the view object currently being used
* echo Menu::widget($this, array(
* 'items' => array( * 'items' => array(
* // Important: you need to specify url as 'controller/action', * // Important: you need to specify url as 'controller/action',
* // not just as 'controller' even if default acion is used. * // not just as 'controller' even if default action is used.
* array('label' => 'Home', 'url' => array('site/index')), * array('label' => 'Home', 'url' => array('site/index')),
* // 'Products' menu item will be selected as long as the route is 'product/index' * // 'Products' menu item will be selected as long as the route is 'product/index'
* array('label' => 'Products', 'url' => array('product/index'), 'items' => array( * array('label' => 'Products', 'url' => array('product/index'), 'items' => array(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment