---
# System prepended metadata

title: 11/17 學員操作手冊

---

# 11/17 學員操作手冊 
---

專案下載 https://ppt.cc/f0MAqx

---

### Step 01 專案使用特定的 prototype (10 min)
1. 創建一個 TodoList.vue, 只留下 template script style tag
2. 打開prototype, 將 index.html 的 ```<section class="todoapp">``` 區塊複製到 TodoList.vue 的 template
3. 將 App.vue 內的 HelloWorld 元件改為 TodoList 元件
4. assets 建立css folder, 建立 base.css & index.css
5. import base.css & index.css 到 App.vue (不是TodoList.vue)
---

### Step 02 將頁面切分成多個子元件 (5 min)
1. 建立 TitleComponent.vue
2. 命名元件, 並了解建立元件的命名規則 
3. 將 ```<h1>todos</h1>``` 貼到 template
4. 回到 TodoList.vue import TitleComponent元件

---

### Step 03 快速修改頁面上的文字 (10 min)
1. 把 TitleComponent h1 裡面的 hardcode 改成動態的 title by 雙括弧
2. 把 TodoList.vue 的 input placeholder 透過 v-bind 的方式改變內容
3. 簡化 v-bind 撰寫方式

---

### Step 04 事件繫結 Event Binding (15 min)
1. data 新增 keyinItemList: [], 幫input 加上v-model="keyinItem", 同時data 也新增 keyinItem: null
2. 透過雙括弧展示目前已可取到值
3. input 加上 v-on:keyup.enter="keyinTodo()" , 新增 methods, 新增 keyinTodo方法
4. 簡化v-on 的撰寫方式
5. keyinTodo method裡面 把資料寫入keyinItemList, 寫入後清除keyinItem 的值, 最後透過 {{keyinItemList}} 展示目前已可取到值

---

### Step 05 使用結構語法(10 min)
1. 把版面作調整, mark掉部分HTML, 注意v-for 用法, 包含後面的 :key="index"
2. 在 ```<button class="destroy"> 新增 @click="removeItem(index)" ``` 然後在 methods 新增 removeItem
3. 判斷當清單是空的時候, 隱藏最底下區塊

---

### Step 06 語法運用(15 min)

1. 改變 keyinItemList 結構, 增加 isChecked 屬性
2. 新增 computed 屬性 remaining
3. 透過樣式綁定修正 class 來達成"已完成"項目
4. 透過 v-if 去控制右下角 Clear completed, 並完成該功能
5. 透過 computed 實作 get set 完成左上角的 toggle-all
---

### Step 07 根據條件篩選結果 (15 min)

* 在 export default 外面新增 filters 方法
* data 新增 visibility 參數
* method 新增 onHashChange 方法
* 新增生命週期 mounted
* 樣式綁定被選擇的 all / active / completed
* computed 新增 filterItemList
* 將 v-for 的 keyinItemList 替換成 filterItemList

---

### Step 08 元件溝通 (10 min)
1. TodoList.vue 的 script 新增 props, template 新增一個接收 props 的變數: createAuthor 
2. props 官方推薦的正確用法 create-author
    * https://v3.vuejs.org/style-guide/
3. TodoList.vue 的 method 新增 sendToParent, 裡面使用$emit 去觸發自定義的父親事件 from-child, 
    template 新增 sendToParent 方法
    App.vue 的 template 新增 @from-child 自定義事件, 然後 script新增一個 reveiveMsg 去承接 @from-child

---

### 程式碼

---

#### 1-1 
檔案: TodoList.vue
```javascript=
<template>
 
</template>
<script>
export default {
    
}
</script>
<style>

</style>
```

---

