表單

表單簡介

  • 表單輸入元素透過v-model指令創建雙向數據綁定,雙向自動更新元素内容。
  • v-model綁定的表單元素初始值為其綁定的data 變數,並非元素的相關屬性(如 value、checked等)
  • 不同的輸入元素會使用不同的屬性及拋出不同的事件:
元素 對應的屬性 拋出的事件
text
textarea
value input
select value change
checkbox
radio
checked change

文字輸入項

In [2]:
<div id="app-3-1">
  <input v-model="message">
  <p>您輸入: {{ message }}</p>
</div>

<script>
  var vm = new Vue({
    el: '#app-3-1',
    data: {
      message:''
    }
  })
</script>

您輸入: {{ message }}

多行文字輸入項

簡單的即時網頁編輯器範例:

In [3]:
<div id="app-3-2">
  <textarea v-model="message"></textarea>
  <div v-html="message"></div>
</div>

<script>
var vm = new Vue({
  el: '#app-3-2',
  data: {
    message:'<i>test</i><br><i>test</i>'
  }
})
</script>

複選框

單個複選框,可綁定到布林變數:

In [4]:
<div id="app-3-3">
  <input type="checkbox" v-model="checked">
  <label for="checkbox">{{ checked }}</label>
</div>

<script>
  var vm = new Vue({
    el: '#app-3-3',
    data: {
      checked:true
    }
  })
</script>

多個複選框,綁定到同一個陣列:

In [5]:
<div id="app-3-4">
  <div v-for="item in items">
    <input type="checkbox" :id="item.id" :value="item.text" v-model="checkedItem">
    <label :for="item.id" v-text="item.text"></label>
  </div>
  <hr>
  <span>選到的項目: {{ checkedItem }}</span>
</div>

<script>
  var vm = new Vue({
    el: '#app-3-4',
    data: {
      items: [
        { id: 1, text: "一" },
        { id: 2, text: "二" },
        { id: 3, text: "三" }
      ],
      checkedItem:[]
    }
  })
</script>

選到的項目: {{ checkedItem }}

單選按鈕

In [6]:
<div id="app-3-5">
  <div v-for="item in items">
    <input type="radio" :id="item.id" :value="item.text" v-model="checkedItem">
    <label :for="item.id" v-text="item.text"></label>
  </div>
  <hr>
  <span>選到的項目: {{ checkedItem }}</span>
</div>

<script>
  var vm = new Vue({
    el: '#app-3-5',
    data: {
      items: [
        { id: 1, text: "一" },
        { id: 2, text: "二" },
        { id: 3, text: "三" }
      ],
      checkedItem:'一'
    }
  })
</script>

選到的項目: {{ checkedItem }}

下拉式選單

In [7]:
<div id="app-3-6">
  <select v-model="myoption">
    <option value="">請選擇</option>
    <option>A</option>
    <option>B</option>
    <option>C</option>
  </select>
  <span>已選: {{ myoption }}</span>
</div>

<script>
  var vm1 = new Vue({
    el: '#app-3-6',
    data: {
      myoption: ''
    }
  });
</script>
已選: {{ myoption }}
  • 可復選的下拉式選單需綁定到陣列
In [8]:
<div id="app-3-7">
  <select v-model="myoption" multiple>
    <option value="">請選擇</option>
    <option>A</option>
    <option>B</option>
    <option>C</option>
  </select>
  <span>已選: {{ myoption }}</span>
</div>

<script>
  var vm1 = new Vue({
    el: '#app-3-7',
    data: {
      myoption: []
    }
  });
</script>
已選: {{ myoption }}

axios

  • Axios 是用於瀏覽器的 HTTP Client,可以方便開發者使用 AJAX 的套件。
<!DOCTYPE html>
<html>
<head>
  <!--調整手機瀏覽器的螢幕解析度 -->
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <!-- 安裝 Vue.js  -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>

  <!-- axios 套件-->
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
  <div id="app">
    <!-- 顯示 Json 資料 -->
    <pre v-text="items"></pre>
  </div>

  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        url: "http://ewin.tw/myApp/sample/UBike/",
        items: "資料讀取中..."
      },
      mounted() {
        //透過 axios 讀取網路上的json資料
        var _this=this;
        axios.get(this.url)
          .then(function (response) {
            _this.items=response.data.result.records;
          })
          .catch(function (error) {
          })
      }
    });
  </script>
