Herramientas de usuario

Herramientas del sitio


cursos:yii:views:tactiveform

TActiveForm

Si queremos utilizar extensiones en los formularios para campos autocomplete, fechas, editores HTML, etc, en lugar de repetir las lineas de código para esos campos especiales en cada atributo del formulario, es más práctico crear una clase derivada de CActiveForm e incluir en ella estos widgets. De esta forma, nuestros formularios quedarán más simples, y un cambio en esa clase derivada se transmitirá a todos los formularios.

En la carpeta protected/components crearemos el fichero TActiveForm.php:

class TActiveForm extends CActiveForm {
 
	/**
	 * Crea un campo autocomplete.
	 * @param type $model
	 * @param type $attribute
	 * @param type $otps
	 * @param type $lookupaction Acción donde se buscan los datos. Por defecto es
	 *  CLASE/lookup($term)  donde clase es el controlador que se llama igual que la clase del modelo.
	 *   Autores->  autores/lookup&term=antonio
	 */
	function autocomplete($model,$attribute,$options=array(),$lookupaction=""){
 
		$reldata=$this->findrelation($model,$attribute); // Busca la relación asociada al atributo
 
		CHtml::resolveNameID($model,$attribute,$opt);
		$idinput=$opt['id'];
 
		$opts=array_merge(array(
			'showAnim'=>'fold','minLength'=>2,
			'focus'=>"js:function(){\$('#$attribute').select();}", //TODO No va
			),$options);
 
		// El campo oculto es el que guarda el valor a enviar (id)
		echo CHtml::activeHiddenField($model,$attribute);
		if(!isset($opts['select']))
			$opts['select']="js:function(event, ui){\$('#$idinput').val(ui.item.id);}";
 
		$this->widget('zii.widgets.jui.CJuiAutoComplete', array(
			'name'=>$attribute,
			'value'=>$model->{$reldata->name},
			'sourceUrl'=>Yii::app()->createUrl("{$reldata->className}/lookup"),
			//'source'=>array('ac1', 'ac2', 'ac3'),
			'options'=>$opts,
			'htmlOptions'=>array('size'=>'40'),
		));
	}
	/** Devuelve la relación con foreignkey=attribute
	 *
	 * @param type $model
	 * @param type $attribute
	 * @return type
	*/
	private function findrelation($model,$attribute){
		if(!method_exists($model,'getMetadata')) return null;
 
		foreach($model->getMetaData()->relations as $relation=>$reldata)
				if($reldata->foreignKey==$attribute) return $reldata;
		return null;
	}
 
	/**
	 * Muestra un campo con selector de fecha
	 * @param unknown_type $model
	 * @param unknown_type $attribute
	 * @return unknown_type
	 */
	public function DateField($model,$attribute,$htmlOptions=array(),$calopts=array()){
 
		$name=get_class($model)."[".$attribute."]";
 
		yii::app()->controller->widget('zii.widgets.jui.CJuiDatePicker',array(
			'name'=>$name,
			'language'=>'es',
			//'themeUrl'=>'css/themes',
			//'theme'=>'base',
			'value'=>$model->$attribute,
			'options'=>array_merge(array('showAnim'=>'show','numberOfMonths'=>2,'changeMonth'=>true,'changeYear'=>true,
				'dateFormat'=>'dd/mm/yy','firstDay'=>1,'showOn'=> "button"),$calopts
			),
			'htmlOptions'=>array_merge(array('size'=>'10'),$htmlOptions)
		   )
		);
	}
	/**
	 *
	 */
	public function EditorField($model,$attribute,$opts=array()){
	    $options=array_merge(array('width'=>'600','height'=>250,'useCSS'=>true),$opts);
	    $this->widget('application.extensions.cleditor.ECLEditor', array(
			'model'=>$model,
			'attribute'=>$attribute, //Model attribute name. Nome do atributo do modelo.
			'options'=>$options,
			'value'=>$model->$attribute
	    ));
	}
}

Así, en las vistas, sustituiremos CActiveForm por TActiveForm:

 $form=$this->beginWidget('TActiveForm', array(
	'id'=>'usuarios-form',
	'enableAjaxValidation'=>false));
  echo $form->dateField($model,'fecha_alta');

Campos Autocomplete

Cuando un atributo está relacionado con otro modelo del que existen muchos posibles valores, el desplegable no es una buena opción, porque es incómodo seleccionar un valor de una lista muy larga. En estos casos podemos utilizar el widget CJuiAutocomplete de jQueryUI. Utilizaremos un campo oculto que guarda el valor real a utilizar (la id), y mostraremos un campo con el texto asociado a ese valor.

Para mayor facilidad de uso, utilizaremos un formulario TActiveForm . Si, por ejemplo, tenemos el modelo Entradas que tiene un atributo usuarios_id que conecta con el modelo Usuarios, los pasos a realizar son:

1º Crear una acción lookup en el controlador Usuarios, que nos devolverá datos de usuarios en formato JSON segun un filtro de entrada:

class EntradasController extends Controller
	public function actionLookup($term) {
 
	    $criteria = new CDbCriteria;
	    $criteria->condition = 'nombre like :q '; // campo o campos por los que se filtra
	    $criteria->params = array(':q' => '%'.trim($term) . '%');
	    $criteria->order = 'nombre'; // criterio de ordenación
	    $criteria->limit = 100; // Máximo número de valores devueltos
 
	    $models=Usuarios::model()->findAll($criteria);
            if (!empty($models)) {
              $out = array();
              foreach ($models as $model) {
                $out[] = array(
                    'label' => $model->nombre,  // Texto a mostrar en el desplegable
                    'value' => $model->nombre, // valor
                    'id' => $model->id, // Valor que devuelve el autocomplete
                );
              }
              echo CJSON::encode($out);
              Yii::app()->end();
           } 
       }
 
}

(Recuerda dar permisos a esta acción para los usuarios que la vayan a utilizar)

2º En la vista del formulario, utiliza TActiveForm en lugar de CActiveForm

3º En el campo autor sustituye textfield por autocomplete:

  echo $form->autocomplete($model,'usuarios_id');