#### 1-2
檔案: TodoList.vue
```javascript=
<template>
    <section class="todoapp">
        <header class="header">
            <h1>todos</h1>
            <input class="new-todo" placeholder="What needs to be done?" autofocus>
        </header>
        <!-- This section should be hidden by default and shown when there are todos -->
        <section class="main">
            <input class="toggle-all" type="checkbox">
            <label for="toggle-all">Mark all as complete</label>
            <ul class="todo-list">
                <!-- These are here just to show the structure of the list items -->
                <!-- List items should get the class `editing` when editing and `completed` when marked as completed -->
                <li class="completed">
                    <div class="view">
                        <input class="toggle" type="checkbox" checked>
                        <label>Taste JavaScript</label>
                        <button class="destroy"></button>
                    </div>
                    <input class="edit" value="Create a TodoMVC template">
                </li>
                <li>
                    <div class="view">
                        <input class="toggle" type="checkbox">
                        <label>Buy a unicorn</label>
                        <button class="destroy"></button>
                    </div>
                    <input class="edit" value="Rule the web">
                </li>
            </ul>
        </section>
        <!-- This footer should hidden by default and shown when there are todos -->
        <footer class="footer">
            <!-- This should be `0 items left` by default -->
            <span class="todo-count"><strong>0</strong> item left</span>
            <!-- Remove this if you don't implement routing -->
            <ul class="filters">
                <li>
                    <a class="selected" href="#/">All</a>
                </li>
                <li>
                    <a href="#/active">Active</a>
                </li>
                <li>
                    <a href="#/completed">Completed</a>
                </li>
            </ul>
            <!-- Hidden if no completed items are left ↓ -->
            <button class="clear-completed">Clear completed</button>
        </footer>
    </section>
</template>
<script>
export default {
    
}
</script>
<style>

</style>
```

---

#### 1-3
檔案: APP.vue
```javascript=
<template>
  <img alt="Vue logo" src="./assets/logo.png"> ==>移除
  <HelloWorld msg="Welcome to Your Vue.js App"/> ==> 移除
</template>

<script>
import HelloWorld from './components/HelloWorld.vue' ==>移除

export default {
  name: 'App',
  components: {
    HelloWorld ==> 移除
  }
}
</script>
```
<br/>

檔案: TodoList.vue

```javascript=
<template>
    <section class="todoapp">
        <header class="header">
            <h1>todos</h1>
            <input class="new-todo" placeholder="What needs to be done?" autofocus>
        </header>
        <!-- This section should be hidden by default and shown when there are todos -->
        <section class="main">
            <input class="toggle-all" type="checkbox">
            <label for="toggle-all">Mark all as complete</label>
            <ul class="todo-list">
                <!-- These are here just to show the structure of the list items -->
                <!-- List items should get the class `editing` when editing and `completed` when marked as completed -->
                <li class="completed">
                    <div class="view">
                        <input class="toggle" type="checkbox" checked>
                        <label>Taste JavaScript</label>
                        <button class="destroy"></button>
                    </div>
                    <input class="edit" value="Create a TodoMVC template">
                </li>
                <li>
                    <div class="view">
                        <input class="toggle" type="checkbox">
                        <label>Buy a unicorn</label>
                        <button class="destroy"></button>
                    </div>
                    <input class="edit" value="Rule the web">
                </li>
            </ul>
        </section>
        <!-- This footer should hidden by default and shown when there are todos -->
        <footer class="footer">
            <!-- This should be `0 items left` by default -->
            <span class="todo-count"><strong>0</strong> item left</span>
            <!-- Remove this if you don't implement routing -->
            <ul class="filters">
                <li>
                    <a class="selected" href="#/">All</a>
                </li>
                <li>
                    <a href="#/active">Active</a>
                </li>
                <li>
                    <a href="#/completed">Completed</a>
                </li>
            </ul>
            <!-- Hidden if no completed items are left ↓ -->
            <button class="clear-completed">Clear completed</button>
        </footer>
    </section>
</template>
<script>
export default {
    name: 'TodoList'
}
</script>
<style>

</style>
```
---

#### 2-1
檔案: TitleComponent.vue
```javascript=
<template>
    
</template>
<script>

</script>
<style>

</style>
```

---

