Lesson 3: Simple CRUD using Vue JS

Here is simple CRUD with frontend using VueJs, backend using api with Laravel
Data structure:
– Question 1
+ Answer 1
+ Answer 2
– Question 2
+ Answer 1
+ Answer 2
+ Answer 3

Add file resources/views/question/list.blade.php

@extends('layouts.admin')
@section('content')
<script>
    var questionType = {"1" : "Type 1", "2" : "Type 2"};
</script>
<h4 class="m-t-0">Question manage</h4>

<div class="row">
    <div class="col-sm-12">
        <div class="card-box">
            <div id="app">
                <div class="row">
                    <div class="col-sm-6"></div>
                    <div class="col-sm-6">
                        <div id="datatable-responsive_filter" class="dataTables_filter">
                            <a type="button" class="btn btn-primary" v-on:click="addNew">Add</a>
                            <button type="button" v-on:click="bulkDelete" class="btn btn-primary">Delete</button>
                        </div>
                    </div>
                </div>
                <table class="table table-hover mails m-0 table-actions-bar">
                    <thead>
                    <tr>
                        <th>
                            <div class="checkbox checkbox-primary checkbox-single m-r-15">
                                <input id="action-checkbox" type="checkbox"/>
                                <label for="action-checkbox"></label>
                            </div>
                        </th>
                        <th>Question</th>
                        <th>Type</th>
                        <th>Description</th>
                        <th>Parameter</th>
                        <th></th>
                    </tr>
                    </thead>

                    <tbody>
                    <tr v-if="showAddPopup()">
                        <td colspan="6">
                            <form v-on:submit="save" method="post">
                                <div class="row">
                                    <div class="col-sm-6">
                                        <div class="form-group">
                                            <label for="title">Question title</label>
                                            <input id="title" type="text" class="form-control" v-model="currentItem.title" name="title">
                                        </div>
                                        <div class="form-group">
                                            <label for="description">Detail</label>
                                            <input id="description" type="text" class="form-control" v-model="currentItem.description" name="description">
                                        </div>
                                        <div class="form-group">
                                            <label for="notes">notes</label>
                                            <input id="notes" type="text" class="form-control" v-model="currentItem.notes" name="notes">
                                        </div>
                                        <div class="form-group">
                                            <label for="type">Type</label>
                                            <select name="type" id="" class="form-control" v-model="currentItem.type">
                                                <option v-for="t in questionType" :label="t.display" :value="t.value" :key="t.value"></option>
                                            </select>
                                        </div>
                                    </div>
                                    <div class="col-sm-6">
                                        <label for="" class="pull-left">Câu trả lời</label>
                                        <button v-on:click="addAnswer" class="btn btn-primary pull-right" type="button">New Answer</button>
                                        <table class="table table-hover mails m-0">
                                            <tr v-for="(ans,index) in currentItem.answers">
                                                <td>
                                                    <input type="hidden" v-model="ans.id">
                                                    <input type="text" class="form-control" v-model="ans.content" placeholder="Answer content">
                                                </td>
                                                <td width="30%">
                                                    <input type="number" class="form-control" v-model.number="ans.point" placeholder="Point">
                                                </td>
                                                <td width="10%"><button class="btn btn-danger" type="button" v-on:click="deleteAnswer(index)">X</button></td>
                                            </tr>
                                        </table>
                                    </div>
                                </div>
                                <input type="hidden" name="id" v-model="currentItem.id"/>
                                <center>
                                    <button type="submit" class="btn btn-primary ">Save</button>
                                    <button type="button" v-on:click="closeAddPopup" class="btn btn-danger ">Close</button>
                                </center>
                                
                            </form>
                        </td>
                    </tr>
                    <tr v-for="item in items">
                        <td>
                            <div class="checkbox checkbox-primary m-r-15">
                                <input id="checkbox1" type="checkbox">
                                <label for="checkbox1"></label>
                            </div>
                        </td>
                        <td>
                            <a v-on:click="detail(item.id)">@{{ item.title }}</a>
                        </td>
                        <td>@{{ item.type }}</td>
                        <td>@{{ item.description }}</td>
                        <td>@{{ item.notes }}</td>
                        <td><a v-on:click="deleteItem(item.id)" class="btn btn-primary">Delete</a></td>
                    </tr>
                    </tbody>
                </table>
            </div>


        </div>
    </div>
</div>


<script src="{{ asset('js/question.js') }}"></script>

@endsection

Create js file /public/js/question.js


