Load page in two steps using Ajax

Hello world, I’m migrating a Yii1 application to Yii2.
For a specific controller loading data is easily too long, so I want to do this:

  1. the “view” action call the view that render just the skeleton of the page with a waiting message;
  2. the view display page and register a js code that calls “viewajax” action;
  3. the “viewajax” action load data then call the view to display them substituting the waiting message.

The following code worked with Yii1.

Controller

public function actionView($id) {
    $sintesi = Sintesi::findOne($id);
    if ($this->ajaxAttivo()) {
       return $this->render('view', [
                   'corrente' => $sintesi,
                   'chiamaAjax' => true,
       ]);
  }
  $sintesi->elabora();
   $dataProvider = new ArrayDataProvider([
       'allModels' => $sintesi->risultato->tabella,
       'pagination' => false,
   ]);
   return $this->render('view', [
               'corrente' => $sintesi,
               'chiamaAjax' => false,
               'searchModel' => new SintesiSearch(),
               'dataProvider' => $dataProvider,
   ]);
}
public function actionViewajax($id) {
   $sintesi = Sintesi::findOne($id);
   $sintesi->elabora();
   $dataProvider = new ArrayDataProvider([
       'allModels' => $sintesi->risultato->tabella,
       'pagination' => false,
   ]);
   return $this->renderAjax('view', [
               'corrente' => $sintesi,
               'chiamaAjax' => false,
               'searchModel' => new SintesiSearch(),
               'dataProvider' => $dataProvider,
   ]);

}

View

if ($chiamaAjax) {
    $ajax = GenLib::codiceAjaxView('tabdati', ['sintesi/viewajax', 'id' => $corrente->id);
    $this->registerJs($ajax, \yii\web\View::POS_LOAD);
    echo Html::beginTag('div', ['id' => 'tabdati']);
    echo 'Caricamento dati... Attendere';
    echo Html::endTag('div');
 } else {
    $colonne = $corrente->risultato->colonne;
    echo DynaGrid::widget([
       'options' => ['id' => 'lst-' . $searchModel->tableName()],
       'theme' => 'panel-info',
       'gridOptions' => [
       'dataProvider' => $dataProvider,
       'filterModel' => null,
       'pjax' => false,
       'panel' => [
           'heading' => '<p class="panel-title">' . $corrente->indicatore->descrizione . '</p>',
       ],
       'panelHeadingTemplate' => '{title}<div class="clearfix"></div>',
       'toolbar' => false,
       'hover' => true,
       'floatHeader' => Yii::$app->user->isJsActive,
       'responsiveWrap' => false,
   ],
   'columns' => $colonne,
]);
}

GenLib

public static function codiceAjaxView($destinazione, $indirizzo) {
   $codice = "
       jQuery.ajax({
           type: 'POST',
           dataType: 'html',
           url: '" . Url::to($indirizzo) . "',
           success: function(data){
               document.getElementById('" . $destinazione . "').innerHTML = data;
           },
           error: function(){
               alert('Errore Ajax');
           }
       });
   ";
   return $codice;
}

Step 1 and 2 correctly runs but the js code return “errore”. I’m not so able with js and Ajax; can someone help me? Thanks

Hi,

Open your browser inspector, and see what’s the URL being called using ajax.
Does it seem correct ? What is it outputting ?

The link is correct. This is the js code generated in the page:

<script>jQuery(window).on('load', function () {
           jQuery.ajax({
               type: 'POST',
               dataType: 'html',
               url: '/mia2/web/index.php/sintesi/viewajax?id=70',
               success: function(data){
                   document.getElementById('tabdati').innerHTML = data;
               },
               error: function(){
                   alert('Errore Ajax');
               }
           });
   });</script>

In the log I find a “400 - Bad Request” error.

If it’s showing the “Errore Ajax” alert, then the URL called by Ajax is returning an Error code.
See it’s details in your browser developer tools.

Like this for example:44%20PM

Here, the Status Code is “200”, and it’s okay.

What does it give for your URL?

In the log I find a “400 - Bad Request” error.

We’re making progress. This error can sometimes mean that you’re using the wrong HTTP method.

Your ajax call is doing a POST. Did you configure your controller to accept POST for that action?

    /**
     * @inheritdoc
     */
    public function behaviors()
    {
        return [
            'verbs' => [
                'class' => \yii\filters\VerbFilter::class,
                'actions' => [
                    'viewajax' => ['post'],
                ],
            ],
        ];
    }

The verb filter was not configured in the behaviors. But the code you suggest doesn’t solve the problem. Same error 400.
Curiosly the specific message (sorry, I didn’t see it before) of the error is “Unable to verify your data submission.” But there is no data submission.

Check this out: https://www.yiiframework.com/doc/guide/2.0/en/security-best-practices#avoiding-csrf

1 Like

Great!
Setting “$enableCsrfValidation = false” it works.
Now the question is: why does Yii identity this as a CSRF request? I’m working on my local (and closed) dev system.

Yii identifies this request as a CSRF request, because you didn’t pass along the CSRF token when performing the Ajax request.

Disabling the feature is a really bad practice, and you shouldn’t do it, as once your website is in production, any malicious user will be able to perform POST requests to this URL.

Warning: Disabling CSRF will allow any site to send POST requests to your site. It is important to implement extra validation such as checking an IP address or a secret token in this case.

You may consider it harmless in this particular case, but it’s a good practice to always protect your endpoints by default.

You can pass it from your ajax request by modifying it like this:

<script>jQuery(window).on('load', function () {
           jQuery.ajax({
               type: 'POST',
               dataType: 'html',
               url: '/mia2/web/index.php/sintesi/viewajax?id=70',
+              data: {_csrf: : yii.getCsrfToken()}
               success: function(data){
                   document.getElementById('tabdati').innerHTML = data;
               },
               error: function(){
                   alert('Errore Ajax');
               }
           });
   });</script>

and undoing setting $enableCsrfValidation = false which is a bad quick fix.

Check this SO entry for more details.

2 Likes

Thank you very much.
Now it runs perfectly. And sure.

1 Like