#### 2-2
檔案: TitleComponent.vue
```javascript=
<script>
export default {
    name: 'TitleComponent'
}
</script>
```

---

#### 2-3
檔案: TitleComponent.vue
```javascript=
<template>
    <h1>Todos</h1>
</template>
```

---

#### 2-4
檔案: TodoList.vue
```javascript=
<template>
    <section class="todoapp">
        <header class="header">
            <TitleComponent></TitleComponent>
            <input class="new-todo" placeholder="What needs to be done?" autofocus>
        </header>
        .
        .
        .
        .
            
    </section>
</template>
<script>
import TitleComponent from './TitleComponent.vue'

export default {
    name: 'TodoList',
    components: {
        TitleComponent
    }
}
</script>
```

---

#### 3-1
檔案: TitleComponent.vue
```javascript=
<template>
    <h1>{{title}}</h1>
</template>
<script>
export default {
    name: 'TitleComponent',
    data() {
        return {
            title: 'Chris Todos'
        }
    }
}
</script>
```

---


#### 3-2
檔案: TodoList.vue
```javascript=
<template>
    <section class="todoapp">
        <header class="header">
            <TitleComponent></TitleComponent>
            <input class="new-todo" v-bind:placeholder="thePlaceholder" autofocus>
        </header>
        .
        .
        .
<template>
        
<script>
import TitleComponent from './TitleComponent.vue'

export default {
    name: 'TodoList',
    components: {
        TitleComponent
    },
    data() {
        return {
            thePlaceholder: '請輸入代辦事項'
        }
    }
}
</script>
```        

---

#### 3-3
檔案: TodoList.vue
```javascript=
<template>
    <section class="todoapp">
        <header class="header">
            <TitleComponent></TitleComponent>
            <input class="new-todo" :placeholder="thePlaceholder" autofocus>
        </header>
        .
        .
        .
<template>
```   

---

#### 4-1
檔案: TodoList.vue

```javascript=
<template>
    <section class="todoapp">
        <header class="header">
            <TitleComponent></TitleComponent>
            <input class="new-todo" :placeholder="thePlaceholder" v-model="keyinItem" autofocus>
        </header>
        .
        .
        .
        .
</template>

export default {
    name: 'TodoList',
    components: {
        TitleComponent
    },
    data() {
        return {
            thePlaceholder: '請輸入代辦事項',
            keyinItemList: [],
            keyinItem: null
        }
    }
}
```

---

#### 4-3 
檔案: TodoList.vue
```javascript=
<template>
    <section class="todoapp">
        <header class="header">
            <TitleComponent></TitleComponent>{{keyinItem}}
            <input class="new-todo" :placeholder="thePlaceholder" v-model="keyinItem" v-on:keyup.enter="keyinTodo()" autofocus>
        </header>
        .
        .
        .
        .
</template>

<script>
import TitleComponent from './TitleComponent.vue'

export default {
    name: 'TodoList',
    components: {
        TitleComponent
    },
    data() {
        return {
            thePlaceholder: '請輸入代辦事項',
            keyinItemList: [],
            keyinItem: ''
        }
    },
    methods: {
        keyinTodo () {
            this.keyinItem += '好~';
        }
    },
}
</script>
```

---

#### 4-4 
檔案: TodoList.vue
```javascript=
<template>
    <section class="todoapp">
        <header class="header">
            <TitleComponent></TitleComponent>{{keyinItem}}
            <input class="new-todo" :placeholder="thePlaceholder" v-model="keyinItem" @keyup.enter="keyinTodo()" autofocus>
        </header>
        .
        .
        .
        .
</template>
```

---

#### 4-5
檔案: TodoList.vue
```javascript=
<template>
    <section class="todoapp">
        <header class="header">
            <TitleComponent></TitleComponent>{{keyinItemList}}
            <input class="new-todo" :placeholder="thePlaceholder" v-model="keyinItem" @keyup.enter="keyinTodo()" autofocus>
        </header>
        .
        .
        .
        .
</template>

<script>
        .
        .
        .
methods: {
        keyinTodo () {
            const _value = this.keyinItem && this.keyinItem.trim();
            if(_value) {
                this.keyinItemList.push(_value)
                this.keyinItem = null;
            }
            
        }
    },
    .
    .
    .
</script>
```

