一步一步學Vue(二)

  接上篇,在本篇中,咱們將要實現以下,功能,編輯和查詢,咱們當前的todolist程序,和線上其它的demo程序不一樣,咱們會對其進行增刪改查的基本操做,以後進行進一步的完善,按照常規的系統使用經驗,通常咱們新增和編輯都是在模態框中處理,這裏咱們不會去構建複雜的模態框,只用一個簡單的div層來代替,後期接下來的文章中咱們會重複造輪子,構建咱們本身的輕量級框架(UI庫)。css

  首先,咱們對咱們的頁面結構進行一下簡單的調整,加入bootstrap只是爲了讓頁面不那麼赤裸裸,對其它不會有任何影響html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>demo1</title>
    <script src="https://cdn.bootcss.com/vue/2.4.1/vue.js"></script>
    <link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="stylesheet">

</head>

<body class="container">
    <div id="app" class='row'>
        <div class="col-md-6">
            <table class="table table-bordered">
                <tr>
                    <th>title</th>
                    <th>desc</th>
                    <th></th>
                </tr>
                <tr v-for="(todoItem,index) in todolist">
                    <td>{{todoItem.title}}</td>
                    <td>{{todoItem.desc}}</td>
                    <td><input type="button" value="remove" @click="remove(index)" class="btn btn-danger" /></td>
                </tr>
            </table>
        </div>
        <div class="col-md-6">

            <div class="form-inline">
                <label for="title" class="control-label col-md-4">title:</label>
                <input type="text" v-model="title" class="form-control col-md-8">
            </div>
            <div class="form-inline">
                <label for="desc" class="control-label col-md-4">desc</label>
                <input type="text" v-model="desc" class="form-control col-md-8">
            </div>
            <div class="form-inline">
                <input type="button" value="OK" v-on:click="addItem()" class="btn btn-primary offset-md-10" />

            </div>

        </div>


    </div>
    <script>
        var TodoItem = function (title, desc) {
            this.title = title;
            this.desc = desc;
        }
        new Vue({
            el: '#app',
            data: {
                todolist: [],
                title: '',
                desc: ''
            },
            methods: {
                addItem: function () {
                    this.todolist.push(new TodoItem(this.title, this.desc))

                    this.title = this.desc = '';

                },
                remove: function (index) {
                    this.todolist.splice(index, 1);
                }

            }
        })
    </script>
</body>

</html>

js沒有任何變化,只是引入了bootstrap4以後,對html結構進行了微調整,因爲咱們須要增長編輯操做,咱們把增長編輯操做概括爲如下幾個步驟:vue

一、增長編輯按鈕;ios

二、點擊編輯按鈕綁定所對應todoitem到表單進行編輯bootstrap

三、點擊表單中OK按鈕,對編輯結果進行應用。axios

注意:這裏須要區分,在點擊OK按鈕時,進行的是新增操做仍是編輯操做,咱們對咱們數據結構加入自增ID來標示,若是編輯項目有ID,則爲保存編輯操做,若是不存在ID則爲新增保存操做,對咱們的數據結構進行如下微調,因爲新增了ID項目,那麼在data屬性中也要增長ID屬性,這樣每次新增屬性都要直接修改data屬性,這就是變化點,下面咱們對變化點進行簡單封裝,修改代碼以下:後端

 data: {
                todolist: [],
                todoItem:{
                    id:'',
                    title:'',
                    desc:''
                }
            },

另外咱們須要實現自增ID,這裏採用最直接的方式,全局ID,使其自增便可,對TodoItem進行簡單的閉包處理:緩存

  var TodoItem = (function () {
            var id = 1;
            return function (title, desc) {
                this.title = title;
                this.desc = desc;

                this.id = id++;
            }
        })();

爲了適應新數據結構的變化,則其它修改部分總體貼出來,變化部分見黃色:數據結構

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>demo1</title>
    <script src="https://cdn.bootcss.com/vue/2.4.1/vue.js"></script>
    <link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="stylesheet">

</head>

