Yii Booster - Dropdownlistgroup - Ajax

Ciao a tutti, nella sezione "Profilo" dei miei utenti devo far inserire i seguenti campi:

  • Stato

  • Regione

  • Provincia

  • Comune

Nel DB ho una tabella chiamata "stati", una "regione", etc… e nella tabella "profili" ho un campo per ognuno dove memorizzo il loro Id.

Ho creato una DropDownListGroup usando Yii Booster di questo tipo:


       $form = $this->beginWidget(

                                    'booster.widgets.TbActiveForm',

                                    array(

                                            'id' => 'horizontalForm',

                                            'type' => 'horizontal',

                                            'enableAjaxValidation'=>true,

                                            'action' => Yii::app()->createUrl('/profile/profile/update'),

                                        )

                                ); 


// STATO

           // retrieve the models from db

            $models = Stati::model()->findAll(

                                                    array('order' => 'nome__stati')

                                                );


            // format models as $key=>$value with listData

            $list = CHtml::listData($models,  'id', 'nome__stati');


            echo $form->dropDownListGroup(

                                            $profile,

                                            'stato',

                                            array(

                                                    'wrapperHtmlOptions' => array(

                                                                                    'class' => 'col-sm-5',

                                                                                    'style' => 'width:300px',

                                                                                ),

                                                    'widgetOptions' => array(

                                                                                //'data' => array('Something ...', '1', '2', '3', '4', '5'),

                                                                                'data' => $list,

                                                                                'htmlOptions' => array(),

                                                                            ),

                                                

                                                ),

                                            array(

                                                    

                                                    'ajax' => array(

                                                                    'type'=>'POST', //request type

                                                                    'url'=>CController::createUrl('/ajax/dynamicregion'), //url to call.

                                                                    //Style: CController::createUrl('currentController/methodToCall')

                                                                    'update'=>'#YumProfile_regione', //selector to update

                                                                    //'data'=>'js:javascript statement' 

                                                                    //leave out the data key to pass all form values through

                                                                ),    

                                                        )

                                        ); 


// REGIONE

            echo $form->dropDownListGroup(

                                            $profile,

                                            'regione',

                                            array()

                                        );

            






Poi ho fatto un AjaxController.php con questo codice





public function actionDynamicregion()

{

    $data=Regioni::model()->findAll('id_stato=:id_stato', array(':id_stato'=>(int) $_POST['YumProfile']['stato']));

    $data=Regioni::model()->findAll();

    $data=CHtml::listData($data,'id','regione');

    foreach($data as $value=>$name)

    {

        echo CHtml::tag('option',

                   array('value'=>$value),CHtml::encode($name),true);

    }

}



Facendo un po’ di copia-incolla con le soluzioni che trovavo su internet qua e là… Non ho capito bene in che punto va inserita la parte ajax nella dichiarazione della DropDownListGroup, non ho trovato 1 solo esempio. Ho fatto un po’ di prove senza successo.

L’unica cosa che funziona è che scelgo lo stato, viene memorizzato nel DB correttamente, ma non funziona la successiva chiamata al controller Ajax che dovrebbe popolarmi la seconda DropDown con le regioni.

Qualche idea o consiglio?

Grazie 1.000

mi rispondo da solo… continunando a googlare ho finalmente capito che:

-Yii bootstrap: non è più supportato

-Yii booster: creato sulla base di bootstrap non è più supportato

Adesso c’è una nuova estensione che si chiama Yiistrap nata dalla fusione dei 2 precedenti, per cui essendo all’inizio del mio progetto, faccio un roll-back e riparto con YiiStrap

Se anche voi usate YiiStrap e avete già affrontato una soluzione simile per Regione-Provincia-Città mi fate sapere?

grazie di nuovo