---

#### 5-1
檔案: TodoList.vue
```javascript=
<template>
    <section class="todoapp">
        <header class="header">
            <TitleComponent></TitleComponent>
            <input class="new-todo" :placeholder="thePlaceholder" v-model="keyinItem" @keyup.enter="keyinTodo()" autofocus>
        </header>
        <!-- This section should be hidden by default and shown when there are todos -->
        <section class="main">
            <input class="toggle-all" type="checkbox">
            <label for="toggle-all">Mark all as complete</label>
            <ul class="todo-list">
                <!-- These are here just to show the structure of the list items -->
                <!-- List items should get the class `editing` when editing and `completed` when marked as completed -->
                <!-- <li class="completed">
                    <div class="view">
                        <input class="toggle" type="checkbox" checked>
                        <label>Taste JavaScript</label>
                        <button class="destroy"></button>
                    </div>
                    <input class="edit" value="Create a TodoMVC template">
                </li> -->
                <li v-for="(item, index) in keyinItemList" :key="index">
                    <div class="view">
                        <input class="toggle" type="checkbox">
                        <label>{{item}}</label>
                        <button class="destroy"></button>
                    </div>
                    <!-- <input class="edit" value="Rule the web"> -->
                </li>
            </ul>
        </section>
        <!-- This footer should hidden by default and shown when there are todos -->
        <footer class="footer">
            <!-- This should be `0 items left` by default -->
            <span class="todo-count"><strong>0</strong> item left</span>
            <!-- Remove this if you don't implement routing -->
            <ul class="filters">
                <li>
                    <a class="selected" href="#/">All</a>
                </li>
                <li>
                    <a href="#/active">Active</a>
                </li>
                <li>
                    <a href="#/completed">Completed</a>
                </li>
            </ul>
            <!-- Hidden if no completed items are left ↓ -->
            <button class="clear-completed">Clear completed</button>
        </footer>
    </section>
</template>
<script>
import TitleComponent from './TitleComponent.vue'

export default {
    name: 'TodoList',
    components: {
        TitleComponent
    },
    data() {
        return {
            thePlaceholder: '請輸入代辦事項',
            keyinItemList: [],
            keyinItem: ''
        }
    },
    methods: {
        keyinTodo () {
            const _value = this.keyinItem && this.keyinItem.trim();
            if(_value) {
                this.keyinItemList.push(_value)
                this.keyinItem = null;
            }
            
        }
    },
}
</script>
```
---

#### 5-2
檔案: TodoList.vue
```javascript=
<template>
    .
    .
    .
    <div class="view">
        <input class="toggle" type="checkbox">
        <label>{{item}}</label>
        <button class="destroy" @click="removeItem(index)"></button>
    </div>
    .
    .
    .
</template>

<script>
    .
    .
    .
    methods: {
        keyinTodo () {
            const _value = this.keyinItem && this.keyinItem.trim();
            if(_value) {
                this.keyinItemList.push(_value)
                this.keyinItem = null;
            }
            
        },
        removeItem(index) {
            this.keyinItemList.splice(index, 1);
        }
    },
    .
    .
    .
</script>
```

---

#### 5-3
檔案: TodoList.vue
```javascript=
<template>
    .
    .
    <footer class="footer" v-if="keyinItemList.length">
    .
    .
</template>
```

---

#### 6-1
檔案: TodoList.vue
```javascript=
<template>
    .
    .
    <div class="view">
        <input class="toggle" type="checkbox">
        <label>{{item}}</label>
        <label>{{ item.text }}</label>
        <button class="destroy" @click="removeItem(index)"></button>
    </div>
    .
    .
</template>


<script>
    .
    .
    methods: {
        keyinTodo () {
            const _value = this.keyinItem && this.keyinItem.trim();
            if(_value) {
                this.keyinItemList.push({
                        text: _value,
                        isChecked: false
                    })
                this.keyinItem = null;
            }
            
        },
    .
    .
</script>
```

