From b07499534b6d0ed57a2b80c4d95354ca3791c236 Mon Sep 17 00:00:00 2001 From: xue <> Date: Sat, 15 Apr 2006 14:21:40 +0000 Subject: Breaking change! Changed context of the expressions in template to the template control. Evaluations of <%= %> are now all in PreRender stage. --- framework/Exceptions/messages.txt | 2 +- framework/Web/UI/TControl.php | 143 +++++++++++++++++++++++++++++++++- framework/Web/UI/TTemplateManager.php | 104 +++---------------------- 3 files changed, 152 insertions(+), 97 deletions(-) (limited to 'framework') diff --git a/framework/Exceptions/messages.txt b/framework/Exceptions/messages.txt index dd680098..e11d4261 100644 --- a/framework/Exceptions/messages.txt +++ b/framework/Exceptions/messages.txt @@ -110,7 +110,7 @@ template_property_unknown = {0} has no property called '{1}'. template_event_unknown = {0} has no event called '{1}'. template_property_readonly = {0} has a read-only property '{1}'. template_event_forbidden = {0} is a non-control component. No handler can be attached to its event '{1}' in a template. -template_databind_forbidden = {0} is a non-control component. No databinding expression can be set to its property '{1}'. +template_databind_forbidden = {0} is a non-control component. Expressions cannot be bound to its property '{1}'. template_component_required = '{0}' is not a component. Only components can appear in a template. template_format_invalid = Error in {0} (line {1}) : {2} template_format_invalid2 = Error at line {0} of the following template: {1} {2} diff --git a/framework/Web/UI/TControl.php b/framework/Web/UI/TControl.php index 7b22760f..2a3a6a30 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 @@ -721,7 +722,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 */ @@ -739,6 +740,19 @@ class TControl extends TApplicationComponent implements IRenderable, IBindable unset($this->_rf[self::RF_DATA_BINDINGS][$name]); } + /** + * 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. */ @@ -759,8 +773,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)); } } @@ -1157,6 +1187,8 @@ class TControl extends TApplicationComponent implements IRenderable, IBindable { if($this->_stagegetTemplateControl())===null) + $context=$this; if(isset($this->_rf[self::RF_ADAPTER])) $this->_rf[self::RF_ADAPTER]->onLoad(null); else @@ -1165,8 +1197,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_stage=self::CS_LOADED; @@ -1178,6 +1212,8 @@ class TControl extends TApplicationComponent implements IRenderable, IBindable */ protected function preRenderRecursive() { + $this->autoDataBindProperties(); + if($this->getVisible(false)) { $this->ensureChildControls(); @@ -1188,8 +1224,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; @@ -2016,4 +2056,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 + * @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 fa4cbcbf..dd3f7af8 100644 --- a/framework/Web/UI/TTemplateManager.php +++ b/framework/Web/UI/TTemplateManager.php @@ -312,6 +312,7 @@ class TTemplate extends TApplicationComponent implements ITemplate { if($object[1] instanceof TCompositeLiteral) { + // need to clone a new object because the one in template is reused $o=clone $object[1]; $o->setContainer($tplControl); $parent->addParsedObject($o); @@ -374,7 +375,6 @@ class TTemplate extends TApplicationComponent implements ITemplate */ protected function configureProperty($component,$name,$value) { - $setter='set'.$name; if(is_array($value)) { switch($value[0]) @@ -383,19 +383,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 @@ -403,7 +407,10 @@ class TTemplate extends TApplicationComponent implements ITemplate } } else + { + $setter='set'.$name; $component->$setter($value); + } } /** @@ -794,7 +801,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) { @@ -823,95 +830,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 - * @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 -- cgit v1.2.3