Search code examples
yii2

Yii2: Handle dropdown data to widget


I'm building a widget on Yii2 that displays charts.

I have the defining ChartsWidget class file.

class ChartsWidget extends Widget
{
    public $limit;

    public $topten;

    public $type;

    public function init()
    {
        parent::init();
        if ($this->limit === null) {
            $this->limit = 10;
        }
        $this->topten = $this->GetTopTen();
        if ($this->type === null) {
            $this->type = "column";
        }else{
            $this->type=
        }
    }

    private function GetTopTen()
    {
        $connection = Yii::$app->getDb();

        $command = $connection->createCommand("SELECT colonias.nomasen AS name, count(rv.geom) AS conteo FROM colonias LEFT JOIN rv ON ST_Contains(colonias.geom, rv.geom) WHERE colonias.nomasen!='NINGUNO'  GROUP BY  colonias.nomasen ORDER BY conteo DESC LIMIT :limit")->bindValue(':limit', $this->limit);

        $tops = $command->queryAll();

        foreach ($tops as $top) {
            $topten['names'][]=$top['name'];
            $topten['conteos'][]= $this->ToInt($top['conteo']);
        }

        return $topten;
    }

    
    private function ToInt($valor)
    {
        return (int)$valor;
    }

    public function run()
    {
        return $this->render('charts', ['topten' => $this->topten, 'limit' => $this->limit, 'type' => $this->type]);
    }
}

I have two views "type" that is an ActiveForm. I want it to be a type selector.

<?php

use yii\helpers\Html;
use yii\widgets\ActiveForm;
use kartik\select2\Select2;

?>

<div class="type-search" style="float: left; width: 48%;">

    <?php    
    
    $form = ActiveForm::begin([
        'action' => ['type'],
        'method' => 'get',
    ]); 

    $data = ["column", "bar", "pie"];

    echo '<label class="control-label">Tipo</label>';
    echo Select2::widget([
        'name' => 'state_10',
        'data' => $data,
        'options' => [
            'placeholder' => 'Selecciona un tipo'
        ],
    ]);

    ?>
    
    <div class="form-group">
        <?= Html::submitButton('Search', ['class' => 'btn btn-primary']) ?>
        <?= Html::resetButton('Reset', ['class' => 'btn btn-default']) ?>
    </div>

    <?php ActiveForm::end(); ?>

</div>

How do I pass the selected item in the dropdown list to ChartsWidget???

I tried $query = Yii::$app->request->queryParams; it doesn't work.

Obviously, ChartsWidget has no actions like any controller has.

EDIT

I am using dropdown because I want the user to select the type of chart like bars, columns, pie, etc.

I'm calling the widget in Maps view like this

<?= ChartsWidget::widget(['limit' => 12, 'type' => 'bar']) ?>

This is the view Mapsview

I can access type using 'type' => 'value' like above. But I want user to select type.

By the way, I am using Kartik/select2 widget for dropdown lists. And 2amigos chart plugin which I use to make my own widget.

DONT GET CONFUSED

This might be a little confusing. What I am looking for is. How do I pass selected drop-down data in the active form to ChartsWidget class. And how to get that data in ChartsWidget class.

I hope you can help. Thanks.