---

#### 6-2
檔案: TodoList.vue
```javascript=
<template>
    .
    .
    <span class="todo-count"><strong>{{ remaining }}</strong> item left</span>
    .
    .
</template>


<script>
    .
    .
    computed: {
        remaining() {
            return this.keyinItemList.filter( item => { return !item.isChecked}).length;
        }
    },
    .
    .
</script>
```

---

#### 6-3
檔案: TodoList.vue
```javascript=
<template>
.
.
.
<li v-for="(item, index) in filterItemList" :key="index" :class="{ 'completed': item.isChecked}">
    <div class="view">
        <input class="toggle" type="checkbox" v-model="item.isChecked" @click="item.isChecked=!item.isChecked">
        <label>{{ item.text }}</label>
        <button class="destroy" @click="removeItem(index)"></button>
    </div>
    <!-- <input class="edit" value="Rule the web"> -->
</li>

.
.
.
</template>
```

---

#### 6-4
檔案: TodoList.vue
```javascript=
<template>
.
.
.
<button class="clear-completed" v-if="keyinItemList.length > remaining" @click="clearCompleted()">Clear completed</button>
.
.
.
</template>

<script>
.
.
.
// methods新增以下方法
clearCompleted() {
    this.keyinItemList = this.keyinItemList.filter( item => { return !item.isChecked});
},
.
.
.
</script>
```

---

#### 6-5
檔案: TodoList.vue
```javascript=
<template>
.
.
.
    <input class="toggle-all" type="checkbox" v-model="allDone" v-if="keyinItemList.length">
.
.
.
</template>

<script>
.
.
.
//computed新增以下方法
allDone: {
    get() {
        return this.remaining === 0;
    },
    set(value) {
            this.keyinItemList.forEach((item) => {
            item.isChecked = value;
        });
    }
}
.
.
.
</script>
```

---

