Yiistrap - How To Render Whdatetimepicker Inside Tbactiveform

Hi,

I am playing with yiistrap and yiiwheels. I want to use WhDateTimePicker inside of TbActiveForm. I want to render also its label and validation errors, so I used method TbHtml::customActiveControlGroup:




<?php $form = $this->beginWidget('bootstrap.widgets.TbActiveForm'); ?>


// ...


    <?php $datePicker = $this->widget('yiiwheels.widgets.datetimepicker.WhDateTimePicker', array(

        'model' => $model,

        'attribute' => 'date_',

    ), true); ?>


    <?php echo TbHtml::customActiveControlGroup($datePicker, $model, 'date_'); ?>


// ...


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




Widget itself works fine and also label is rendered. But validation error is not shown. I would expect so, because all others methods named *ControlGroup render also errors. How to do it? Thanks.

I needed to do the same thing and your code helped me a great deal. Initially I also thought that error messages were not being displayed by TbHtml::customActiveControlGroup but that is not the case. I added a model rule to check that the date field is not empty (required) but on the client side this was not enforced even though client side checking of the rules have been switched on and works for the other fields. I then found that when submitting the form the server side checking is done and the error is correctly shown when the form is re-loaded after validation.

Because of the above I then inspected the HTML code that is rendered by the view and noticed that the client side JavaScript to check that the date field is not empty is not being generated.

I then replaced the WhDatePicker with a WhMaskMoney widget. Again the input field rendered fine but no client side Javascript for the field other than to define what type of input it is.

I also switched of the client side validation and replaced it with Ajax validation. Still same result. Again because the client side Javascript to do the Ajax call for the input field is not rendered.

It seem that using the TbHtml::customActiveControlGroup for the particular field prevents the Java Script for the client side validation for that field to be rendered.

That is strange. For me it doesnt work even if I try simple example like this:




    <?php echo TbHtml::errorSummary($model); ?>

    <?php $field = "<input type='text' name='ModelName[attr]'/>"; ?>

    <?php echo TbHtml::customActiveControlGroup($field, $model, 'attr'); ?>



Error is shown properly in summary, but not under the input field. Can you please post fragment of your code that is working for you? Thanks.

Ok, after some investigation I finally found solution. Modify method TbHtml::customActiveControlGroup to this:




    public static function customActiveControlGroup($input, $model, $attribute, $htmlOptions = array())

    {

        $htmlOptions['input'] = $input;

        if($model->hasErrors($attribute)) {

          $htmlOptions['color'] = TbHtml::INPUT_COLOR_ERROR;

          $htmlOptions['help'] = $model->getError($attribute);

          $htmlOptions['helpOptions'] = array('type' => TbHtml::HELP_TYPE_BLOCK);

        }


        return self::activeControlGroup(self::INPUT_TYPE_CUSTOM, $model, $attribute, $htmlOptions);

    }



Now we need to find way to make client validations working :)

Sorry, you’re right. I see the same symptoms that you do.

I think you should be able to use the below code but for the life of me a cannot get it to work and I’m too tired to figure that our now. Tomorrow is another day. ;)


        $htmlOptions['input'] = $input;

        if($model->hasErrors($attribute)) {

        	parent::addErrorCss($htmlOptions);

        	self::error($model, $attribute, $htmlOptions);

        }


        return self::activeControlGroup(self::INPUT_TYPE_CUSTOM, $model, $attribute, $htmlOptions);



Right! I finally figured it out with the help that Speculatius’ solution provided. :D Speculatius’ solution was however not the correct approach because it displayed the error message as a help message.

The trick is to see how the parent class of tbHtml handles it. cHtml is the parent class and it contains an error function that differs from the error function of the tbHtml class. In short, the code the need to be added to the customActiveControlGroup need to:

[list=1]

[*]Check if the model has an error

[*]If it does then add the error class css

[*]and then generate the HTML code to display the error message

[*]add the error HTML code to the $htmlOptions array variable as a key/value pair (‘error’/<error html text>). This need to be done because the $htmlOptions variable is analysed in the function activeControlGroup which extract the error message and add it to the HTML to be finally displayed.

[/list]

The above can be accomplished by calling two functions from the parent class (cHtml).

The final code for customActiveControlGroup can be seen below.


public static function customActiveControlGroup($input, $model, $attribute, $htmlOptions = array())

    {

        $htmlOptions['input'] = $input;

        if($model->hasErrors($attribute)) {

        	parent::addErrorCss($htmlOptions);

        	$htmlOptions['error'] = parent::error($model, $attribute);

        }


        return self::activeControlGroup(self::INPUT_TYPE_CUSTOM, $model, $attribute, $htmlOptions);

    }



I wasn’t satisfied with the solution I gave above. While it does display error messages it did not allow for client side validation. I delved through the TbHtml class and the TbActiveForm classes to understand how this can be achieved. Although it took me many hours to figure it out the solution was very easy (3 lines of code) and I’m not sure why it was not included in TbActiveForm from the start.