<body class="container">
    <div id="app" class='row'>
        <div class="col-md-6">
            <table class="table table-bordered">
                <tr>
                    <th></th>
                    <th>title</th>
                    <th>desc</th>
                    <th></th>
                </tr>
                <tr v-for="(todoItem,index) in todolist">
                    <th>{{todoItem.id}}</th>
                    <td>{{todoItem.title}}</td>
                    <td>{{todoItem.desc}}</td>
                    <td><input type="button" value="remove" @click="remove(index)" class="btn btn-danger" /></td>
                </tr>
            </table>
        </div>
        <div class="col-md-6">

            <div class="form-inline">
                <label for="title" class="control-label col-md-4">title:</label>
                <input type="hidden" v-bind:value="todoItem.id" />
                <input type="text" v-model="todoItem.title" class="form-control col-md-8">
            </div>
            <div class="form-inline">
                <label for="desc" class="control-label col-md-4">desc</label>
                <input type="text" v-model="todoItem.desc" class="form-control col-md-8">
            </div>
            <div class="form-inline">
                <input type="button" value="OK" v-on:click="addItem()" class="btn btn-primary offset-md-10" />

            </div>

        </div>


    </div>
    <script>
        var TodoItem = (function () {
            var id = 1;
            return function (title, desc) {
                this.title = title;
                this.desc = desc;

                this.id = id++;
            }
        })();
        new Vue({
            el: '#app',
            data: {
                todolist: [],
                todoItem: {
                    id: '',
                    title: '',
                    desc: ''
                }
            },
            methods: {
                addItem: function () {
                    // this.todolist.push(new TodoItem(this.title, this.desc))
                    this.todolist.push( new TodoItem( this.todoItem.title, this.todoItem.desc ) ); this.todoItem={};

                },
                remove: function (index) {
                    this.todolist.splice(index, 1);
                }

            }
        })
    </script>
</body>

</html>

刷新頁面,測試效果以下:閉包

自增ID已經完成,那麼添加編輯時綁定,按照上面的步驟,先添加編輯按鈕,在刪除按鈕後添加編輯按鈕,並在methods對象中增長對應的回調函數,對edit操做進行響應,函數中主要實現對todoItem對象進行綁定操做,具體代碼修改以下:

    <table class="table table-bordered">
                <tr>
                    <th></th>
                    <th>title</th>
                    <th>desc</th>
                    <th></th>
                </tr>
                <tr v-for="(todoItem,index) in todolist">
                    <th>{{todoItem.id}}</th>
                    <td>{{todoItem.title}}</td>
                    <td>{{todoItem.desc}}</td>
                    <td>
                        <input type="button" value="remove" @click="remove(index)" class="btn btn-danger" />
                        <input type="button" value="edit" @click="edit(todoItem.id)" class="btn btn-info" />
                    </td>
                </tr>
            </table>
 methods: {
                edit: function (id) {
                    //找到id值等於所傳參數的todoitem
                    var obj = this.todolist.filter(v => v.id === id)[0];
                    //對數據進行綁定,則數據會響應到表單上
                    this.todoItem = obj;
                },
            ...省略其它
}

這樣有沒有問題呢?咱們運行看一下效果:

從運行結果上看,咱們點擊edit操做,的確完成了綁定,可是在咱們修改編輯,尚未點擊OK按鈕的狀況下,表單中的變化已經提現到了列表中,這就不符合正常邏輯了,爲何會有這樣的狀況呢,緣由就在於this.todoItem=obj;這句代碼就是所謂的引用賦值,因此todoitem和obj指向的是同一個地址,則對this.todoItem的修改,會直接反應到obj上,也就是todoList中的這個元素上,因此在列表中會直接提現出來,避免這種狀況的發生的方法,只要避免飲用賦值便可,因此對上述代碼進行以下修改:

 //找到id值等於所傳參數的todoitem
                    var obj = this.todolist.filter(v => v.id === id)[0];
                    //對數據進行綁定,則數據會響應到表單上
                    this.todoItem = {
                        id:obj.id,
                        title:obj.title,
                        desc:obj.desc
                    };

