diff options
Diffstat (limited to 'framework/Web/UI')
-rw-r--r-- | framework/Web/UI/TControl.php | 143 | ||||
-rw-r--r-- | framework/Web/UI/TTemplateManager.php | 105 | ||||
-rw-r--r-- | framework/Web/UI/WebControls/TRepeater.php | 52 |
3 files changed, 203 insertions, 97 deletions
diff --git a/framework/Web/UI/TControl.php b/framework/Web/UI/TControl.php index 78c5bdfc..9ede6be5 100644 --- a/framework/Web/UI/TControl.php +++ b/framework/Web/UI/TControl.php @@ -122,6 +122,7 @@ class TControl extends TApplicationComponent implements IRenderable, IBindable const RF_CONTROLSTATE=7; // controlstate
const RF_NAMED_OBJECTS=8; // controls declared with ID on template
const RF_ADAPTER=9; // adapter
+ const RF_AUTO_BINDINGS=10; // auto data bindings
/**
* @var string control ID
@@ -722,7 +723,7 @@ class TControl extends TApplicationComponent implements IRenderable, IBindable /**
* Sets up the binding between a property (or property path) and an expression.
- * The context of the expression is the control itself.
+ * The context of the expression is the template control (or the control itself if it is a page).
* @param string the property name, or property path
* @param string the expression
*/
@@ -741,6 +742,19 @@ class TControl extends TApplicationComponent implements IRenderable, IBindable }
/**
+ * Sets up the binding between a property (or property path) and an expression.
+ * Unlike regular databinding, the expression bound by this method
+ * is automatically evaluated during {@link prerenderRecursive()}.
+ * The context of the expression is the template control (or the control itself if it is a page).
+ * @param string the property name, or property path
+ * @param string the expression
+ */
+ public function autoBindProperty($name,$expression)
+ {
+ $this->_rf[self::RF_AUTO_BINDINGS][$name]=$expression;
+ }
+
+ /**
* Performs the databinding for this control.
*/
public function dataBind()
@@ -760,8 +774,24 @@ class TControl extends TApplicationComponent implements IRenderable, IBindable {
if(isset($this->_rf[self::RF_DATA_BINDINGS]))
{
+ if(($context=$this->getTemplateControl())===null)
+ $context=$this;
foreach($this->_rf[self::RF_DATA_BINDINGS] as $property=>$expression)
- $this->setSubProperty($property,$this->evaluateExpression($expression));
+ $this->setSubProperty($property,$context->evaluateExpression($expression));
+ }
+ }
+
+ /**
+ * Auto databinding properties of the control.
+ */
+ protected function autoDataBindProperties()
+ {
+ if(isset($this->_rf[self::RF_AUTO_BINDINGS]))
+ {
+ if(($context=$this->getTemplateControl())===null)
+ $context=$this;
+ foreach($this->_rf[self::RF_AUTO_BINDINGS] as $property=>$expression)
+ $this->setSubProperty($property,$context->evaluateExpression($expression));
}
}
@@ -1159,6 +1189,8 @@ class TControl extends TApplicationComponent implements IRenderable, IBindable {
if($this->_stage<self::CS_LOADED)
{
+ if(($context=$this->getTemplateControl())===null)
+ $context=$this;
if(isset($this->_rf[self::RF_ADAPTER]))
$this->_rf[self::RF_ADAPTER]->onLoad(null);
else
@@ -1167,8 +1199,10 @@ class TControl extends TApplicationComponent implements IRenderable, IBindable if($this->getHasControls())
{
foreach($this->_rf[self::RF_CONTROLS] as $control)
+ {
if($control instanceof TControl)
$control->loadRecursive();
+ }
}
if($this->_stage<self::CS_LOADED)
$this->_stage=self::CS_LOADED;
@@ -1180,6 +1214,8 @@ class TControl extends TApplicationComponent implements IRenderable, IBindable */
protected function preRenderRecursive()
{
+ $this->autoDataBindProperties();
+
if($this->getVisible(false))
{
$this->ensureChildControls();
@@ -1190,8 +1226,12 @@ class TControl extends TApplicationComponent implements IRenderable, IBindable if($this->getHasControls())
{
foreach($this->_rf[self::RF_CONTROLS] as $control)
+ {
if($control instanceof TControl)
$control->preRenderRecursive();
+ else if($control instanceof TCompositeLiteral)
+ $control->evaluateDynamicContent();
+ }
}
}
$this->_stage=self::CS_PRERENDERED;
@@ -2030,4 +2070,103 @@ class TCommandEventParameter extends TEventParameter }
}
+
+/**
+ * TCompositeLiteral class
+ *
+ * TCompositeLiteral is used internally by {@link TTemplate} for representing
+ * consecutive static strings, expressions and statements.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Revision: $ $Date: $
+ * @package System.Web.UI
+ * @since 3.0
+ */
+class TCompositeLiteral extends TComponent implements IRenderable, IBindable
+{
+ const TYPE_EXPRESSION=0;
+ const TYPE_STATEMENTS=1;
+ const TYPE_DATABINDING=2;
+ private $_container=null;
+ private $_items=array();
+ private $_expressions=array();
+ private $_statements=array();
+ private $_bindings=array();
+
+ /**
+ * Constructor.
+ * @param array list of items to be represented by TCompositeLiteral
+ */
+ public function __construct($items)
+ {
+ $this->_items=array();
+ $this->_expressions=array();
+ $this->_statements=array();
+ foreach($items as $id=>$item)
+ {
+ if(is_array($item))
+ {
+ if($item[0]===self::TYPE_EXPRESSION)
+ $this->_expressions[$id]=$item[1];
+ else if($item[0]===self::TYPE_STATEMENTS)
+ $this->_statements[$id]=$item[1];
+ else if($item[0]===self::TYPE_DATABINDING)
+ $this->_bindings[$id]=$item[1];
+ $this->_items[$id]='';
+ }
+ else
+ $this->_items[$id]=$item;
+ }
+ }
+
+ /**
+ * @return TComponent container of this component. It serves as the evaluation context of expressions and statements.
+ */
+ public function getContainer()
+ {
+ return $this->_container;
+ }
+
+ /**
+ * @param TComponent container of this component. It serves as the evaluation context of expressions and statements.
+ */
+ public function setContainer(TComponent $value)
+ {
+ $this->_container=$value;
+ }
+
+ /**
+ * Evaluates the expressions and/or statements in the component.
+ */
+ public function evaluateDynamicContent()
+ {
+ $context=$this->_container===null?$this:$this->_container;
+ foreach($this->_expressions as $id=>$expression)
+ $this->_items[$id]=$context->evaluateExpression($expression);
+ foreach($this->_statements as $id=>$statement)
+ $this->_items[$id]=$context->evaluateStatements($statement);
+ }
+
+ /**
+ * Performs databindings.
+ * This method is required by {@link IBindable}
+ */
+ public function dataBind()
+ {
+ $context=$this->_container===null?$this:$this->_container;
+ foreach($this->_bindings as $id=>$binding)
+ $this->_items[$id]=$context->evaluateExpression($binding);
+ }
+
+ /**
+ * Renders the content stored in this component.
+ * This method is required by {@link IRenderable}
+ * @param ITextWriter
+ */
+ public function render($writer)
+ {
+ $writer->write(implode('',$this->_items));
+ }
+}
+
?>
\ No newline at end of file diff --git a/framework/Web/UI/TTemplateManager.php b/framework/Web/UI/TTemplateManager.php index 7961a121..488a3a3b 100644 --- a/framework/Web/UI/TTemplateManager.php +++ b/framework/Web/UI/TTemplateManager.php @@ -397,7 +397,6 @@ class TTemplate extends TApplicationComponent implements ITemplate */
protected function configureProperty($component,$name,$value)
{
- $setter='set'.$name;
if(is_array($value))
{
switch($value[0])
@@ -406,19 +405,23 @@ class TTemplate extends TApplicationComponent implements ITemplate $component->bindProperty($name,$value[1]);
break;
case self::CONFIG_EXPRESSION:
- $component->$setter($component->evaluateExpression($value[1]));
+ $component->autoBindProperty($name,$value[1]);
break;
case self::CONFIG_TEMPLATE:
+ $setter='set'.$name;
$component->$setter($value[1]);
break;
case self::CONFIG_ASSET: // asset URL
+ $setter='set'.$name;
$url=$this->publishFilePath($this->_contextPath.'/'.$value[1]);
$component->$setter($url);
break;
case self::CONFIG_PARAMETER: // application parameter
+ $setter='set'.$name;
$component->$setter($this->getApplication()->getParameters()->itemAt($value[1]));
break;
case self::CONFIG_LOCALIZATION:
+ $setter='set'.$name;
$component->$setter(Prado::localize($value[1]));
break;
default: // an error if reaching here
@@ -426,7 +429,10 @@ class TTemplate extends TApplicationComponent implements ITemplate }
}
else
+ {
+ $setter='set'.$name;
$component->$setter($value);
+ }
}
/**
@@ -817,7 +823,7 @@ class TTemplate extends TApplicationComponent implements ITemplate {
foreach($attributes as $name=>$att)
{
- if(is_array($att) && $att[0]===self::CONFIG_DATABIND)
+ if(is_array($att) && ($att[0]===self::CONFIG_DATABIND || $att[0]===self::CONFIG_EXPRESSION))
throw new TConfigurationException('template_databind_forbidden',$type,$name);
if(($pos=strpos($name,'.'))!==false)
{
@@ -846,95 +852,4 @@ class TTemplate extends TApplicationComponent implements ITemplate }
}
-/**
- * TCompositeLiteral class
- *
- * TCompositeLiteral is used internally by {@link TTemplate} for representing
- * consecutive static strings, expressions and statements.
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @version $Revision: $ $Date: $
- * @package System.Web.UI
- * @since 3.0
- */
-class TCompositeLiteral extends TComponent implements IRenderable, IBindable
-{
- const TYPE_EXPRESSION=0;
- const TYPE_STATEMENTS=1;
- const TYPE_DATABINDING=2;
- private $_container=null;
- private $_items=array();
- private $_expressions=array();
- private $_statements=array();
- private $_bindings=array();
-
- /**
- * Constructor.
- * @param array list of items to be represented by TCompositeLiteral
- */
- public function __construct($items)
- {
- $this->_items=array();
- $this->_expressions=array();
- $this->_statements=array();
- foreach($items as $id=>$item)
- {
- if(is_array($item))
- {
- if($item[0]===self::TYPE_EXPRESSION)
- $this->_expressions[$id]=$item[1];
- else if($item[0]===self::TYPE_STATEMENTS)
- $this->_statements[$id]=$item[1];
- else if($item[0]===self::TYPE_DATABINDING)
- $this->_bindings[$id]=$item[1];
- $this->_items[$id]='';
- }
- else
- $this->_items[$id]=$item;
- }
- }
-
- /**
- * @return TComponent container of this component. It serves as the evaluation context of expressions and statements.
- */
- public function getContainer()
- {
- return $this->_container;
- }
-
- /**
- * @param TComponent container of this component. It serves as the evaluation context of expressions and statements.
- */
- public function setContainer(TComponent $value)
- {
- $this->_container=$value;
- }
-
- /**
- * Renders the content stored in this component.
- * This method is required by {@link IRenderable}
- * @param ITextWriter
- */
- public function render($writer)
- {
- $context=$this->_container===null?$this:$this->_container;
- foreach($this->_expressions as $id=>$expression)
- $this->_items[$id]=$context->evaluateExpression($expression);
- foreach($this->_statements as $id=>$statement)
- $this->_items[$id]=$context->evaluateStatements($statement);
- $writer->write(implode('',$this->_items));
- }
-
- /**
- * Performs databindings.
- * This method is required by {@link IBindable}
- */
- public function dataBind()
- {
- $context=$this->_container===null?$this:$this->_container;
- foreach($this->_bindings as $id=>$binding)
- $this->_items[$id]=$context->evaluateExpression($binding);
- }
-}
-
-?>
\ No newline at end of file +?>
diff --git a/framework/Web/UI/WebControls/TRepeater.php b/framework/Web/UI/WebControls/TRepeater.php index 3d57fbd5..1acdc766 100644 --- a/framework/Web/UI/WebControls/TRepeater.php +++ b/framework/Web/UI/WebControls/TRepeater.php @@ -259,6 +259,35 @@ class TRepeater extends TDataBoundControl implements INamingContainer }
/**
+ * @return string the field of the data source that provides the keys of the list items.
+ */
+ public function getDataKeyField()
+ {
+ return $this->getViewState('DataKeyField','');
+ }
+
+ /**
+ * @param string the field of the data source that provides the keys of the list items.
+ */
+ public function setDataKeyField($value)
+ {
+ $this->setViewState('DataKeyField',$value,'');
+ }
+
+ /**
+ * @return TList the keys used in the data listing control.
+ */
+ public function getDataKeys()
+ {
+ if(($dataKeys=$this->getViewState('DataKeys',null))===null)
+ {
+ $dataKeys=new TList;
+ $this->setViewState('DataKeys',$dataKeys,null);
+ }
+ return $dataKeys;
+ }
+
+ /**
* Creates a repeater item instance based on the item type and index.
* @param integer zero-based item index
* @param string item type, may be 'Header', 'Footer', 'Empty', 'Item', 'Separator', 'AlternatingItem'.
@@ -405,11 +434,18 @@ class TRepeater extends TDataBoundControl implements INamingContainer protected function performDataBinding($data)
{
$this->reset();
+
+ $keys=$this->getDataKeys();
+ $keys->clear();
+ $keyField=$this->getDataKeyField();
+
$items=$this->getItems();
$itemIndex=0;
$hasSeparator=$this->_separatorTemplate!==null;
foreach($data as $dataItem)
{
+ if($keyField!=='')
+ $keys->add($this->getDataFieldValue($dataItem,$keyField));
if($itemIndex===0 && $this->_headerTemplate!==null)
$this->_header=$this->createItemInternal(-1,self::IT_HEADER,true,null);
if($hasSeparator && $itemIndex>0)
@@ -493,6 +529,22 @@ class TRepeater extends TDataBoundControl implements INamingContainer {
$this->raiseEvent('OnItemCommand',$this,$param);
}
+
+ /**
+ * Returns the value of the data at the specified field.
+ * If data is an array, TMap or TList, the value will be returned at the index
+ * of the specified field. If the data is a component with a property named
+ * as the field name, the property value will be returned.
+ * Otherwise, an exception will be raised.
+ * @param mixed data item
+ * @param mixed field name
+ * @return mixed data value at the specified field
+ * @throws TInvalidDataValueException if the data is invalid
+ */
+ protected function getDataFieldValue($data,$field)
+ {
+ return TDataFieldAccessor::getDataFieldValue($data,$field);
+ }
}
/**
|