The trick is to add the below code to TbActiveForm.php under the widgets folder. In essence this will allow TbActiveForm to support the custom control making it VERY flexible. The solution I suggested above should be reversed since it was a hack at best.

Of course the best way to implement this is to create a new class that inherits from the TbActiveForm class and then implement the code below and not change TbActiveForm.php directly.


    /**

     * Generates a control group with a custom (pre-rendered) control  for a model attribute

     * @param string $input the rendered input field.

     * @param CModel $model the data model.

     * @param string $attribute the attribute name.

     * @param array $htmlOptions additional HTML attributes.

     * @return string the generated row.

     * @see TbHtml::customActiveControlGroup

     * 

     * EXAMPLE:

     * The below code will be used in your view in a TbActiveForm widget

     * 

     * <?php $datePicker = $this->widget('yiiwheels.widgets.datepicker.WhDatePicker', array(

     *   		'model' => $model,

     *   		'attribute' => 'event_date',

	 *			'pluginOptions' => array(

     *				'format' => 'yyyy/mm/dd'

     *			),

     *		), true); 

	 * ?>

     *

     * <?php echo $form->customControlGroup($datePicker, $model, 'event_date'); ?>	

     * 

     */

    public function customControlGroup($input, $model, $attribute, $htmlOptions = array())

    {

        $htmlOptions = $this->processRowOptions($model, $attribute, $htmlOptions);

    	return TbHtml::customActiveControlGroup($input, $model, $attribute, $htmlOptions);

    }



Thank you for the code, works great!

For convenience, I added a function datePickerControlGroup () in my class

FTActiveForm class extends TbActiveForm:





	public function datePickerControlGroup($model, $attribute, $htmlOptions = array()) {

    	// the options for the Bootstrap JavaScript plugin

    	$datePickerOptions = array(

        	'model' => $model,

        	'attribute' => $attribute,

        	'pluginOptions' => TbArray::popValue('pluginOptions', $htmlOptions, array()),

        	'events' => TbArray::popValue('events', $htmlOptions, array()),

        	'htmlOptions' => $htmlOptions,

    	);

    	$datePicker = $this->owner->widget('FT.widgets.FTDatePicker', $datePickerOptions, true);

    	return $this->customControlGroup($datePicker, $model, $attribute, $htmlOptions);

	}




By cons to use the options "append", "prepend", "span" … I also extended the class YiiWheels: FTDatePicker extends WhDatePicker and replaced renderField () function to use TbHtml:





	/**

 	* Renders field

 	*/

	public function renderField()

	{

    	list($name, $id) = $this->resolveNameID();


    	TbArray::defaultValue('id', $id, $this->htmlOptions);

    	TbArray::defaultValue('name', $name, $this->htmlOptions);


    	if ($this->hasModel()) {

        	echo TbHtml::activeTextField($this->model, $this->attribute, $this->htmlOptions);

    	} else {

        	echo TbHtml::textField($name, $this->value, $this->htmlOptions);

    	}

	}



ajaxValidation ok, but still does not work clientValidation

Very good, Gerhardvr, thank you for your solution!

Please, help needed!

I’ve read about “customActiveControlGroup” in this topic and tried to figure out something from sources, but I still can’t APPEND and PREPEND something to it. I use:




<?php $dateinput = $this->widget('yiiwheels.widgets.datepicker.WhDatePicker', array(

                            'attribute' => 'birthdate',

                            'model'=>$model,

                            'pluginOptions' => array(

                                'format' => 'dd.mm.yyyy',

                                'language' => 'uk',

                            ),

                            ),true);

                    ?>

<?php echo TbHtml::customActiveControlGroup($dateinput,$model,'birthdate',array('append'=>'@')); ?>

// want to append like in the sample from docs (on textFieldControlGroup):

<?php // echo $form->textFieldControlGroup($model, 'append', array('append' => '.00')); ?>



and it doesn’t append\prepend anything (ignores my htmlOptions).