刷新運行,發生程序按照預期運行了,接下來,增長更新保存操做,修改OK按鈕的事件綁定方式爲save,並經過id判斷新增仍是修改操做:

 <div class="col-md-6">

            <div class="form-inline">
                <label for="title" class="control-label col-md-4">title:</label>
                <input type="hidden" v-bind:value="todoItem.id" />
                <input type="text" v-model="todoItem.title" class="form-control col-md-8">
            </div>
            <div class="form-inline">
                <label for="desc" class="control-label col-md-4">desc</label>
                <input type="text" v-model="todoItem.desc" class="form-control col-md-8">
            </div>
            <div class="form-inline">
                <input type="button" value="OK" v-on:click="save()" class="btn btn-primary offset-md-10" />
            </div>
        </div>
 methods: {
                edit: function (id) {
                    //找到id值等於所傳參數的todoitem
                    var obj = this.todolist.filter(v => v.id === id)[0];
                    //對數據進行綁定,則數據會響應到表單上
                    this.todoItem = {
                        id: obj.id,
                        title: obj.title,
                        desc: obj.desc
                    };
                },
                save: function () {
                    if (this.todoItem.id) {
                        //編輯保存
                        var obj = this.todolist.filter(v => v.id === this.todoItem.id)[0];
                        obj.title = this.todoItem.title;
                        obj.desc = this.todoItem.desc;

                    } else {
                        //新增保存
                        this.todolist.push(
                            new TodoItem(
                                this.todoItem.title,
                                this.todoItem.desc
                            )
                        );
                    }
                    //重置表單  這部分筆誤,在實際代碼中已修改,可是貼上來的代碼沒有作修改,具體見最下面代碼,錯誤緣由見下方回覆
                    this.todoItem = {};
                },
                
                remove: function (index) {
                    this.todolist.splice(index, 1);
                }

            }

代碼比較簡單,這裏就再也不贅述,能夠看一下運行效果:

爲了逼格更高一點,這裏我再介紹一個指令,其實上面已經使用了,v-bind ,這個指令和v-on是相似的,二者的區別在於後面的參數不一樣,通常v-bind用來傳遞屬性參數,通常使用縮寫形式:attr,能夠綁定自定義屬性,上面代碼中咱們對Id值的綁定已經使用了v-bind:value="todoItem.id",這裏至關於angular中ng-bind。基於對v-bind的瞭解,咱們能夠推理,給v-bind添加disable的屬性,使OK按鈕只有再title不爲空的前提下再可用。

<div class="form-inline">
                <input type="button" value="OK" v-on:click="save()" class="btn btn-primary offset-md-10" :disabled='!todoItem.title'/>
            </div>

刷新運行:

上面代碼能很好的運行,可是如今若是我須要修改一下驗證規則,在title和desc都不爲空的狀況下,才使用OK按鈕可用,如何作?你固然會說,很簡單,直接加入一個&&條件不就好了,可是問題在於,如今個人模型比較小,屬性比較少,若是我存在一個大量屬性的對象,作相似的驗證,這樣來修改代碼就是一個坑了,說到這裏,其實已經能夠想到,既然驗證規則再變,那麼能夠考慮做爲一個變化點封裝起來,最直觀的方式,是封裝爲一個方法,可是vue提供了更好的方式:computed,計算屬性,這個計算屬性應該是來自於knockout的概念,有興趣的能夠看一下knockout中計算屬性,修改代碼以下:

 new Vue({
            el: '#app',
            data: {
                todolist: [],
                todoItem: {
                    id: '',
                    title: '',
                    desc: ''
                }
            },
            computed:{ canSave:function(){ return !this.todoItem.title || !this.todoItem.desc; } },
           ...省略其它
            }
        })

能夠看到computed屬性是以方法的方式來定義的,這裏是最簡單方式,只讀的方式,固然還能夠經過get set方式進行讀寫控制,咱們後期的代碼中可能會見到,若是捉急,能夠去查看官方文檔。在computed使用的時候,和方法調用大相徑庭,而是和data屬性中代理的模式同樣,若是你使用過knockout,那麼你對這種用法就見怪不怪了,html部分修改以下:

<div class="form-inline">
                <input type="button" value="OK" v-on:click="save()" class="btn btn-primary offset-md-10" :disabled='canSave'/>
            </div>

刷新頁面,運行效果如圖:

ok,編輯這部份內容就到這裏吧,在沒有後端接口的前提下,保存操做都是模擬的,接下來對咱們的查詢進行一下簡單的介紹,這裏查詢,因爲沒有後端接口,我們只作最簡單的演示,對代碼作以下處理:

一、增長查詢輸入框,keyword,添加查詢按鈕

二、點擊查詢操做,預期結果:根據輸入的查詢關鍵字,過濾列表

按照上面思路對咱們代碼進行修改:

 <div class="row toolbar">
        <div class="col-md-8">
            keyword:
            <input type="text" v-model="keyword" />
            <input type="button" @click="query()" value="search" />
        </div>
    </div>