Ciao, potresti fare qualcosa di simile:


            <?php echo TbHtml::dropDownList('uff2',

                            $uff2,

                            CHTML::listData(UfficioLiv2::model()->getUfficiLivello2(),'id','denominazione'),

                            array(

                                'prompt' => 'Seleziona Ufficio',

                                'ajax' => array(

                                    'type'=>'POST', //request type

                                    'dataType' => 'json',

                                    'url'=>CController::createUrl('ufficioLiv4/popolaUffici3Liv'), //url to call.

                                    'data' => array('coduff2' => 'js:$("#uff2").val()'),

                                    'success' => 'function(response){

                                            if (response.error == "false")

                                            {

                                                $("#'.CHTML::activeId($model,'id_ufficio_liv3').'").empty();

                                                $("#'.CHTML::activeId($model,'id_ufficio_liv3').'").append($("<option>").text("Seleziona Ufficio").attr("value","0"));

                                                var elenco = response.uffici;

                                                $.each(elenco, function(i, value) {

                                                    $("#'.CHTML::activeId($model,'id_ufficio_liv3').'").append($("<option>").text(value.denominazione).attr("value",value.id));

                                                });

                                            }

                                            else

                                            {

                                                $("#'.CHTML::activeId($model,'id_ufficio_liv3').'").empty();

                                                $("#'.CHTML::activeId($model,'id_ufficio_liv3').'").append($("<option>").text("Seleziona Ufficio").attr("value","0"));

                                                console.log(response.status);

                                            }

                                        }'

                            )));  ?>

                

            </div></div>                


            <?php

            

                if($model->isNewRecord) {

            

                    echo $form->dropDownListControlGroup($model,

                                                         'id_ufficio_liv3',

                                                         array(),

                                                         array('prompt' => 'Seleziona Ufficio',

                                                               ));

                    

                } else {

                    

                    echo $form->dropDownListControlGroup($model,

                                                         'id_ufficio_liv3',

                                                         CHTML::listData(UfficioLiv3::model()->getUfficiLivello3($uff2),'id','denominazione'),

                                                         array('prompt' => 'Seleziona Ufficio',

                                                               ));                    

                    

                }

            

            ?>

Nel action ajax puoi inserire:


        public function actionPopolaUffici3Liv()

        {

            $coduff2 = Yii::app()->request->getParam('coduff2');

            if (Yii::app()->request->isAjaxRequest) {

                if ($coduff2 == '') {

                    echo CJSON::encode(array(

                        'error' => 'true',

                        'status' => 'Richiesta non valida.'

                    ));

                    Yii::app()->end();

                } else {   

                    $sql = "SELECT id, denominazione FROM ufficio_liv3 WHERE id_ufficio_liv2 = :coduff2";

                    $cmd = Yii::app()->db->createCommand($sql);

                    $cmd->bindParam(':coduff2', $coduff2, PDO::PARAM_INT);

                    $result = $cmd->queryAll();

                    $sql2 = $cmd->text;

                    if($result) {

                        echo CJSON::encode(array(

                                                'error' => 'false',

                                                'status' => 'Ufficio esistente',

                                                'uffici' => $result

                                           )                                

                             );

                        Yii::app()->end();

                    } else {

                        echo CJSON::encode(array(

                            'error' => 'true',

                            'status' => 'Questo Ufficio ' . strval($coduff2) . ' non ha uffici dipendenti.'

                        ));

                        Yii::app()->end();

                    }

                }

                

            }

            

        }

Grazie 1.000, gentilissimo e soprattutto utilissimo, proprio quel che mi serviva per la mia implementazione.

ciao Lerstat, dopo aver disinstallato yii-boostrap e yii-booster e installato yii-strap e riadattato le cose + semplici (ci son sempre imprevisti che richiedono + tempo), sono ritornato alla questione Stato-Regione-Province originaria.

Volevo chiederti se mi puoi dare qualche consiglio (o chiunque legga qui) su come debuggare questa fase.

Io riesco a popolare la DropDownList con gli stati presi dal mio DB, ma selezionandone uno o l’altro, non succede nulla alla dropdownlist successiva (quella delle regioni), che in pratica rimane sempre vuota.

Questo il codice della View:




// STATO


 echo TbHtml::dropDownList('stato',

                            $profile,

                            CHtml::listData(Stati::model()->findAll(array('order' => 'nome__stati')),  'id', 'nome__stati'),

                            array(

                                //'prompt' => 'Seleziona Categoria',

                                'ajax' => array(

                                    'type'=>'POST', //request type

                                    'dataType' => 'json',

                                    'url'=>CController::createUrl('/ajax/dynamicRegion'), //url to call.

                                    'data' => array('id_stato' => 'js:$("#YumProfile_stato").val()'),

                                    'success' => 'function(response){

                                            if (response.error == "false")

                                            {

                                                $("#'.CHTML::activeId($profile,'regione').'").empty();

                                                $("#'.CHTML::activeId($profile,'regione').'").append($("<option>").text("Seleziona Regione").attr("value","0"));

                                                var elenco = response.regioni;

                                                $.each(elenco, function(i, value) {

                                                    $("#'.CHTML::activeId($profile,'regione').'").append($("<option>").text(value.regione).attr("value",value.id));

                                                });

                                            }

                                            else

                                            {

                                                $("#'.CHTML::activeId($profile,'regione').'").empty();

                                                $("#'.CHTML::activeId($profile,'regione').'").append($("<option>").text("Seleziona Regione").attr("value","0"));

                                                console.log(response.status);

                                            }

                                            alert("ciao");

                                        }'

                                )

                            )

           );


