ในบทความนี้เป็นแนวทางในการนำ VueJS มาใช้ร่วมกันกับ Yii Framework 2 ในการโหลดข้อมูล API / JSON มาแสดงผลด้วย Axios โดยไม่ได้ Refresh หน้าเว็บ (คุณสมบัติเด่นเขาล่ะ one page application)
Package ที่จะใช้มี 3 ตัวคือ
- VueJS ตัวพระเอก
- Axios ตัวนางเอก
- NProgress เอาไวดู progress การโหลดข้อมูล (พระรอง)
หน้าตาก็จะประมาณนี้
เรียก Package ที่เกี่ยวข้อง
เอาละเริ่มกันเลยครับ โดยเริ่มจากการนำ package เข้ามาใส่ใน frontend/views/layouts/main.php เนื่องจากเราไม่ได้ใช้งานแบบ NodeJS ก็เรียกมาใช้แบบนี้ล่ะ
<?php $this->registerJsFile("//cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js", ['position' => \yii\web\View::POS_HEAD])?>
<?php $this->registerJsFile("//unpkg.com/axios/dist/axios.min.js", ['position' => \yii\web\View::POS_HEAD])?>
<?php $this->registerCssFile("https://cdn.rawgit.com/rikmms/progress-bar-4-axios/0a3acf92/dist/nprogress.css")?>
<?php $this->registerJsFile("https://cdn.rawgit.com/rikmms/progress-bar-4-axios/0a3acf92/dist/index.js", ['position' => \yii\web\View::POS_HEAD])?>
POS_HEAD เอาไว้บนหัวเลยเพราะไม่ต้อง load jQuery ก่อน (ไม่เกี่ยวกัน)
เขียน Action ให้ Response JSON
ในที่นี้จะสร้าง Controller action ใหม่ เพื่อให้ response JSON (ยังไม่ได้ทำ API นะ)
<?php
namespace frontend\modules\information\controllers;
use Yii;
use yii\web\Controller;
use yii\web\Response;
class ReportJsonController extends Controller
{
public function actionAverageGrade()
{
Yii::$app->response->format = Response::FORMAT_JSON;
$data = [];
$dataProvider = [];
if(Yii::$app->request->get()){
$model = Yii::$app->request->get();
$sql = "
SELECT
MIN(c.ClassName) AS ClassName,
COUNT(CASE WHEN (CONVERT(float,GPA) < 1) THEN PersonID ELSE NULL END) AS cnt1,
COUNT(CASE WHEN (CONVERT(float,GPA) BETWEEN 1.00 AND 1.49) THEN PersonID ELSE NULL END) AS cnt2,
COUNT(CASE WHEN (CONVERT(float,GPA) BETWEEN 1.50 AND 1.99) THEN PersonID ELSE NULL END) AS cnt3,
COUNT(CASE WHEN (CONVERT(float,GPA) BETWEEN 2.00 AND 2.49) THEN PersonID ELSE NULL END) AS cnt4,
COUNT(CASE WHEN (CONVERT(float,GPA) BETWEEN 2.50 AND 2.99) THEN PersonID ELSE NULL END) AS cnt5,
COUNT(CASE WHEN (CONVERT(float,GPA) BETWEEN 3.00 AND 3.49) THEN PersonID ELSE NULL END) AS cnt6,
COUNT(CASE WHEN (CONVERT(float,GPA) BETWEEN 3.50 AND 3.99) THEN PersonID ELSE NULL END) AS cnt7,
COUNT(CASE WHEN (CONVERT(float,GPA) = 4.00) THEN PersonID ELSE NULL END) AS cnt8
FROM VRegGPATermClass gpa
LEFT JOIN RegBClass c ON c.ClassID = gpa.ClassID
WHERE gpa.CurriculumID = :CurriculumID
AND AcademicYear = :AcademicYear
AND TermID = :TermID
GROUP BY c.ClassID
ORDER BY c.ClassID
";
$c = Yii::$app->db->createCommand($sql);
$c->bindValues([
'CurriculumID' => $model['CurriculumID'],
'AcademicYear' => $model['AcademicYear'],
'TermID' => $model['TermID']
]);
$data = $c->queryAll();
return $data;
}
}
}
หน้าตา JSON ก็จะประมาณนี้
[{"ClassName":"มัธยมศึกษาปีที่ 1","cnt1":"1","cnt2":"6","cnt3":"49","cnt4":"48","cnt5":"55","cnt6":"45","cnt7":"1","cnt8":"0"},{"ClassName":"มัธยมศึกษาปีที่ 2","cnt1":"2","cnt2":"12","cnt3":"53","cnt4":"63","cnt5":"49","cnt6":"34","cnt7":"10","cnt8":"0"},{"ClassName":"มัธยมศึกษาปีที่ 3","cnt1":"0","cnt2":"2","cnt3":"52","cnt4":"55","cnt5":"33","cnt6":"47","cnt7":"20","cnt8":"0"}]
สร้าง Model สำหรับรับข้อมูล
<?php
/**
* User: Manop Kongoon
* Date: 4/7/2018
* Time: 10:44 PM
*/
namespace frontend\modules\information\models;
class InformationForm extends \yii\base\Model
{
public $AcademicYear;
public $CurriculumID;
public $TermID;
public $ClassID;
public function rules()
{
return [
[['AcademicYear', 'CurriculumID', 'ClassID', 'TermID'], 'required'],
];
}
public function scenarios() {
$scenarios = parent::scenarios();
$scenarios['average_grade'] = ['AcademicYear','CurriculumID', 'TermID'];
$scenarios['summary_class'] = ['AcademicYear','ClassID', 'TermID'];
return $scenarios;
}
public function attributeLabels()
{
return [
'AcademicYear' => 'ปีการศึกษา',
'CurriculumID' => 'ระดับการศึกษา',
'TermID' => 'เทอม',
'ClassID' => 'ชั้น'
];
}
}
เขียนการแสดงผลข้อมูล
<?php
namespace frontend\modules\information\controllers;
use frontend\modules\information\models\InformationForm;
use Yii;
class ReportController extends \yii\web\Controller
{
public function actionAverageGrade()
{
$model = new InformationForm(['scenario' => 'average_grade']);
if($model->load(Yii::$app->request->post())){
}
return $this->render('average-grade', [
'model' => $model
]);
}
}
views average-grade.php
<?php
use common\models\RegBCurriculum;
use common\models\RegBTerm;
use yii\grid\GridView;
use yii\helpers\ArrayHelper;
use yii\helpers\Html;
use yii\helpers\Url;
use yii\widgets\ActiveForm;
$this->title = 'ผลการเรียนเฉลี่ย';
?>
<?php $this->registerCss("
@media only screen and (min-width : 768px) {
.search {
display: flex;
align-items: center;
}
}
") ?>
<div id="app">
<div class="panel">
<div class="panel-body">
<h3 class="title-hero">
<?= $this->title ?>
</h3>
<?php $form = ActiveForm::begin() ?>
<div class="row">
<div class="search">
<div class="col-md-2"><?= $form->field($model, 'AcademicYear')->textInput(['v-model' => 'AcademicYear']) ?></div>
<div class="col-md-2"><?= $form->field($model, 'CurriculumID')->dropDownList(ArrayHelper::map(RegBCurriculum::find()->all(), 'CurriculumID', 'CurriculumName'), ['v-model' => 'CurriculumID']) ?></div>
<div class="col-md-2"><?= $form->field($model, 'TermID')->dropDownList(ArrayHelper::map(RegBTerm::find()->all(), 'TermID', 'TermName'), ['v-model' => 'TermID']) ?></div>
<div class="col-md-2"><?= Html::button('แสดงผล', ['class' => 'btn btn-success btn-submit', 'v-on:click' => 'report']) ?></div>
</div>
</div>
<?php ActiveForm::end() ?>
</div>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>ระดับ</th>
<th>0.00-0.99</th>
<th>1.00-1.49</th>
<th>1.50-1.99</th>
<th>2.00-2.49</th>
<th>2.50-2.99</th>
<th>3.00-3.49</th>
<th>3.50-3.99</th>
<th>4.00</th>
</tr>
</thead>
<tbody>
<tr v-for="data in Results">
<td>{{ data.ClassName }}</td>
<td>{{ data.cnt1 }}</td>
<td>{{ data.cnt2 }}</td>
<td>{{ data.cnt3 }}</td>
<td>{{ data.cnt4 }}</td>
<td>{{ data.cnt5 }}</td>
<td>{{ data.cnt6 }}</td>
<td>{{ data.cnt7 }}</td>
<td>{{ data.cnt8 }}</td>
</tr>
</tbody>
</table>
</div>
</div><!-- app -->
<script>
loadProgressBar()
app = new Vue({
el: "#app",
data: {
AcademicYear: "",
TermID: "",
CurriculumID: "",
Results: []
},
methods: {
report: function (event) {
var vm = this;
axios.get("<?=Url::to(['/information/report-json/average-grade', true])?>", {
params: {
AcademicYear: vm.AcademicYear,
CurriculumID: vm.CurriculumID,
TermID: vm.TermID
}
})
.then(function (response) {
vm.Results = response.data
//console.log(response);
})
.catch(function (error) {
console.log(error);
})
}
}
})
</script>
concept คือ มีตัวแปรหลัก 3 ตัว (v-model) คือ ปีการศึกษา, ระดับ และ เทอม ใน input และ dropdownlist
เมื่อกดปุ่ม axios จะส่งข้อมูลแบบ get ไปพร้อมกับตัวแปร ปีการศึกษา, ระดับ, เทอม ไปประมวลผลตาม url ที่กำหนด แล้วตอบกลับเป็น JSON จากนั้นนำ reponse.data มากำหนดค่าให้กับ vm.Results (vm คือ this)
จากนั้นตรงตาราง data in Results ก็เอามา loop (จริงๆ ถ้าใช้ grid component ก็น่าจะดีกว่า) ด้วย v-for
ความคิดเห็น