data中增長keyword屬性,以實現對輸入框的綁定,在methods中添加query方法:

 //全局變量,用來緩存全部數據
        var list = [];
 data: {
                todolist: [],
                todoItem: {
                    id: '',
                    title: '',
                    desc: ''
                },
                keyword:''
            },

 

query: function () {
                    //過濾title中不包含keyword的數據
                    //這裏必須經過list全局變量過濾,而不能經過this.todolist,由於須要給this.todolist賦值,賦值後沒法還原原來的列表。
                    this.todolist = list.filter(v => v.title.indexOf(this.keyword) !== -1);
                }

刷新頁面運行效果如圖:

代碼部分註釋中已經寫的很清楚了,有疑問可提價comment。本章就到這裏,文章有點水,在(三)中會加入添加服務端支持,並使用axios和服務端rest接口進行交互敬請期待。。。準備睡覺。。😴。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>demo1</title>
    <script src="https://cdn.bootcss.com/vue/2.4.1/vue.js"></script>
    <link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="stylesheet">

</head>

<body class="container">
    <div id="app">
        <div class="row toolbar">
            <div class="col-md-8">
                keyword:
                <input type="text" v-model="keyword" />
                <input type="button" @click="query()" value="search" />
            </div>
        </div>
        <div class='row'>

            <div class="col-md-6">
                <table class="table table-bordered">
                    <tr>
                        <th></th>
                        <th>title</th>
                        <th>desc</th>
                        <th></th>
                    </tr>
                    <tr v-for="(todoItem,index) in todolist">
                        <th>{{todoItem.id}}</th>
                        <td>{{todoItem.title}}</td>
                        <td>{{todoItem.desc}}</td>
                        <td>
                            <input type="button" value="remove" @click="remove(index)" class="btn btn-danger" />
                            <input type="button" value="edit" @click="edit(todoItem.id)" class="btn btn-info" />
                        </td>
                    </tr>
                </table>
            </div>
            <div class="col-md-6">

                <div class="form-inline">
                    <label for="title" class="control-label col-md-4">title:</label>
                    <input type="hidden" v-bind:value="todoItem.id" />
                    <input type="text" v-model="todoItem.title" class="form-control col-md-8">
                </div>
                <div class="form-inline">
                    <label for="desc" class="control-label col-md-4">desc</label>
                    <input type="text" v-model="todoItem.desc" class="form-control col-md-8">
                </div>
                <div class="form-inline">
                    <input type="button" value="OK" v-on:click="save()" class="btn btn-primary offset-md-10" :disabled='canSave' />
                </div>
            </div>


        </div>
    </div>
    <script>
        var list = [];
        var TodoItem = (function () {
            var id = 1;
            return function (title, desc) {
                this.title = title;
                this.desc = desc;

                this.id = id++;
            }
        })();
        new Vue({
            el: '#app',
            data: {
                todolist: [],
                todoItem: {
                    id: '',
                    title: '',
                    desc: ''
                },
                keyword: ''
            }, computed: {
                canSave: function () {
                    return !this.todoItem.title || !this.todoItem.desc;
                }
            },
            methods: {
                query: function () {
                    //過濾title中不包含keyword的數據
                    //這裏必須經過list全局變量過濾,而不能經過this.todolist,由於須要給this.todolist賦值,賦值後沒法還原原來的列表。
                    this.todolist = list.filter(v => v.title.indexOf(this.keyword) !== -1);
                },
                edit: function (id) {
                    //找到id值等於所傳參數的todoitem
                    var obj = this.todolist.filter(v => v.id === id)[0];
                    //對數據進行綁定,則數據會響應到表單上
                    this.todoItem = {
                        id: obj.id,
                        title: obj.title,
                        desc: obj.desc
                    };
                },
                save: function () {
                    if (this.todoItem.id) {
                        //編輯保存
                        var obj = this.todolist.filter(v => v.id === this.todoItem.id)[0];
                        obj.title = this.todoItem.title;
                        obj.desc = this.todoItem.desc;

                    } else {
                        //新增保存
                        this.todolist.push(
                            new TodoItem(
                                this.todoItem.title,
                                this.todoItem.desc
                            )
                        );
                    }
                    //重置表單
                    this.todoItem = { title: '', desc: '' };
                  
                    list = this.todolist;
                },

                remove: function (index) {
                    this.todolist.splice(index, 1);
                }

            }
        })
    </script>
</body>

</html>
View Code

 訂正:

remove方法執行中,加入list=this.todolist;

相關文章
相關標籤/搜索