I tried the VueJS tutorial!

I tried the VueJS tutorial!

Overview

I tried VueJS's "Let's learn while making a tutorial to-do list!". (Lol) I've added some arrangements.

Deliverables

https://github.com/Akira-Demizu/vue-tutorials

environment

The environment used docker + vue-cli. Please try to build it referring to the following.

-Simple vue.js environment construction with docker-compose

Constitution

project
├── docker
│   └── web
│       └── Dockerfile
├── docker-compose.yml
└── server
    └── src

procedure

1. Various settings

Since we are using vue-cli this time, the configuration is different from the original tutorial.

CSS preparation

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;
}

Edit App.vue

--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>

Creating a component

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>

Routing settings

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
    }
  ]
})

2. Create a table for the list

<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>

3. List rendering

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 ( tag in this case).

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 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">
        <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>

4. Get form input value

--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>

5. Automation of storage and retrieval

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.

Creating a plugin to operate 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: {
  // ...
}

6. State change and deletion process

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>

7. List filtering function

We will add a "narrowing function" that displays only a list of specific working conditions.

Creating a selection form

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
    }
  },
  // ...
}

Implementation of narrowing function

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">

8. Character string conversion process

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>

Complete vue

<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>

reference

-Let's learn while making a to-do list!

Recommended Posts

I tried the VueJS tutorial!
I tried the Docker tutorial!
[I tried] Spring tutorial
I tried the FizzBuzz problem
I tried Angular tutorial + SpringBoot + PostgreSQL
I tried to explain the method
I tried the Java framework "Quarkus"
[Rails] I tried deleting the application
I tried to summarize the methods used
I tried Spring.
I tried tomcat
I tried youtubeDataApi.
I tried to summarize the Stream API
I tried refactoring ①
I tried the AutoValue library in Intellij
I tried FizzBuzz.
I tried JHipster 5.1
I tried using Docker for the first time
I rewrote the Rails tutorial test with RSpec
I tried to organize the session in Rails
[API] I tried using the zip code search API
I tried touching Docker for the first time
I tried to set tomcat to run the Servlet.
I tried using the profiler of IntelliJ IDEA
I tried running Autoware
I tried using Gson
I tried QUARKUS immediately
I tried using TestNG
I tried Spring Batch
I tried using Galasa
I tried node-jt400 (Programs)
I tried node-jt400 (execute)
I tried node-jt400 (Transactions)
I tried to organize the cases used in programming
I tried using the Server Push function of Servlet 4.0
I tried to summarize the state transition of docker
I tried to decorate the simple calendar a little
[Rails] I tried playing with the comment send button
05. I tried to stub the source of Spring Boot
I tried to reduce the capacity of Spring Boot
I tried the new feature profiler of IntelliJ IDEA 2019.2.
I tried using the Migration Toolkit for Application Binaries
I tried installing the Docker Integration plugin in IntelliJ
[WIP] I tried the configuration of Docker + Streama + NFS
I tried to implement the Euclidean algorithm in Java
I tried to implement the like function by asynchronous communication
I tried to introduce Bootstrap 4 to the Rails 6 app [for beginners]
I investigated the enclosing instance.
I tried using the GitHub repository as a library server
I tried DI with Ruby
I tried node-jt400 (IFS write)
[Rails] I tried using the button_to method for the first time
I tried to increase the processing speed with spiritual engineering
[JDBC] I tried to access the SQLite3 database from Java.
I tried to summarize the basics of kotlin and java
I summarized the collection framework.
I tried node-jt400 (SQL Update)
[Swift] I tried to implement the function of the vending machine
Let's try the S2Struts tutorial (# 3_180425)
I tried using azure cloud-init
I tried Spring State machine