</body>
</html>

綜合練習-簡易匯率

<!DOCTYPE html>
<html>
<head>
  <!-- 調整手機瀏覽器的螢幕解析度 -->
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">

  <!-- Vue.js  -->
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

  <!-- Sortable -->
  <script src="https://cdn.jsdelivr.net/npm/sortablejs@1.8.4/Sortable.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/Vue.Draggable/2.20.0/vuedraggable.umd.min.js"></script>

  <!-- axios -->
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>

  <style>
    .rateList {
      cursor: pointer
    }

      .rateList img {
        width: 40px;
        height: 36px;
        margin: 5px 5px -10px;
      }
  </style>

  <div id="app">
    <draggable v-model="items">

      <!--清單顯示國旗圖像及匯率名稱-->
      <div v-for="item in items" class="rateList" @click="itemClicked(item)">
        <img :src="url+'img/'+ item.id +'.png'" />
        {{item.id+' '+item.title+' '+item.ratio}}
      </div>

    </draggable>

    <pre>{{items}}</pre>
  </div>

  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        url: "http://ewin.tw/myApp/sample/匯率/",
        items: []
      },
      methods: {
        itemClicked(item) {
          alert(item.title + ":" + item.ratio);
        }
      },
      mounted() {
        var _this = this;
        axios.get(this.url)
          .then(function (response) {
            _this.items = response.data;
          })
          .catch(function (error) {
            console.log(error);
          })
      }
    });
  </script>
</body>
</html>

簡易匯率-Viutify 版

Viutify 清單

<!-- Vuetify.js -->
<link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.js"></script>

<v-app>
  <v-list two-line style="width:100%;">
      <div>
        <!-- 清單 -->
        <v-list-item>
          <v-list-item-avatar>
            <v-img></v-img>
          </v-list-item-avatar>

          <v-list-item-content>
            <v-list-item-title>
            </v-list-item-title>

            <v-list-item-subtitle>
            </v-list-item-subtitle>
          </v-list-item-content>

        <!--分隔線-->
        <v-divider></v-divider>
      </div>
  </v-list>
</v-app>
<!DOCTYPE html>
<html>
<head>
  <!-- 調整手機瀏覽器的螢幕解析度 -->
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">

  <!-- Vue.js  -->
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

  <!-- Sortable -->
  <script src="https://cdn.jsdelivr.net/npm/sortablejs@1.8.4/Sortable.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/Vue.Draggable/2.20.0/vuedraggable.umd.min.js"></script>

  <!-- axios -->
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>

  <!-- Vuetify.js -->
  <link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
  <script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.js"></script>

  <!-- 圖標 -->
  <script src="https://kit.fontawesome.com/1adb36782c.js" crossorigin="anonymous"></script>
</head>
<body>

  <style>
    .rateList {
      cursor: pointer
    }

      .rateList img {
        width: 40px;
        height: 36px;
        margin: 5px 5px -10px;
      }
  </style>

  <div id="app">
    <v-app>
      <v-list two-line style="width:100%;">
        <draggable v-model="items" handle=".handle">
          <div v-for="item in items" :key="item.id">
            <!-- 清單 -->
            <v-list-item @click="itemClicked(item)">
              <!--國旗圖像-->
              <v-list-item-avatar class="handle">
                <v-img :src="url+'img/'+ item.id +'.png'">
                </v-img>
              </v-list-item-avatar>

              <v-list-item-content>
                <!--匯率名稱-->
                <v-list-item-title v-text="item.title">
                </v-list-item-title>

                <!--匯率值-->
                <v-list-item-subtitle v-text="item.ratio">
                </v-list-item-subtitle>
              </v-list-item-content>

              <!--拖曳塊-->
              <v-list-item-avatar class="handle">
                <i class="fa fa-align-justify handle"></i>
              </v-list-item-avatar>
            </v-list-item>

            <!--分隔線-->
            <v-divider></v-divider>
          </div>
        </draggable>
      </v-list>

      <pre>{{items}}</pre>
    </v-app>
  </div>

  <script>
    var vm = new Vue({
      el: '#app',
      vuetify: new Vuetify(),
      data: {
        url: "http://ewin.tw/myApp/sample/匯率/",
        items: []
      },
      methods: {
        itemClicked(item) {
          alert(item.title + ":" + item.ratio);
        }
      },
      mounted() {
        var _this = this;
        axios.get(this.url)
          .then(function (response) {
            _this.items = response.data;
          })
          .catch(function (error) {
            console.log(error);
          })
      }
    });
  </script>