const app = new Vue({
    el: '#app',
    data() {
        return {
            items: [],
            loading: true,
            new: false,
            edit: false,
            currentItem: {},
            error: false
        }
      },
    mounted: function() {
        fetch(url + "admin?controller=Question&task=getItems", {
          method: 'get'
        })
          .then((response) => {
            return response.json()
          })
          .then((jsonData) => {
            this.items = jsonData;
          })
      },
    methods: {
        addNew(){
            this.currentItem = {
                title:'',
                description: '',
                notes: '',
                type: '',
                id: '',
                answers: []
            };
            this.new = true;
        },
        detail(id){
            for (i in this.items){
                if(this.items[i].id==id){
                    this.currentItem = this.items[i];
                    break;
                }
            }
            this.edit= true;
        },
        save(e){
            e.preventDefault();
            self = this;
            axios({
                method: 'post',
                url: url+'admin?controller=Question&task=store',
                data: this.currentItem,
                responseType: 'json'
            })
            .then(function(response){
                if(response.data.status){
                    if(self.currentItem.id != ''){
                        //update
                        for (i in self.items){
                            if(self.items[i].id==id){
                                self.items[i] = self.currentItem;
                                break;
                            }
                        }
                    }else{
                        //insert
                        self.items.push(response.data.data);
                        self.edit=false;
                        self.new=false;
                    }
                }else{
                    alert(response.data.message);
                }
            })
            .catch(function(error) {
                console.log(error);
                alert('Error');
                self.error = 'Error';
            })
        },
        deleteItem(id){
            if(confirm('Do you want to delete?')){
                fetch(url+'admin?controller=Question&task=destroy&id='+id, {
                    method: 'get'
                  })
                .then((response) => {
                    return response.json()
                })
                .then((jsonData) => {
                    console.log(this.items);
                    if(jsonData.status){
                        for (i in this.items){
                            if(this.items[i].id==id){
                                this.items.splice(i, 1);
                                break;
                            }
                        }
                    }else{
                        alert('delete failed');
                    }
                }).catch(function(error){
                    alert('Delete fail ed');
                })
            }
        },
        bulkDelete(e){

        },
        showAddPopup(e){
            return this.edit || this.new;
        },
        closeAddPopup(){
            this.edit=false;
            this.new=false;
        },
        addAnswer(){
            this.currentItem.answers.push({content: '',point:''});
        },
        deleteAnswer(index){
            this.currentItem.answers.splice(index,1);
        }
    }
});

Create Controller file \app\Http\Controllers\Admin\QuestionController.php

<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Admin\Controller;
use App\Question;
use App\Answer;
use Illuminate\Http\Request;
use App\Glossary\QuestionType;


class QuestionController extends Controller
{
    protected $type;
    private $rule = [
        // 'name'=>'required|max:30',
    ];

    public function __construct(){
    }
    public function index()
    {
        $questionType = (QuestionType::getAll());
        return view('admin.question.list', [
            'questionType' => $questionType
        ]);
    }
    public function getItems(){
        $filter = isset($_GET['filter'])?$_GET['filter']:array();

        $data = array();
        $data['filter'] = $filter;
        $items = Question::getItems($data['filter']);
        return $items;
    }

    public function getAnswers(){
        $question_id = request()->input('id');
        return Answer::getAnswers($question_id);
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */

    public function create()
    {
        return view('admin.question.create', [
        ]);
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        $data = request()->post();
        $data['description'] = strval($data['description']);
        $data['notes'] = strval($data['notes']);
        $answers = $data['answers'];
        if($data['id']){
            $result = Question::find($data['id']);
            $result->update($data);
        }else{
            $result = Question::create($data);
        }
        $result->answers = $this->saveAnswers($result->id,$answers);
        return ['status'=>1,'data'=>$result];
    }

    private function saveAnswers($question_id,$answers){
        if(!$question_id) return [];
        $result = [];
        $orignal = Answer::getAnswers($question_id);
        $orignalId = array_map(function($e){return $e->id;},$orignal);
        $new = array_filter($answers,function($e){return !isset($e['id']);});
        $update = array_filter($answers,function($e){return isset($e['id']);});
        $updateId = array_map(function($e){return $e['id'];},$update);
        sort($orignalId);
        sort($updateId);
        $deleteId = array_diff($orignalId,$updateId);
        if(count($deleteId) > 0){
            Answer::destroy($deleteId);
        }
        if(count($update)){
            foreach($update as $data){
                $model = Answer::find($data['id']);
                $model->update($data);
                $result[] = $model;
            }
        }
        if(count($new)){
            foreach($new as $data){
                $data['question_id'] = $question_id;
                $result[] = Answer::create($data);
            }
        }
        return $result;
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
    }



    /**
     * Show the form for editing the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function edit($id)
    {
    }

    public function update(Request $request)
    {

    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy(Request $request)
    {
        $id = $request->input('id');
        $result = Question::destroy($id);
        return ['status'=>$result];
    }
}

Create model file \app\Question.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Question extends Model
{
    protected $table = 'questions';
    protected $primaryKey = 'id';
    //
    protected $fillable = array('title','description','notes','type');

    public static function getItems($filter=[]){
        $items = Question::all()->toArray();
        $ids = array_map(function($e){return $e['id'];},$items);
        if($ids){
            $answers = \App\Answer::whereIn('question_id',$ids)->get()->toArray();
        }
        foreach($items as &$item){
            $item['answers'] = array_filter($answers,function($e) use ($item) {return $e['question_id'] ==  $item['id'];});
        }
        return $items;
    }

    public function answers(){
        return $this->hasMany('App\Answer','question_id','id')->select(['id','content','point']);
    }

}

Leave a Reply

Your email address will not be published. Required fields are marked *