// REGIONE

            echo $form->dropDownListControlGroup($profile, 'regione', array(), array('style' => 'width:300px')); 




e questo il mio AjaxController.php





        public function actionDynamicRegion()

        {

            $id_stato = Yii::app()->request->getParam('id_stato');

            if (Yii::app()->request->isAjaxRequest) {

                if ($id_stato == '') {

                    echo CJSON::encode(array(

                        'error' => 'true',

                        'status' => 'Richiesta non valida.'

                    ));

                    Yii::app()->end();

                } else {   

                    $sql = "SELECT id, regione FROM regioni WHERE id_stato = :idstato";

                    $cmd = Yii::app()->db->createCommand($sql);

                    $cmd->bindParam(':idstato', $id_stato, PDO::PARAM_INT);

                    $result = $cmd->queryAll();

                    $sql2 = $cmd->text;

                    if($result) {

                        echo CJSON::encode(array(

                                                'error' => 'false',

                                                'status' => 'Regione esistente',

                                                'regioni' => $result

                                           )                                

                             );

                        Yii::app()->end();

                    } else {

                        echo CJSON::encode(array(

                            'error' => 'true',

                            'status' => 'Questo Stato ' . strval($id_stato) . ' non ha regioni.'

                        ));

                        Yii::app()->end();

                    }

                }

                

            }

            

        }



Vorrei capire, visto che è la prima volta che utilizzo qui ajax, se viene lanciata la chiamataa, se non parte nemmeno, se si blocca nel controller, etc… ma essendo nuovo di MVC e soprattutto di Yii mi muovo ancora lentamente.

Con FireBug son riuscito a capire solo questo per ora:




URL richiesta: 	http://localhost/MyProject/index.php?r=ajax/dynamicRegion

Metodo di richiesta: 	POST

Codice di stato: 	HTTP/1.1 200 OK




Utilizzando la funzione:




Yii::log("test");            



ed analizzando il file application.log che si trova dentro \protected\runtime ho visto che dopo aver selezionato lo Stato, viene chiamata regolarmente la funzione dentro il mio AjaxCotroller. Viene eseguita correttamente la query e $result viene correttamente valorizzata.

Dunque il problema è ora capire come debuggare la fase di "ritorno" dei dati dallo script Ajax alla pagina che lo ha lanciato.

Grazie in anticipo

Nell’esempio di codice che ti avevo dato, la prima dropdownlist è sganciata dal model, quindi se vuoi inviare al controller il valore selezionato, in questo caso devi usare


array('id_stato' => 'js:$("#stato").val()')

e non


array('id_stato' => 'js:$("#YumProfile_stato").val()')

per il debug io uso Firebug e/o Alert…

hai detto che il controller ajax viene eseguito, quindi presumo che i permessi sul controller sono ok…

il problema è la risposta json del controller che dovresti controllare tramite firebug.

controlla la risposta nella console…

ah… non ho a disposizione il mio ambiente di sviluppo, quindi non posso controllare…

ciao grazie dell’aiuto, per adesso ho capito che il dato di ritorno dal controller mi sembra OK:




{"error":"false","status":"Regione esistente","regioni":[{"id":"1","regione":"PIEMONTE"},{"id":"2","regione":"VALLE D'AOSTA\/VALL?E D'AOSTE"},{"id":"3","regione":"LOMBARDIA"},{"id":"4","regione":"TRENTINO-ALTO ADIGE"},{"id":"5","regione":"VENETO"},{"id":"6","regione":"FRIULI-VENEZIA GIULIA"},{"id":"7","regione":"LIGURIA"},{"id":"8","regione":"EMILIA-ROMAGNA"},{"id":"9","regione":"TOSCANA"},{"id":"10","regione":"UMBRIA"},{"id":"11","regione":"MARCHE"},{"id":"12","regione":"LAZIO"},{"id":"13","regione":"ABRUZZO"},{"id":"14","regione":"MOLISE"},{"id":"15","regione":"CAMPANIA"},{"id":"16","regione":"PUGLIA"},{"id":"17","regione":"BASILICATA"},{"id":"18","regione":"CALABRIA"},{"id":"19","regione":"SICILIA"},{"id":"20","regione":"SARDEGNA"}]}<script language="javascript" type="text/javascript">