</body>
</html>

混入(Mixin)

混入(mixin) 提供了一種讓 Vue 組件可重複使用的功能,混入的對象可以包含任何組件,所有混入對象的選項將被“混合”進入該組件本身的選項。 下面例子把 age 過濾器獨立建立物件 myMixin,再以 mixin 方式混入,如此 myMixin 便可以切割到獨立的檔案,並可重複在別的專案中使用。

In [9]:
<!-- 定義 mixin,可以獨立放在 js 檔-->
<script>
  var myMixin = {
    filters: {
      age: function (year) {
        return new Date().getFullYear() - year;
      }
    }
  }
</script>
  
<div id="app-2-1">
  出生年:<input type="number" v-model="year"><br>
  年齡:{{year | age}}
</div>

<script>
  const vm = new Vue({
    data: {
      year: 1970
    },
    mixins: [myMixin]
  }).$mount('#app-2-1');  
</script>
出生年:
年齡:{{year | age}}

路由 vue-router

vue-router 路由允許我們通過不同的URL訪問不同的內容,可以實現多視圖的單頁 Web 應用程式(Single Page web Application, SPA)。

  • Vue 路由需要載入vue-router,可以透過 CDN方式載入:
    <script src="https://cdn.jsdelivr.net/npm/vue-router"></script>
    
  • 每一個頁面需包裝成元件
  • template 中的最頂層元件必須為單一元件,例如可用<div></div>包覆。
In [10]:
<!DOCTYPE html>
<html>
<head>
  <!--1.調整手機瀏覽器的螢幕解析度 -->
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">

  <!-- Vue.js  -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>

  <!-- vue-router 路由套件-->
  <script src="https://cdn.jsdelivr.net/npm/vue-router"></script>
</head>
<body>
  <!--首頁 -->
  <style></style>

  <template id="home">
    <div>首頁内容</div>
  </template>

  <script>
    const home = {
      template: '#home',
      data() {
        return {
        }
      },
      methods: {
      }
    };
  </script>

    
  <!-- page2 -->
  <style></style>

  <template id="page2">
    <div>第二頁</div>
  </template>

  <script>
    const page2 = {
      template: '#page2'
    };
  </script>


  <!-- 應用程式 -->
  <div id="app-ru1">
    <div>
      <!-- 路由鏈結 -->
      <router-link to="/">首頁</router-link>
      <router-link to="/page2">第二頁</router-link>     
    </div>

    <!-- 結果在此顯示 -->
    <router-view />
  </div>

  <script>
    const router = new VueRouter({
      routes: [
        {
          path: '/',
          component: home
        },
        {
          path: '/page2',
          component: page2
        }
      ]
    });

    const vm = new Vue({
      router,
      data() {
        return {
        }
      },
    }).$mount('#app-ru1')
  </script>
</body>
</html>
首頁 第二頁
  • 下面例子中新增了回上一頁的按鈕及 goBack() 方法
    • <button @click="goBack">←</button>
In [11]:
<!-- 應用程式 -->
<div id="app-ru2">
<div>
  <button @click="goBack"></button><!-- 路由鏈結 -->
  <router-link to="/">首頁</router-link>
  <router-link to="/page2">第二頁</router-link>     
</div><!-- 結果在此顯示 -->
<router-view />
</div><script>
const router = new VueRouter({
  routes: [
    {
      path: '/',
      component: home
    },
    {
      path: '/page2',
      component: page2
    }
  ]
});

const vm = new Vue({
  router,
  data() {
    return {
    }
  },
  methods: {
    goBack() { // 回上一頁
      window.history.length > 1
        ? this.$router.go(-1)
        : this.$router.push('/')
    }
  }      
}).$mount('#app-ru2')
</script>
首頁内容
第二頁
← 首頁 第二頁
3.1  動態路由
路由可以用來傳遞參數

在 routes 中加入參數 /:xxx
在 router-link 中傳遞參數值
在頁面中透過 $route.params.xxx 顯示參數
首頁 第二頁
首頁内容 第二頁 ← 首頁 第二頁 3.1 動態路由 路由可以用來傳遞參數 在 routes 中加入參數 /:xxx 在 router-link 中傳遞參數值 在頁面中透過 $route.params.xxx 顯示參數

