CActiveForm ajax validation的问题

第一次出问题是因为我在layout文件中已经加载了jQuery.js(那个layout文件是美工设计好的模板,自己就用了jquery), 然后create activeform widget的时候registerCoreScript又添了一个jquery进去. 就冲突了.

第二次出问题是因为我没有写:

<div class="row">


    <?php echo $form->label($model,'your new email address'); ?>


    <?php echo $form->textField($model,'username') ?>


<?php echo $form->error($model,'username'); ?>    <---忘记写这一句, 就不会执行server side 的ajax validation. 


</div>

后来看看源码CActiveForm.php339行, 如果不设定error, 就要进这个if, 然后就return了, yiiactiveform.js都没有加载, 自然不能执行ajax动作:

if(!$this->enableAjaxValidation && !$this->enableClientValidation || empty($this->attributes)).

第三次出问题情况比较复杂, 而且很奇怪.

暂时只知道是跟我用ajax载入了页面有关, 如果在第2步中不用ajax而用刷新页面方式, 就没有问题正常使用activeform

还是先记录在这里:

1.首先是action->render view, 没有问题

public function actionProfile($page=null)

{

...


	if(null===$page)


		$this->render('account');


	else


		//load from ajax


		$this->renderPartial($page);

}

2.account.php中:

<p class="opLink"><a href="#index.php?r=site/show&page=changeEmail" tar="#content" class="ajaxLoad">Change your email</a></p>

就是用ajax载入了href去掉#号的url.

3.changeEmail.php:

<div id="changeEmail">

<?php

$model = new User;

$form=$this->beginWidget(‘CActiveForm’, array(

'action'=&gt;'index.php?r=user/changeEmail',


'id'=&gt;'user-changeEmail-form',


'enableAjaxValidation'=&gt;true,


'enableClientValidation'=&gt;true,


'focus'=&gt;array(&#036;model,'username'),

)); ?>

&lt;?php echo &#036;form-&gt;errorSummary(&#036;model); ?&gt;





&lt;div class=&quot;row&quot;&gt;


    &lt;?php echo &#036;form-&gt;label(&#036;model,'your new email address'); ?&gt;


    &lt;?php echo &#036;form-&gt;textField(&#036;model,'username') ?&gt;


&lt;?php echo &#036;form-&gt;error(&#036;model,'username'); ?&gt;


&lt;/div&gt;





&lt;div class=&quot;row&quot;&gt;


    &lt;?php echo &#036;form-&gt;label(&#036;model,'your current password'); ?&gt;


    &lt;?php echo &#036;form-&gt;passwordField(&#036;model,'password') ?&gt;


&lt;?php echo &#036;form-&gt;error(&#036;model,'password'); ?&gt;   

</div>

&lt;div class=&quot;row submit&quot;&gt;


    &lt;?php echo CHtml::submitButton('Change it&#33;'); ?&gt;


&lt;/div&gt;

<?php $this->endWidget(); ?>

</div><!-- form -->

然后浏览器中查看源文件, 硬是不加载yiiactiveform.js, 以及插入js触发代码. 跟踪php代码进CactiveForm.php的run()方法, 又确实执行了yiiactiveform的注册…

我后来在页面头部手动加入 Yii::app()->clientScript->registerCoreScript("yiiactiveform"); 倒是肯定一定的可以载入这个js了, 但是…因为js触发代码没有插入, 还是不能执行ajax validation…

不知如何是好.

'clientOptions '=>array(

‘validateOnSubmit’=>true,

);

自己到 这里 看所有可用的设置

在一个 action 不能是硬编码的 要用控制器创建 ‘action’=>‘index.php?r=user/changeEmail’, 改为 $this->createUrl(‘user/changeEmail’); 因为url可用模式重写 可以变成其他形式:index.php/user/changeEmail …

手动硬编码的将不能自动完成这种格式的隐式转换 你可以追踪看看createUrl的底层实现

You saved my life.

Oh no…

我加入了

'clientOptions '=>array(

‘validateOnType’=>true,

‘validateOnChange’=>true,

); 并且也使用createUrl来生成url了

但是, ajax问题依然存在:

<a href="#<?php echo $this->createUrl(‘site/showPartial’,array(‘page’=>’/user/changeEmail’));?>" tar="#content" class=“ajaxLoad”>Change your email</a> 这样做就不会载入yiiactiveform 和 JS触发代码

改为页面刷新, 就一切正常

<a href="<?php echo $this->createUrl(‘site/showPartial’,array(‘page’=>’/user/changeEmail’));?>" tar="#content" class=“NoAjaxLoad”>Change your email</a>

我又想了一下您说的, 我需要的validateOnChange这个默认是true, 应该不用指定,

然后我跟踪了createUrl的代码, 里面也没有载入yiiactiveform或者其他的准备工作, 再仔细想想, 用createUrl的好处应该就是你说的, 可以以后改变URL模式, 但是应该跟是否ajax载入页面而导致activeform不能执行ajax validation无关.

所以关键的问题我还是没有清楚:

即在同等情况下, 即widget activeform的代码不做变化, 仅仅是页面是否用ajax load, 就会导致activeform可用与不可用, 是为什么?

再次检查能够成功ajax validation的传统页面刷新方式的调用堆栈, 跟着跑一遍流程, 初步发现一点线索:

那些script注册了以后, 其实并没有马上render出来, 而是在Controller的render调用以后, 再跑进clientScript的render方法. 在那里才真正插入html中.

然后再看调用堆栈, 程序流是怎么进入clientScript的render()的? 就找到了processOutput()这个方法, 联想到render的代码:

public function render($view,$data=null,$return=false)

{


	if(&#036;this-&gt;beforeRender(&#036;view))


	{


		&#036;output=&#036;this-&gt;renderPartial(&#036;view,&#036;data,true);             &lt;----------注意这里


		if((&#036;layoutFile=&#036;this-&gt;getLayoutFile(&#036;this-&gt;layout))&#33;==false)


			&#036;output=&#036;this-&gt;renderFile(&#036;layoutFile,array('content'=&gt;&#036;output),true);





		&#036;this-&gt;afterRender(&#036;view,&#036;output);





		&#036;output=&#036;this-&gt;processOutput(&#036;output);                      &lt;----------注意这里





		if(&#036;return)


			return &#036;output;


		else


			echo &#036;output;


	}


}

而我ajax载入的时候, 并没用render(), 用的是renderPartial(), 显然就没有processOutput()这个流程, 自然那些js script都不会被插入html页面中. 正打算手动插入processOutput, 瞟了一眼renderPartial的定义:

public function renderPartial($view,$data=null,$return=false,$processOutput=false)

                                                              ^


                                                              +-----------这不正是我需要的么...

然后改action中的

$this->renderPartial($page);

为:

$this->renderPartial($page, null, false, true);

至此功德圆满.