#### 7
檔案: TodoList.vue
```javascript=
<template>
    <section class="todoapp">
        <header class="header">
            <TitleComponent></TitleComponent>
            <input class="new-todo" :placeholder="thePlaceholder" v-model="keyinItem" @keyup.enter="keyinTodo()" autofocus>
        </header>
        <!-- This section should be hidden by default and shown when there are todos -->
        <section class="main">
            <input class="toggle-all" type="checkbox" v-model="allDone" v-if="keyinItemList.length">
            <label for="toggle-all">Mark all as complete</label>
            <ul class="todo-list">
                <!-- These are here just to show the structure of the list items -->
                <!-- List items should get the class `editing` when editing and `completed` when marked as completed -->
                <!-- <li class="completed">
                    <div class="view">
                        <input class="toggle" type="checkbox" checked>
                        <label>Taste JavaScript</label>
                        <button class="destroy"></button>
                    </div>
                    <input class="edit" value="Create a TodoMVC template">
                </li> -->
                <li v-for="(item, index) in filterItemList" :key="index" :class="{ 'completed': item.isChecked}">
                    <div class="view">
                        <input class="toggle" type="checkbox" v-model="item.isChecked" @click="item.isChecked=!item.isChecked">
                        <label>{{ item.text }}</label>
                        <button class="destroy" @click="removeItem(index)"></button>
                    </div>
                    <!-- <input class="edit" value="Rule the web"> -->
                </li>
            </ul>
        </section>
        <!-- This footer should hidden by default and shown when there are todos -->
        <footer class="footer" v-if="keyinItemList.length">
            <!-- This should be `0 items left` by default -->
            <span class="todo-count"><strong>{{ remaining }}</strong> item left</span>
            <!-- Remove this if you don't implement routing -->
            <ul class="filters">
                <li>
                    <a href="#/all" :class="{ selected: visibility == 'all' }">All</a>
                </li>
                <li>
                    <a href="#/active" :class="{ selected: visibility == 'active' }">Active</a>
                </li>
                <li>
                    <a href="#/completed" :class="{ selected: visibility == 'completed' }">Completed</a>
                </li>
            </ul>
            <!-- Hidden if no completed items are left ↓ -->
            <button class="clear-completed" v-if="keyinItemList.length > remaining" @click="clearCompleted()">Clear completed</button>
        </footer>
    </section>
</template>


<script>
import TitleComponent from './TitleComponent.vue'

const filters = {
    all: function(todos) {
        return todos;
    },
    active: function(todos) {
        return todos.filter((todo)=> {
            return !todo.isChecked;
        });
    },
    completed: function(todos) {
        return todos.filter((todo)=> {
            return todo.isChecked;
        });
    }
};

export default {
    name: 'TodoList',
    components: {
        TitleComponent
    },
    data() {
        return {
            thePlaceholder: '請輸入代辦事項',
            keyinItemList: [],
            keyinItem: '',
            visibility: 'all'
        }
    },
    computed: {
        filterItemList() {
            return filters[this.visibility](this.keyinItemList);
        },
        remaining() {
            return this.keyinItemList.filter( item => { return !item.isChecked}).length;
        },
        allDone: {
            get() {
                return this.remaining === 0;
            },
            set(value) {
                    this.keyinItemList.forEach((item) => {
                    item.isChecked = value;
                });
            }
        }
    },
    mounted() {
        window.addEventListener("hashchange", this.onHashChange);
        this.onHashChange();
    },
    methods: {
        keyinTodo () {
            const _value = this.keyinItem && this.keyinItem.trim();
            if(_value) {
                this.keyinItemList.push({
                        text: _value,
                        isChecked: false
                    })
                this.keyinItem = null;
            }
            
        },
        removeItem(index) {
            this.keyinItemList.splice(index, 1);
        },
        clearCompleted() {
            this.keyinItemList = this.keyinItemList.filter( item => { return !item.isChecked});
        },
        onHashChange(){
            const visibility = window.location.hash.replace(/#\/?/, "");
                if (filters[visibility]) {
                    this.visibility = visibility;
                } else {
                    window.location.hash = "";
                    this.visibility = "all";
                }
        }
    },
}
</script>
```

---

#### 8-1

檔案: App.vue
```javascript=
<template>
  <TodoList :createAuthor="author"></TodoList>
</template>

<script>
import TodoList from './components/TodoList.vue'

export default {
  name: 'App',
  components: {
    TodoList
  },
  data() {
    return {
      author: 'Chris Chiang'
    }
  },
}
</script>
```



檔案: TodoList.vue
```javascript=
<template>
.
.
.
    <footer class="info">
      <p>Written by {{createAuthor}}</p>
    </footer>
</template>


<script>
    .
    .
    components: {
        TitleComponent
    },
    props: ["createAuthor"],
    data() {
    .
    .
</script>
```

---

#### 8-2

檔案: App.vue
```javascript=
<template>
  <TodoList :create-author="author"></TodoList>
</template>
```

檔案: TodoList.vue
```javascript=
<script>
.
.
.
    components: {
        TitleComponent
    },
    props: {
        createAuthor: {
            type: String
        }
    },
    data() {
.
.
.
</script>

```

---

#### 8-3

檔案: App.vue
```javascript=
<template>
  <TodoList :create-author="author" @from-child="receiveMsg"></TodoList>
</template>

<script>
  .
  .
  .
  methods: {
    receiveMsg(val) {
      alert(val);
    }
  },
  .
  .
  .
</script>
```

檔案: TodoList.vue
```javascript=
<template>
.
.
.
<footer class="info">
      <p @click="sendToParent()">Written by {{createAuthor}}</p>
    </footer>
</template>

<script>
.
.
.
        sendToParent() {
            this.$emit('fromChild', 'Hello papa');
        }
.
.
.
</script>
```