I’m sure I missed something important. By the way I replaced function renderField() and used TbHtml (as was mentioned) but that didn’t helped me too :(

Ok, no one answered - I found workaround more like "hack" solution.

It seems like there’s no functionality to pass parameters of Append\Prepend to customControlGroup, so had to change couple of lines in sources. Also I’ve added beautiful Font Awesome (like in BootStrap3) vector icons (can be downloaded and copied to css and font folders) support in all YiiStrap.

To those who are interested - it’s not so elegant but worked for me:

to TbActiveForm as was mentioned in topic - added function




public function customControlGroup($input, $model, $attribute, $htmlOptions = array())

    {


        $htmlOptions = $this->processControlGroupOptions($model, $attribute, $htmlOptions);

        //echo Yii::trace(CVarDumper::dumpAsString($htmlOptions),'vardump');

        return TbHtml::customActiveControlGroup($input, $model, $attribute, $htmlOptions);

    }



TbHtml.php, starting from 2101 line add:




        $myappend_icon=TbArray::popValue('myappend_icon', $htmlOptions);

        if (isset($htmlOptions['input']) && !empty($myappend_icon))

         {

           $input='<div class="input-append">'.$input.'<span class="add-on"><i class="'.$myappend_icon.'"></i></span></div>';

         }



and replace 2728 line:




            if ((strpos($icon, 'icon') === false) && (strpos($icon, 'fa fa-') === false)) {



thats all - so you can call this nice code:




<?php $dateinput = $this->widget('yiiwheels.widgets.datepicker.WhDatePicker', array(

                            'attribute' => 'date_fired',

                            'model'=>$model,

                            'pluginOptions' => array(

                                'format' => 'dd.mm.yyyy',

                                'language' => 'uk',

                            ),

                            ),true);

                    ?>

            <?php echo $form->customControlGroup($dateinput,$model,'date_fired',array('myappend_icon'=>'fa fa-calendar')); ?>



and I’ve also edited my bootstrap Gii code generator a bit to generate “date”-fields automatically in forms when CRUD generation is performed - now it’s much easier and pleasant to develop ;)

Could someone please help me out with something which is really frustrating me:

With the code below the datepicker popup refuses to appear. In Firebug I noticed that the css and js files for the datepicker are not being included.

I have used "dateFieldControlGroup" and then, as shown in this post, I used "customActiveControlGroup", but for neither does the popup appear.

Below the "customActiveControlGroup" there is a portion of code commented out. When I do include this bit of code, then the popup appears, and I notice in Firebug that the relevant WhDateTimePicker js and css is included…

Here is the code of the form:




<?php

/* @var $this MemberController */

/* @var $model Member */

/* @var $form TbActiveForm */

?>


<div class="form">


	<?php $form=$this->beginWidget('bootstrap.widgets.TbActiveForm', array(

	'id'=>'member-form',

	// Please note: When you enable ajax validation, make sure the corresponding

	// controller action is handling ajax validation correctly.

	// There is a call to performAjaxValidation() commented in generated controller code.

	// See class documentation of CActiveForm for details on this.

	'enableAjaxValidation'=>false,

	//'layout'=>'horizontal',

)); ?>


	<p class="help-block">Fields with <span class="required">*</span> are required.</p>


	<?php echo $form->errorSummary($model); ?>


        	<?php echo $form->textFieldControlGroup($model,'Name',array('span'=>5,'maxlength'=>64)); ?>


        	<?php echo $form->textFieldControlGroup($model,'Surname',array('span'=>5,'maxlength'=>64)); ?>


        	<?php echo $form->emailFieldControlGroup($model,'Email',array('span'=>5)); ?>


        	<?php echo $form->textFieldControlGroup($model,'Phone',array('span'=>3)); ?>


        	<?php echo $form->dateFieldControlGroup($model,'FromDate',array('span'=>2)); ?>


        	<?php //echo $form->dateFieldControlGroup($model,'ToDate',array('span'=>2)); ?>


        	<?php $datePicker = $this->widget('yiiwheels.widgets.datetimepicker.WhDateTimePicker', array(

            	'model' => $model,

            	'attribute' => 'ToDate',

            	'pluginOptions' => array(

                	'format' => 'dd.mm.yyyy',

                	'language' => 'uk',

            	),

        	), true); ?>


        	<?php echo TbHtml::customActiveControlGroup($datePicker, $model, 'ToDate'); ?>


<?php /*

        	<div class="control-group ">

            	<label class="control-label" for="Member_ToDate">To</label>

            	<div class="controls">

                	<?php

                	$this->widget('yiiwheels.widgets.datepicker.WhDatePicker',

                    	array(

                        	'model' 	=> $model,

                        	'attribute' => 'ToDate',

                        	'pluginOptions' => array(

                            	'format' => 'yyyy-mm-dd'

                        	)

                    	)

                	);

                	?>

            	</div>

        	</div>

*/?>


    	<div class="form-actions">

    	<?php echo TbHtml::submitButton($model->isNewRecord ? 'Create' : 'Save',array(

			'color'=>TbHtml::BUTTON_COLOR_PRIMARY,

			'size'=>TbHtml::BUTTON_SIZE_LARGE,

		)); ?>

	</div>


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


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



I probably should add my main.php config file ( I use “setPathOfAlias” instead of the ‘aliases’ array because otherwise Yiistrap and Yiiwheels does not work at all):




...


// uncomment the following to define a path alias

// Yii::setPathOfAlias('local','path/to/local-folder');

Yii::setPathOfAlias('bootstrap', dirname(__FILE__).'/../extensions/bootstrap');

Yii::setPathOfAlias('yiiwheels', dirname(__FILE__).'/../extensions/yiiwheels');


// This is the main Web application configuration. Any writable

// CWebApplication properties can be configured here.

return array(

	'basePath'=>dirname(__FILE__).DIRECTORY_SEPARATOR.'..',

	'name'=>'Auroville Entry Service',

	/*

	'aliases' => array(

    	// yiistrap configuration

    	'bootstrap' => realpath(__DIR__ . '/../extensions/bootstrap'), // change if necessary

    	// yiiwheels configuration

    	'yiiwheels' => realpath(__DIR__ . '/../extensions/yiiwheels'), // change if necessary

	),

	*/

...