Solution

  • Looks like you need to use the javascript at this part to change the chart you should use the options and methods for the chart.js to change it on runtime you can use .update() to re-draw the chart along with the data if it needs to be different too.

    Here is the link to the section.

    A simple example to draw a chart is like below

    var myLineChart = new Chart(ctx, {
        type: 'line',
        data: data,
        options: options
    });
    

    now if I want to change the chart type to bar from line i can update the option with

    myLineChart.config.type='bar';
    myLineChart.update();
    

    The above 2 lines need to be bound to the button or in your case the select2

    A simple example to change the chart with a button click in native HTML and javascript is below

    $(document).ready(function() {
      var ctx = document.getElementById("myChart");
      var myChart = new Chart(ctx, {
        type: 'bar',
        data: {
          labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
          datasets: [{
            label: '# of Votes',
            data: [12, 19, 3, 5, 2, 3],
            backgroundColor: [
              'rgba(255, 99, 132, 0.2)',
              'rgba(54, 162, 235, 0.2)',
              'rgba(255, 206, 86, 0.2)',
              'rgba(75, 192, 192, 0.2)',
              'rgba(153, 102, 255, 0.2)',
              'rgba(255, 159, 64, 0.2)'
            ],
            borderColor: [
              'rgba(255,99,132,1)',
              'rgba(54, 162, 235, 1)',
              'rgba(255, 206, 86, 1)',
              'rgba(75, 192, 192, 1)',
              'rgba(153, 102, 255, 1)',
              'rgba(255, 159, 64, 1)'
            ],
            borderWidth: 1
          }]
        },
        options: {
          scales: {
            yAxes: [{
              ticks: {
                beginAtZero: true
              }
            }]
          }
        }
      });
    
      $("#change-bar").on('click', function() {
        myChart.config.type = 'line';
        myChart.update();
      });
    
    })
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.min.js"></script>
    <button id="change-bar">change graph</button>
    <canvas id="myChart" width="400" height="400"></canvas>

    As you want to change the chart by using kartik/select2 you can use the event under the pluginEvents

    "select2:select" => "function() {  }",
    

    Your select2 should look like below

    echo Select2::widget([
            'name' => 'state_10',
            'data' => $data,
            'options' => [
                'placeholder' => 'Selecciona un tipo'
            ],
            'pluginEvents'=>[
                "select2:select" => "function(e) { 
                  myLineChart.config.type=$(this).val();
                  myLineChart.update(); }",
             ]
        ]);
    

    The above requires that the variable myLineChart to be available where you are trying to populate the select dropdown.

    Ideally, you should initialize the code related to the select2 inside the widget as a part of it or pass the id of the select element to your widget and bind the change event inside your widget where you register your scripts.


    EDIT

    Another solution could be appending the selected value from the drop-down to the URL as query string parameter and reload the page with the new URL, and then assigning it to the class option $type inside the init() using Yii::$app->request->queryParams, and pass it to your view file where you are calling the chart widget like below

    <?= ChartsWidget::widget(['limit' => 12, 'type' => $type]) ?>
    

    your view where you are populating the code will look like below in that case

    $data = ["column", "bar", "pie"];    
    $baseUrl=\yii\helpers\Url::current(['type'=>null]);
    
    echo \kartik\widgets\Select2::widget([
            'name' => 'state_10',
            'data' => $data,
            'options' => [
                'placeholder' => 'Selecciona un tipo'
            ],
            'pluginEvents'=>[
                "select2:select" => "function(e) { location='$baseUrl?type='+$(this).select2(\"data\")[0].text}",
             ]
        ]);
    

    In your ChartWidget class inside the init() function add the following

    if(isset(Yii::$app->request->queryParams['type'])){
          $this->type=Yii::$app->request->queryParams['type'];
    }
    

    and rather than checking for the null value in $type set the default type when you declare the class attribute change it to

    public $type='column';
        
    

    Your full code for the class will look like below

    namespace app\componets\charts;
    use Yii;
    use yii\base\Widget;
    class ChartsWidget extends Widget {
    
        public $limit;
        public $topten;
        public $type = 'column';
    
        public function init() {
            parent::init ();
            if ( $this->limit === null ) {
                $this->limit = 10;
            }
            
            $this->topten = $this->GetTopTen ();
            
            if ( isset ( Yii::$app->request->queryParams['type'] ) ) {
                $this->type = Yii::$app->request->queryParams['type'];
            }
        }
    
        private function GetTopTen() {
            $connection = Yii::$app->getDb ();
    
            $command = $connection->createCommand ( "SELECT colonias.nomasen AS name, count(rv.geom) AS conteo FROM colonias LEFT JOIN rv ON ST_Contains(colonias.geom, rv.geom) WHERE colonias.nomasen!='NINGUNO'  GROUP BY  colonias.nomasen ORDER BY conteo DESC LIMIT :limit" )->bindValue ( ':limit' , $this->limit );
    
            $tops = $command->queryAll ();
            
            foreach ( $tops as $top ) {
                $topten['names'][] = $top['name'];
                $topten['conteos'][] = $this->ToInt ( $top['conteo'] );
            }
    
            return $topten;
        }
    
        private function ToInt( $valor ) {
            return ( int ) $valor;
        }
    
        public function run() {
            return $this->render ( 'charts' , [ 'topten' => $this->topten , 'limit' => $this->limit , 'type' => $this->type ] );
        }
    
    }
    

    EDIT 2

    As the currently assigned data is the array

    $data = ["column", "bar", "pie"];  
    

    and the above-given code where we are binding the select2-select event we are using $(this).val() which will pass integer value rather than the actual string you can either change the array to hold name value pair like

    $data = ["column"=>"column", "bar"=>"bar", "pie"=>"pie"];
    

    or change the javascript code to detect the selected text rather than the value

    $(this).select2(\"data\")[0].text;