===== Formularios: ActiveForm ===== Para definir un formulario en una vista, utilizamos ActiveForm::begin() y AcriveForm::end(). Equivale a
, pero además genera un control que evita el hackeo por referencias cruzadas CSRF. $form = ActiveForm::begin(); .... Campos ---- ActiveForm::end() Por defecto, el formulario irá a la misma controlador/action en el que estemos. Si queremos enviarlo a otra acción, lo especificaremos en el begin: ActiveForm::begin(['action'=>['/entradas/cambiaestado']); Si queremos especificar otros atributos de form, como por ejemplo multipart/form-data que se utiliza cuando subimos fichero, o desactivar el autocomplete del navegador, que interfiere con nuestros autcomplete y fechas, lo pondremos en la propiedad "options" ActiveForm::begin(['options'=>[ 'enctype'=>'multipart/form-data', 'autocomplete'=>'off' ]]); ==== Desplegables: Atributos con un conjunto de valores permitidos ==== Cuando un atributo solo admite un conjunto de valores, lo típico será mostrar un desplegable. En el caso de atributos con valores fijos (ver estado en Crear/configurar Modelos), utilizaremos como opciones la propiedad ATRIBUTOOptions: use app\models\Usuarios; ... echo $form->field($model, 'estado')->dropDownList(Usuarios::$estadoOptions,['prompt'=>'Selecciona...']); ==== Atributos relacionados con otro modelo ==== Si tenemos un atributo cuyos valores están relacionados con otro modelo mediante una relación hasOne, lo típico será mostrar un desplegable con los valores posibles. Para ello, cargaremos los valores con find()->all(), que devuelve un array de objetos, y lo convertiremos a un array clave=>descripción mediante la función ArrayHelper::map(), que es lo que necesita el input dropdownList //Utilizamos asArray para que sea más óptimo el acceso, al devolver una lista de arrays $options=ArrayHelper::map(Usuarios::find()->asArray()->all(),'id','nombre'); echo $form->field($model, 'usuarios_id')->dropDownList($options,['prompt'=>'Seleccione...']); ==== Campos de fecha con selector de calendario ==== Consulta [[cursos:yii2:fechas|]] para configurar la conversión de formatos de fecha en las vistas. Una vez hecho, en un formulario lo haremos de la forma: field($model, 'fecha')->widget(DateControl::classname(),['pluginOptions' => ['autoclose'=>true]); ?> ==== Autocomplete ==== Cuando el modelo relacionado con un atributo tiene muchos valores posibles, un desplegable no es adecuado por cuestiones de rendimiento. Podemos utilizar un autocomplete. El widget [[http://demos.krajee.com/widget-details/typeahead|Typehead de Kartik]] funciona muy bien y tiene muchas opciones. Permite utilizar datos locales o pedirlos vía Ajax al servidor. Esto último será lo habitual, y como lo que necesitamos es que nos envíe con el formulario la id del modelo relacionado y no su descripción, tenemos que usar un campo oculto que se actualiza cuando seleccionamos una entrada. Si tenemos el modelo Libros y queremos asociarle un autor (autores_id) con un Typehead: use kartik\typeahead\Typeahead; $value=$model->autor ? $model->autor->nombre : ''; //Valor inicial que mostramos echo Html::activeHiddenInput($model,'autores_id',['id'=>'autor-hidden']) echo Typeahead::widget([ 'name' => autor_th', 'value'=>$value, 'options' => array_merge(['placeholder' => 'Escribe el nombre...'],$options), 'pluginOptions' => ['highlight'=>true], 'pluginEvents' => [ "typeahead:select" => 'function(ev, resp) { $("#autor-hidden").val(resp.id); $("#autor-hidden").change(); }', ], 'dataset' => [ [ 'datumTokenizer' => "Bloodhound.tokenizers.obj.whitespace('id')", 'display' => 'label', 'remote' => [ 'url' => Url::to(['autores/lookup']) . '&term=%QUERY', 'wildcard' => '%QUERY' ] ] ] ]); Es más práctico crear nuestro propio "Widget" autocomplete y así podemos utilizarlo en los formularios de una forma más sencilla: Creamos el fichero app\components\THtml.php getErrors($atributo)?'has-error':'')."'>"; echo Html::activeHiddenInput($model,$atributo,['id'=>$id.'-hidden']); echo Html::activeLabel($model,$atributo); echo Typeahead::widget([ 'name' => $atributo.'_a', 'value'=>$model->$relation, 'options' => array_merge(['placeholder' => 'Nombre, apellidos, Código...'],$options), 'pluginOptions' => ['highlight'=>true], 'pluginEvents' => [ "typeahead:select" => 'function(ev, resp) { $("#'.$id.'-hidden").val(resp.id); $("#'.$id.'-hidden").change(); }', ], 'dataset' => [ [ 'datumTokenizer' => "Bloodhound.tokenizers.obj.whitespace('id')", 'display' => 'label', // 'prefetch' => $baseUrl . '/samples/countries.json', 'remote' => [ 'url' => Url::to(array_merge($lookupaction,['term'=>'XQY'])), 'wildcard' => 'XQY' ] ] ] ]); echo Html::error($model,$atributo); echo ''; } } Así, en la vista del formulario pondremos: use app\components\THtml; Solamente nos falta crear la acción autores/lookup que se encarga de devolver en formato JSON los autores que cumplen el criterio de búsqueda: En AutoresController.php: public function actionLookup($term) { $results = []; foreach (Autores::find()->andwhere("(nombre like :q )", [':q' => '%' . $term . '%'])->asArray()->all() as $model) { $results[] = [ 'id' => $model['id'], 'label' => $model['nombre'], ]; } return \yii\helpers\Json::encode($results); } Una alternativa a THtml más "elegante" es crear una clase derivada de ActiveForm y añadirle el método autocomplete (y otros para fechas, editores o lo que queramos) . Así, en la vistas: class TActiveForm extends ActiveForm { public function autocomplete($model,$atributo,$lookupaction,$relation='',$options=[]){ ... } }