Reputation: 135
I've been learning Vuejs with Vue-cli.
I'm trying to figure out how props
and $emit
work.
One thing I can't figure out is that how do I handle with function
related to sevral components.
All
,Ongoing
, Done
buttons are supposed to filter list depend on each checkbox value and its color changes to green when active.
I don't know where to put filteredTodos() {}
stuff which currently I added in ListTodo.vue
, since those are related in both of Swichers.vue
and ListTodo.vue
.
My code is: Here
If somebody knows, could you tell me how?
Thanks
Upvotes: 1
Views: 1570
Reputation: 406
It would be more in this spirit of vuejs to avoid refs and having state in child components.
The app contains the items and the filter and the todolist.vue binds a props to a filtered list (computed property). The switcher takes a v-model on the filter (two way binding). Remove all is passed as a callback property.
(Edit)
App.vue
<template>
<div>
<h1>TODO</h1>
<input type="text" v-model="inputTask" placeholder="Enter your task">
<button @click="addTask">Add task</button>
<Switchers
v-model='filter'
:onRemoveAll="removeAllItem"
/>
<ListTodo :todos='filtered' :noTask='noTask' :onRemoveTask='removeTask' :onToggleStatus='toggleStatus' />
</div>
</template>
<script>
import Switchers from "./components/Switchers";
import ListTodo from "./components/ListTodo";
export default {
keyName: 'myTodoList',
components: {
Switchers,
ListTodo,
},
created() {
let keyObject =JSON.parse(localStorage.getItem(this.keyName))
if (keyObject) {
this.todos = keyObject;
}
},
data() {
return {
inputTask: '',
// filtered:'',
filter: 'all',
todos: [],
};
},
computed: {
filtered() {
this.filter; this.todos;
return this.filteredTodos()
},
id() { return this.todos.length+1 },
noTask() {
return {
'all': 'No tasks',
'ongoing': 'No ongoing tasks',
'done': 'No done tasks',
}[this.filter]
},
},
methods: {
addTask() {
if (this.inputTask==='') {
return
}
this.addTaskChild(this.inputTask);
this.inputTask=''
},
addTaskChild(inputValue) {
const todo = {id: this.id, task:inputValue, done:false}
this.todos.push(todo)
localStorage.setItem(this.keyName, JSON.stringify(this.todos));
this.filter = 'all'
},
removeAllItem() {
this.todos = []
localStorage.clear();
this.filter = 'all'
},
filteredTodos() {
return this.todos.filter(todo => {
if (this.filter === 'ongoing') {
return !todo.done;
} else if (this.filter === 'done') {
return todo.done;
} else {
return true;
}
});
},
toggleStatus(todo) {
todo.done = !todo.done
localStorage.setItem(this.keyName, JSON.stringify(this.todos));
},
removeTask(t) {
this.todos = this.todos.filter(todo => todo.id !== t.id)
},
}
};
</script>
ListTodo.vue
<template>
<div>
<p>{{todos.length}} tasks left / {{todos.length}} tasks of all</p>
<ul>
<li v-for="todo in todos" :class="{done:todo.done}" :key="todo.id">
<input type="checkbox" :checked="todo.done" @click="status(todo)">
{{ todo.task }}
<button @click="onRemoveTask(todo)">Remove task</button>
</li>
</ul>
<p v-show="todos.length===0">{{noTask}}</p>
</div>
</template>
<script>
export default {
props: {
todos: {
type: Array,
required: true
},
onRemoveTask: {
type: Function,
required: true
},
onToggleStatus: {
type: Function,
required: true
},
noTask: {
type: String,
required: true
}
},
data() {
return {
id: 1,
done:false,
};
},
methods: {
status(todo) {
this.onToggleStatus(todo)
},
},
};
</script>
Switchers.vue
<template>
<div>
<button :class="{active: filter ==='all'}" @click="set_filter('all')">All</button>
<button :class="{active: filter ==='ongoing'}" @click="set_filter('ongoing')">Ongoing</button>
<button :class="{active: filter ==='done'}" @click="set_filter('done')">Done</button>
<button @click="onRemoveAll">Remove all</button>
</div>
</template>
<script>
export default {
props: {
value: {
type: String,
required: true
},
onRemoveAll: {
type: Function,
required: true
}
},
watch: {
value: {
handler(v) {
if (this.filter !== v)
this.filter = v
},
immediate: true
}
},
data() {
return {
filter:'',
};
},
methods: {
set_filter(f) {
this.filter = f
this.$emit('input', f)
},
},
};
</script>
<style lang="scss" scoped>
.active {background: turquoise;}
</style>
Idiomatic vuejs prefers reactive properties to imperative style. In this case, we would rather keep a single copy of the todo list + filter criteria (in App) and expose computed properties for the next id, filtered list and noTask message.
Switchers
is a pure controller component: it has no state and its only role is to translate user selection to call on the App model.
ListTodo
is a view and only takes care of displaying the list of todos that it is given as a prop. It doesn't care about whether the list is filtered or not.
There are small style changes that could have been done too but they don't have anything to do with vuejs / emit, so I didn't do them.
Upvotes: 2