動態路由

路由可以用來傳遞參數

  1. 在 routes 中加入參數 /:xxx
  2. 在 router-link 中傳遞參數值
  3. 在頁面中透過 $route.params.xxx 顯示參數
In [12]:
<!--首頁 -->
<style></style>

<template id="home">
  <div>首頁内容</div>
</template>

<script>
  const home = {
    template: '#home',
    data() {
      return {
      }
    },
    methods: {
    }
  };
</script>

<!-- page2 -->
<style></style>

<template id="page2">
  <div>
    <div>第二頁</div>
    {{ $route.params.id }}
  </div>  
</template>

<script>
  const page2 = {
    template: '#page2'
  };
</script>

<!-- 應用程式 -->
<div id="app-ru3">
  <div>
    <!-- 路由鏈結 -->
    <router-link to="/">首頁</router-link>
    <router-link to="/page2/傳進來的參數">第二頁</router-link>     
  </div>

  <!-- 結果在此顯示 -->
  <router-view />
</div>

<script>
  const router = new VueRouter({
    routes: [
      {
        path: '/',
        component: home
      },
      {
        path: '/page2/:id',
        component: page2
      }
    ]
  });

  const vm = new Vue({
    router,
    data() {
      return {
      }
    }
  }).$mount('#app-ru3')
</script>
首頁 第二頁

改用 props 來傳遞參數

  1. 在 routes 中加入參數 /:xxx 及 props:true
  2. 在頁面元件中加入 props:['xxx'],
  3. 在 router-link 中傳遞參數值
  4. 在頁面中使用 xxx 參數
In [13]:
<!--首頁 -->
<style></style>

<template id="home">
  <div>首頁内容</div>
</template>

<script>
  const home = {
    template: '#home',
    data() {
      return {
      }
    },
    methods: {
    }
  };
</script>

<!-- page2 -->
<style></style>

<template id="page2">
  <div>
    <div>第二頁</div>
    {{ par }}
  </div>  
</template>

<script>
  const page2 = {
    props:['par'],
    template: '#page2'
  };
</script>

<!-- 應用程式 -->
<div id="app-ru4">
  <div>
    <!-- 路由鏈結 -->
    <router-link to="/">首頁</router-link>
    <router-link to="/page2/傳遞的參數">第二頁</router-link>     
  </div>

  <!-- 結果在此顯示 -->
  <router-view />
</div>

<script>
  const router = new VueRouter({
    routes: [
      {
        path: '/',
        component: home
      },
      {
        props:true
        path: '/page2/:par',
        component: page2,
      }
    ]
  });

  const vm = new Vue({
    router,
    data() {
      return {
      }
    }
  }).$mount('#app-ru4')
</script>
首頁 第二頁

傳遞多個參數

  1. 在 routes 中加入參數 name:'page2' 及 props:true
  2. 在頁面元件中加入 props:['par1','par2'] <router-link :to="{name:'page2', params:{par1: parameter, par2:'第2個參數'}}"> </router-link>
  3. 在 router-link 中透過 to 傳遞參數值
  4. 在頁面中使用 par1 及 par2 參數
In [16]:
<!--首頁 -->
<style></style>

<template id="home">
  <div>首頁内容</div>
</template>

<script>
  const home = {
    template: '#home',
    data() {
      return {
      }
    },
    methods: {
    }
  };
</script>

<!-- page2 -->
<style></style>

<template id="page2">
  <div>
    <div>第二頁</div>
    {{ par1 }}
    {{ par2 }}
  </div>  
</template>

<script>
  const page2 = {
    props:['par1','par2'],
    template: '#page2'
  };
</script>

<!-- 應用程式 -->
<div id="app-ru5">
  <div>
    <!-- 路由鏈結 -->
    <router-link to="/">首頁</router-link>
    <router-link :to="{name:'page2', params:{par1: '第1個參數', par2:'第2個參數'}}">第二頁</router-link>     
  </div>

  <!-- 結果在此顯示 -->
  <router-view />
</div>

<script>
  const router = new VueRouter({
    routes: [
      {
        path: '/',
        component: home
      },
      {
        props:true
        path: '/page2',
        name: 'page2',
        component: page2,
      }
    ]
  });

  const vm = new Vue({
    router,
    data() {
      return {
      }
    }
  }).$mount('#app-ru5')
