I tried VueJS's "Let's learn while making a tutorial to-do list!". (Lol) I've added some arrangements.
https://github.com/Akira-Demizu/vue-tutorials
The environment used docker + vue-cli
.
Please try to build it referring to the following.
-Simple vue.js environment construction with docker-compose
project
├── docker
│ └── web
│ └── Dockerfile
├── docker-compose.yml
└── server
└── src
Since we are using vue-cli this time, the configuration is different from the original tutorial.
Even the original tutorial does not explain CSS, so please copy and use it. As it says
, I will not touch on the explanation of CSS in this article, so I will copy and use CSS of the head family.
Create main.css
.
server/src/assets/css/main.css
* {
box-sizing: border-box;
}
#app {
max-width: 640px;
margin: 0 auto;
}
table {
width: 100%;
border-collapse: collapse;
}
thead th {
border-bottom: 2px solid #0099e4; /*#d31c4a */
color: #0099e4;
}
th,
th {
padding: 0 8px;
line-height: 40px;
}
thead th.id {
width: 50px;
}
thead th.state {
width: 100px;
}
thead th.button {
width: 60px;
}
tbody td.button, tbody td.state {
text-align: center;
}
tbody tr td,
tbody tr th {
border-bottom: 1px solid #ccc;
transition: all 0.4s;
}
tbody tr.done td,
tbody tr.done th {
background: #f8f8f8;
color: #bbb;
}
tbody tr:hover td,
tbody tr:hover th {
background: #f4fbff;
}
button {
border: none;
border-radius: 20px;
line-height: 24px;
padding: 0 8px;
background: #0099e4;
color: #fff;
cursor: pointer;
}
--Delete default logo image
--Set to read main.css
server/src/App.vue
<template>
<div id="app">
<router-view/>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
@import "./assets/css/main.css";
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
Let's create server/src/components/Tutorial.vue
.
We will proceed based on the following Tutorial.vue
.
Tutorial.vue
<template>
<p>test</p>
</template>
<script>
export default {
name: 'Tutorial',
data () {}
}
</script>
<style scoped>
</style>
Change from the default HelloWold
to the Tutorial
component.
server/src/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import Tutorial from '@/components/Tutorial'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Tutorial',
component: Tutorial
}
]
})
<template>
<div>
<table>
<!--Table header-->
<thead>
<tr>
<th class="id">ID</th>
<th class="comment">comment</th>
<th class="state">State</th>
<th class="button">-</th>
</tr>
</thead>
<tbody>
<!-- [1]here<tr>I want to repeatedly display ToDo elements line by line-->
</tbody>
</table>
</div>
</template>
Register an empty array for ToDo list data in the data option.
server/src/components/Tutorial.vue
<template>
<!-- ... -->
</template>
<script>
export default {
name: 'Tutorial',
data () {
return {
todos: []
}
}
}
</script>
To repeat the number of array elements (todos
), use the v-for directive for the target tag (
server/src/components/Tutorial.vue
<template>
<div>
<table>
<!--Table header-->
<thead>
<tr>
<th class="id">ID</th>
<th class="comment">comment</th>
<th class="state">State</th>
<th class="button">-</th>
</tr>
</thead>
<tbody>
<!--here<tr>Repeatedly display ToDo elements line by line-->
<tr v-for="item in todos" v-bind:key="item.id">
<!--Element information-->
</tr>
</tbody>
</table>
</div>
</template>
You will be able to use the properties of each element of the todos data inside the tag that describes the v-for. Let's add columns for "ID", "Comment", "Status change button", and "Delete button" inside the
server/src/components/Tutorial.vue
<template>
<div>
<table>
<!--Table header-->
<thead>
<tr>
<th class="id">ID</th>
<th class="comment">comment</th>
<th class="state">State</th>
<th class="button">-</th>
</tr>
</thead>
<tbody>
<!--here<tr>Repeatedly display ToDo elements line by line-->
<tr v-for="item in todos" v-bind:key="item.id">
<th>{{ item.id }}</th>
<td>{{ item.comment }}</td>
<td class="state">
<!--State change button mock-->
<button>{{ item.state }}</button>
</td>
<td class="button">
<!--Delete button mock-->
<button>Delete</button>
</td>
</tr>
</tbody>
</table>
</div>
</template>
--Add an add button below the table
--Add uid
to data
--Add doAdd
method
server/src/components/Tutorial.vue
<template>
<div>
<table>
<!--Table header-->
<thead>
<tr>
<th class="id">ID</th>
<th class="comment">comment</th>
<th class="state">State</th>
<th class="button">-</th>
</tr>
</thead>
<tbody>
<!--here<tr>Repeatedly display ToDo elements line by line-->
<tr v-for="item in todos" v-bind:key="item.id">
<th>{{ item.id }}</th>
<td>{{ item.comment }}</td>
<td class="state">
<!--State change button mock-->
<button>{{ item.state }}</button>
</td>
<td class="button">
<!--Delete button mock-->
<button>Delete</button>
</td>
</tr>
</tbody>
</table>
<h2>Add new work</h2>
<form class="add-form" v-on:submit.prevent="doAdd">
<!--Comment input form-->
comment<input type="text" ref="comment">
<!--Add button mock-->
<button type="submit">add to</button>
</form>
</div>
</template>
<script>
export default {
name: 'Tutorial',
data () {
return {
todos: [],
uid: 0
}
},
methods: {
//ToDo additional processing
doAdd: function (event, value) {
//See the element named with ref
var comment = this.$refs.comment
//If there is no input, do nothing and return
if (!comment.value.length) {
return
}
// {New ID,comment,Working status}
//Push the object to the current todos list
//Working state "state" is the default "working"=Created with "0"
this.todos.push({
id: this.uid++,
comment: comment.value,
state: 0
})
//Empty form element
comment.value = ''
}
}
}
</script>
When the contents of todos data change, I will try to save it to the local storage automatically. Also, when reloading the screen, get the data from the local storage.
At the head family, I added it to main.js
for the time being, but this time I would like to create it as a plug-in assuming reuse.
First, create a plugins
directory in src. Let's create todoStorage.js
in it.
server/src/plugins/todoStorage.js
// https://jp.vuejs.org/v2/examples/todomvc.html
const STORAGE_KEY = 'todos-vuejs-demo'
const todoStorage = {
fetch: function () {
return JSON.parse(
localStorage.getItem(STORAGE_KEY) || '[]'
)
},
save: function (todos) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(todos))
}
}
export default todoStorage
Import and use todoStorage
.
Get the data from the created
method when creating an instance, and save the data with the watch` method when changing the data.
server/src/components/Tutorial.vue
import todoStorage from '../plugins/todoStorage'
export default {
// ...
created () {
//Automatically fetch when creating an instance()To do
this.todos = todoStorage.fetch()
this.uid = this.todos.length
},
watch: {
//Object format when using options
todos: {
//The argument is the changed value of the property you are watching
handler: function (todos) {
todoStorage.save(todos)
},
//Nested data can also be monitored with the deep option
deep: true
}
},
// methods: {
// ...
}
Creating a method
server/src/components/Tutorial.vue
import todoStorage from '../plugins/todoStorage'
export default {
// ...
methods: {
//ToDo additional processing
doAdd: function (event, value) {
// ...
},
//State change processing
doChangeState: function (item) {
item.state = item.state ? 0 : 1
},
//Deletion process
doRemove: function (item) {
const index = this.todos.indexOf(item)
this.todos.splice(index, 1)
}
}
}
Rewrite the button mock.
server/src/components/Tutorial.vue
<template>
<div>
<table>
<tbody>
<!--here<tr>Repeatedly display ToDo elements line by line-->
<tr v-for="item in todos" v-bind:key="item.id">
<th>{{ item.id }}</th>
<td>{{ item.comment }}</td>
<td class="state">
<button v-on:click="doChangeState(item)">
{{ item.state }}
</button>
</td>
<td class="button">
<button v-on:click="doRemove(item)">
Delete
</button>
</td>
</tr>
</tbody>
</table>
...
</div>
</template>
We will add a "narrowing function" that displays only a list of specific working conditions.
Add a list of choice options.
server/src/components/Tutorial.vue
export default {
name: 'Tutorial',
data () {
return {
todos: [],
uid: 0,
options: [
{ value: -1, label: 'all' },
{ value: 0, label: 'Working' },
{ value: 1, label: 'Done' }
],
//Data to remember the value of the selected options
//Set the initial value to "-1 "that is," all "
current: -1
}
},
// ...
}
Register the method that returns the processed data in the computed option.
server/src/components/Tutorial.vue
export default {
name: 'Tutorial',
data () {
// ...
},
computed: {
computedTodos: function () {
//Data current-1 is all
//Otherwise, narrow down to those whose current and state match
return this.todos.filter(function (el) {
return this.current < 0 ? true : this.current === el.state
}, this)
}
},
}
Replace the todos part used in the v-for directive in the list display table with computedTodos.
server/src/components/Tutorial.vue
<tr v-for="item in todos" v-bind:key="item.id">
↓
<tr v-for="item in computedTodos" v-bind:key="item.id">
As a final touch, fix the "Change Status Button" label as a number.
server/src/components/Tutorial.vue
export default {
name: 'Tutorial',
data () {
// ...
},
computed: {
labels () {
return this.options.reduce(function (a, b) {
return Object.assign(a, { [b.value]: b.label })
}, {})
//Create the data processed as follows so that it can be easily found from the key
// {0: 'Working', 1: 'Done', -1: 'all'}
},
// ...
},
}
Modify Mustache to pass the labels object.
server/src/components/Tutorial.vue
<button v-on:click="doChangeState(item)">
{{ labels[item.state] }}
</button>
<template>
<div>
<label v-for="label in options" v-bind:key="label.value">
<input type="radio"
v-model="current"
v-bind:value="label.value">{{ label.label }}
</label>
<table>
<!--Table header-->
<thead>
<tr>
<th class="id">ID</th>
<th class="comment">comment</th>
<th class="state">State</th>
<th class="button">-</th>
</tr>
</thead>
<tbody>
<!--here<tr>Repeatedly display ToDo elements line by line-->
<tr v-for="item in computedTodos" v-bind:key="item.id">
<th>{{ item.id }}</th>
<td>{{ item.comment }}</td>
<td class="state">
<button v-on:click="doChangeState(item)">
{{ labels[item.state] }}
</button>
</td>
<td class="button">
<button v-on:click="doRemove(item)">
Delete
</button>
</td>
</tr>
</tbody>
</table>
<h2>Add new work</h2>
<form class="add-form" v-on:submit.prevent="doAdd">
<!--Comment input form-->
comment<input type="text" ref="comment">
<!--Add button mock-->
<button type="submit">add to</button>
</form>
</div>
</template>
<script>
import todoStorage from '../plugins/todoStorage'
export default {
name: 'Tutorial',
data () {
return {
todos: [],
uid: 0,
options: [
{ value: -1, label: 'all' },
{ value: 0, label: 'Working' },
{ value: 1, label: 'Done' }
],
//Data to remember the value of the selected options
//Set the initial value to "-1 "that is," all "
current: -1
}
},
computed: {
labels () {
return this.options.reduce(function (a, b) {
return Object.assign(a, { [b.value]: b.label })
}, {})
//Create the data processed as follows so that it can be easily found from the key
// {0: 'Working', 1: 'Done', -1: 'all'}
},
computedTodos: function () {
//Data current-1 is all
//Otherwise, narrow down to those whose current and state match
return this.todos.filter(function (el) {
return this.current < 0 ? true : this.current === el.state
}, this)
}
},
created () {
//Automatically fetch when creating an instance()To do
this.todos = todoStorage.fetch()
this.uid = this.todos.length
},
watch: {
//Object format when using options
todos: {
//The argument is the changed value of the property you are watching
handler: function (todos) {
todoStorage.save(todos)
},
//Nested data can also be monitored with the deep option
deep: true
}
},
methods: {
//ToDo additional processing
doAdd: function (event, value) {
//See the element named with ref
var comment = this.$refs.comment
//If there is no input, do nothing and return
if (!comment.value.length) {
return
}
// {New ID,comment,Working status}
//Push the object to the current todos list
//Working state "state" is the default "working"=Created with "0"
this.todos.push({
id: this.uid++,
comment: comment.value,
state: 0
})
//Empty form element
comment.value = ''
},
//State change processing
doChangeState: function (item) {
item.state = item.state ? 0 : 1
},
//Deletion process
doRemove: function (item) {
const index = this.todos.indexOf(item)
this.todos.splice(index, 1)
}
}
}
</script>
<style scoped>
</style>
-Let's learn while making a to-do list!
Recommended Posts