/*<![CDATA[*/

if(typeof(console)=='object')

{

	console.group("Application Log");

}

/*]]>*/

</script>



però ho scoperto che l’evento Ajax che viene lanciato non è ‘success’ bensì ‘ERROR’, e sono riuscito a scoprirlo mettendo banalmente i seguenti parametri Ajax:




                                   'success' => 'function(){alert("successo");} ',

                                   'error' => 'function(){alert("errore");} ',

                                   'complete' => 'function(){alert("completato");} ',

                                   'beforeSend' => 'function(){alert("Prima di Inviare");} ',




Ora, visto che ho il messaggio di errore devo capire cos’è che non va…

EDIT: googlando ho trovato questo metodo, che applicandolo sembra aiutarmi a capire l’errore:




'error' => "function (event, jqXHR, ajaxSettings, thrownError) {alert('[event:' + event + '], [jqXHR:' + jqXHR + '], [ajaxSettings:' + ajaxSettings + '], [thrownError:' + thrownError + '])');  }",



Ed ecco l’errore incriminato:




[event:[object Object]], [jqXHR:parsererror], [ajaxSettings:SyntaxError: JSON.parse: unexpected non-whitespace character after JSON data at line 1 column 747 of the JSON data], [thrownError:undefined])



il problema sembra che il mio JSON in uscita dal controller, in coda all’array con i dati, ci rimette un JavaScript che a quanto pare è inaspettato…anzi a dire il vero non ho idea di chi accodi tale codice…

Sto vedendo che altri hanno problemi simili e come me utilizzano questa View, chiamata con RenderPartial da un’altra view, ma per adesso c’ho capito poco.

Mah… il codice mi sembra ok… e nella mia applicazione funziona perfettamente.

ci sfugge sicuramente qualcosa.

non avevo visto la tua risposta Lerstat per cui avevo aggiunto la sezione EDIT nel mio post precedente. Penso che il problema sia che per qualche motivo, la risposta Ajax che genera la mia applicazione, oltre che ai dati che vogliamo noi, aggiunge anche questo script, che a quanto pare è inaspettato (o quantomeno dovrebbe aggiungere uno SPAZIO se ho ben interpretato l’errore che ricevo)

Questo è il comando della mia View principale con cui chiamo le varie sub-view





    $this->widget('bootstrap.widgets.TbTabs', array(

                                                    'tabs' => array(

                                                                    array(

                                                                            'label' => 'Categoria', 

                                                                            'content' => $this->renderPartial('application.views.profile._categoria', array('profile' => $profile),$this,false), 

                                                                            'active' => true

                                                                        ),

                                                                    array(

                                                                            'label' => 'Anagrafica', 

                                                                            'content' => $this->renderPartial('application.views.profile._anagrafica', array('profile' => $profile),$this,false), 

                                                                            'active' => FALSE

                                                                        ),

                                                        

                                                                ),

                                                )

        );

                                                                




ma il terzo parametro delle renderpartial? $this?

mi serve perchè altrimenti la renderpartial stampa prima l’echo interno e poi l’html interno di fatto incasinandomi la pagina.

Nel particolare io ho una una Tab nella view principale con 3 tab ognuna lancia una renderpartial. Se non metto questo parametro, il content della renderpartial mi va fuori dalla rispettiva tab all’inizio della pagina.

Ad ogni modo dopo aver studiato praticamente tutta la notte… HO TROVATO IL PROBLEMA!

Si tratta di questa istruzione che avevo messo nella sezione LOG del mio main.php nel config:




                                

				array(

					'class'=>'CWebLogRoute',

                                        	'showInFireBug'=>true,

                                                'ignoreAjaxInFireBug'=>false,

				),

                                  

 



Questo codice l’avevo trovato ed inserito cercando un modo per debuggare la parte Ajax, e forse ora che ci guardo l’avevo messo pure nel posto sbagliato (sotto Log->Routes anzichè solo sottoLog).

Aggiungeva in automatico il tag CDATA in coda a qualsiasi cosa nella costruzione di una pagina, quindi anche nella risposta ajax, creando un errore di formattazione.

Ad ogni modo tutta questa storia mi ha insegnato a conoscere MOLTO Dettagliatamente tutta la procedura Ajax all’interno di Yii e come debuggarla con FireBug.

Grazie dell’aiuto!

Bene cosí.