</script>
首頁 第二頁

具名路由
在路由中加入 name 屬性,以方便動態路由的呼叫。同時路徑只會出現在 routes 中,方便日後的維護。

<script>
  const router = new VueRouter({
    routes: [
      {
        path: '/',
        name: 'home',
        component: home
      },
      {
        path: '/page2',
        name: 'page2',
        component: page2,
      }
    ]
  });
</script>

鏈接的 HTML 語法:

<router-link to="/">首頁</router-link>
<router-link :to="{name: 'home'}">首頁</router-link>
<router-link :to="{page2: 'home', params: {par: '傳遞的參數'}}">第二頁</router-link>

鏈接的 Javascript 語法:

    router.push({name: 'home'});
    router.push({page2: 'home', params: {par: '傳遞的參數'}});

程式化路由
如果要傳遞複雜的參數,則可以透過 $router.push()$router.replace() 來達成程式化路由

  • replace() 方式不會記錄到瀏覽器的 history
In [14]:
<!--首頁 -->
<style></style>

<template id="home">
  <div>首頁内容</div>
</template>

<script>
  const home = {
    template: '#home',
    data() {
      return {
      }
    },
    methods: {
    }
  };
</script>

<!-- page2 -->
<style></style>

<template id="page2">
  <div>
    <div>第二頁</div>
    {{ par }}
  </div>  
</template>

<script>
  const page2 = {
    props:['par'],
    template: '#page2'
  };
</script>

<!-- 應用程式 -->
<div id="app-ru5">
  <div>
    <!-- 路由鏈結 -->
    <router-link to="/">首頁</router-link>
    
    <!-- 程式化路由傳遞參數的3種方式 -->
    <router-link to="/page2/傳遞的參數">第二頁</router-link>     
    <router-link :to="{page2: 'home', params: {par: '傳遞的參數'}}">第二頁</router-link>     
        
    <a href="#" @click="$router.push({
        name:'page2',
        params: { par: '傳遞的參數'}
      })">第二頁</a>
  </div>

  <!-- 結果在此顯示 -->
  <router-view />
</div>

<script>
  const router = new VueRouter({
    routes: [
      {
        path: '/',
        component: home
      },
      {
        props:true,
        path: '/page2/:par',
        name: 'page2',
        component: page2,
      }
    ]
  });

  const vm = new Vue({
    router,
    data() {
      return {
      }
    }
  }).$mount('#app-ru5')
</script>
首頁 第二頁 第二頁 第二頁

多視圖(具名視圖)

但具有多個 <router-view></router-view> 視圖時,可以指定視圖名稱,並分別載入不同元件,視圖的預設名稱為 default。

  • 新增 homeII 頁面
  • 新增 <router-view name="partII"></router-view>
  • routes[] 中將 component:home 改為 components: { ... }
In [15]:
<!--首頁 -->
<style></style>

<template id="home">
  <div>首頁内容</div>
</template>

<script>
  const home = {
    template: '#home',
    data() {
      return {
      }
    },
    methods: {
    }
  };
</script>

<!-- page2 -->
<style></style>

<template id="page2">
  <div>
    <div>第二頁</div>
    {{ par }}
  </div>  
</template>

<script>
  const page2 = {
    props:['par'],
    template: '#page2'
  };
</script>

<!-- 應用程式 -->
<div id="app-ru5">
  <div>
    <!-- 路由鏈結 -->
    <router-link to="/">首頁</router-link>
    
    <!-- 程式化路由傳遞參數的3種方式 -->
    <router-link to="/page2/傳遞的參數">第二頁</router-link>     
    <router-link :to="{page2: 'home', params: {par: '傳遞的參數'}}">第二頁</router-link>     
        
    <a href="#" @click="$router.push({
        name:'page2',
        params: { par: '傳遞的參數'}
      })">第二頁</a>
  </div>

  <!-- 結果在此顯示 -->
  <router-view />
</div>

<script>
  const router = new VueRouter({
    routes: [
      {
        path: '/',
        component: home
      },
      {
        props:true,
        path: '/page2/:par',
        name: 'page2',
        component: page2,
      }
    ]
  });

  const vm = new Vue({
    router,
    data() {
      return {
      }
    }
  }).$mount('#app-ru5')
</script>
首頁 第二頁 第二頁 第二頁
🏠