목표
선택된 필터에 타입에 맞게 다른 리스트를 보여준다.
실습
하단에 아래와 같이 아이템의 갯수와 아이템을 필터링 할 수 있는 필터들이 있습니다.
먼저 필터를 구성해보겠습니다.
filter 를 구성하기 위한 state 를 추가합니다.
filter 의 종류는 전체, 진행중, 진행완료 세가지로 이뤄집니다.
Copy // components/Footer.vue
<template>
<footer class="footer">
<span class="todo-count">
<strong>10</strong> items left
</span>
<ul class="filters">
<li v-for="(filter, idx) in filters" :key="idx">
<a href="javascript:;" class="selected">{{ filter }}</a>
</li>
</ul>
</footer>
</template>
<script>
export default {
data() {
return {
filters: ["All", "Active", "Completed"]
};
}
};
</script>
아래와 같이 filter 들이 추가되는 것을 볼 수 있습니다.
selected
라는 class 는 선택된 filter 에만 적용이 되어야 합니다.
선택된 filter 값은 누가 들고 있는것이 좋을까요?
filter 의 역할을 생각해보면 간단합니다. 선택된 filter 에 따라서 todo list 에 변화가 일어납니다.
todo list 는 App.vue
에서 가지고 있는 state 입니다. 해당 state 에 변화를 주어야되니 App.vue 에 선택된 filter 값을 들고 있어야합니다.
Copy // App.vue
export default {
// ...,
data() {
return {
todos: [
{
id: new Date(),
text: "Vue 공부하기",
isDone: true
},
{
id: new Date() + 1,
text: "치킨 먹기",
isDone: false
}
],
filterType: 'All' // 기본 필터는 All 로 지정하겠습니다.
}
},
// ...
}
이제 이 필터값을 Footer.vue
에게 넘겨주면됩니다.
Footer.vue
에서는 이 필터 값을 바탕으로 선택된 필터인지 판단합니다.
Copy // App.vue
<template>
<div id="app">
<section class="todoapp">
<Header @insertTodo="insertTodo" />
<Todo
:todos="todos"
@removeTodo="removeTodo"
@updateDone="updateDone"
@updateTodo="updateTodo"
/>
<Footer :filterType="filterType"/>
</section>
</div>
</template>
App.vue 에서 넘겨받은 filterType 을 바탕으로 selected class 를 제어합니다.
Copy // components/Footer.vue
<template>
<footer class="footer">
<span class="todo-count">
<strong>10</strong> items left
</span>
<ul class="filters">
<li v-for="(filter, idx) in filters" :key="idx">
<a href="javascript:;" :class="{selected: filterType === filter}">{{ filter }}</a>
</li>
</ul>
</footer>
</template>
<script>
export default {
props: {
filterType: { type: String, default: 'All'}
},
data() {
return {
filters: ["All", "Active", "Completed"]
};
}
};
</script>
이제 눌린 filter 로 filterType 을 변경해보겠습니다.
handleFilterType
함수는 type 을 받아 filterType 을 업데이트 합니다.
Copy // App.vue
<template>
<div id="app">
<section class="todoapp">
<Header @insertTodo="insertTodo" />
<Todo
:todos="todos"
@removeTodo="removeTodo"
@updateDone="updateDone"
@updateTodo="updateTodo"
/>
<Footer :filterType="filterType" @onFilterType="handleFilterType"/>
</section>
</div>
</template>
export default {
// ...
methods: {
// ...,
handleFilterType(type) {
this.filterType = type
}
}
};
</script>
Footer.vue
에서는 filter 가 클릭되었을 때 눌린 filter 의 값을 onFilterType
으로 넘겨줍니다.
Copy // components/Footer.vue
<template>
<footer class="footer">
<span class="todo-count">
<strong>10</strong> items left
</span>
<ul class="filters">
<li v-for="(filter, idx) in filters" :key="idx" @click="handleFilterType(filter)">
<a href="javascript:;" :class="{selected: filterType === filter}">{{ filter }}</a>
</li>
</ul>
</footer>
</template>
<script>
export default {
props: {
filterType: { type: String, default: 'All'}
},
data() {
return {
filters: ["All", "Active", "Completed"]
};
},
methods: {
handleFilterType (type) {
this.$emit('onFilterType', type)
}
}
};
</script>
이제는 바뀐 filterType 에 따라 다른 리스트만 보여주면 됩니다.
computed
를 이용해서 필터링을 할거에요
Active: isDone 이 false 인 완료되지 않은 리스트
Completed: isDone 이 true 인 완료된 리스트
그리고 Todo.vue 로 넘겨주던 데이터를 필터링된 리스트로 내려주면됩니다.
아이템 갯수 또한 필터링된 리스트의 사이즈로 내려줍니다
Copy // App.vue
<template>
<div id="app">
<section class="todoapp">
<Header @insertTodo="insertTodo" />
<Todo
:todos="filteredList"
@removeTodo="removeTodo"
@updateDone="updateDone"
@updateTodo="updateTodo"
/>
<Footer
:filterType="filterType"
:size="filteredList.length"
@onFilterType="handleFilterType"
/>
</section>
</div>
</template>
export default {
// ...
computed: {
filteredList () {
switch(this.filterType) {
case "All": {
return this.todos
}
case "Active": {
return this.todos.filter((todo) => !todo.isDone)
}
case "Completed": {
return this.todos.filter((todo) => todo.isDone)
}
default: {
return []
}
}
}
},
// ...
}
Footer.vue 에서는 size prop 을 받아서 보여줍니다.
Copy // components/Footer.vue
<template>
<footer class="footer">
<span class="todo-count">
<strong>{{ size }}</strong> items left
</span>
<ul class="filters">
<li v-for="(filter, idx) in filters" :key="idx" @click="handleFilterType(filter)">
<a href="javascript:;" :class="{selected: filterType === filter}">{{ filter }}</a>
</li>
</ul>
</footer>
</template>
<script>
export default {
props: {
filterType: { type: String, default: 'All'},
size: { type: Number, default: 0 }
},
// ...
};
</script>