Todo - Application

by ti2005

HTML

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
<script src="https://unpkg.com/vue"></script>
<div id="app">

  <h2>{{ title }}</h2>
  <ul class="nav nav-tabs" id="myTab" role="tablist">
    <li class="nav-item">
      <a class="nav-link active" id="todo-tab" data-toggle="tab" href="#todo" role="tab" aria-controls="todo" aria-selected="true">Todo</a>
    </li>
    <li class="nav-item">
      <a class="nav-link" id="user-tab" data-toggle="tab" href="#user" role="tab" aria-controls="user" aria-selected="false">Users</a>
    </li>
  </ul>

  <!-- Tab panes -->
  <div class="tab-content">
    <div class="tab-pane active" id="todo" role="tabpanel" aria-labelledby="todo-tab">
      <input class="form-control new-todo" v-model="newTodo" v-on:keyup.enter="onPressEnter" placeholder="New Todo (Type and press 'enter' key)" @change="checkExistOrNot" />
      <small v-if="duplicateFound" class="error-message">Todo already exists.</small>
      <ol>
        <li v-for="(todo, index) in todos" v-if="checkPropertyExists(todo, 'completed')" v-bind:class="index === 0 ? newItemCss : ''">
          <span v-if="!todo.isEdit" v-bind:class="todo.completed ? 'completed' : ''">
        {{todo.title}}</span>
          <input class="form-control form-control-sm" v-if="todo.isEdit" v-model="todo.title" />
          <button @click="changeStatus(todo)" class="btn btn-sm" v-bind:class="todo.completed ? 'btn-warning' : 'btn-success'">
          {{todo.completed ? 'Reopen' : 'Done'}}
        </button>
          <button v-if="!todo.isEdit && !todo.completed" class="btn btn-sm btn-primary" @click="editTodo(todo, true)">
          Edit
        </button>
          <button v-if="todo.isEdit"...

CSS

.completed {
  text-decoration: line-through;
}

li {
  padding: 5px;
  border-bottom: 1px solid #ccc;
}

li:last-child {
  border-bottom: none;
}

.error-message {
  color: red;
  padding-left: 15px;
  position: relative;
  top: -5px;
  font-size: 11px;
}

.new-todo {
  margin: 5px;
  width: 98%;
}

.li-added-css {
  background-color: rgba(255, 235, 59, 0.55);
}

JavaScript

new Vue({
  el: '#app',
  data: {
    title: '',
    todos: [{
        "userId": 1,
        "id": 1,
        "title": "delectus aut autem",
        "completed": false
      },
      {
        "userId": 1,
        "id": 2,
        "title": "quis ut nam facilis et officia qui",
        "completed": true
      },
      {
        "userId": 1,
        "id": 3,
        "title": "fugiat veniam minus",
        "completed": false
      },
      {
        "userId": 1,
        "id": 4,
        "title": "et porro tempora",
        "completed": true
      },
      {
        "userId": 3,
        "id": 45,
        "title": "velit soluta adipisci molestias reiciendis harum"
      },
      {
        "userId": 1,
        "id": 5,
        "title": "laboriosam mollitia et enim quasi adipisci quia provident illum",
        "completed": false
      }
    ],
    users: [],
    duplicateFound: false,
    newItemCss: ''
  },
  created() {
    fetch('https://jsonplaceholder.typicode.com/users')
      .then(response => response.json())
      .then(json => this.users = json)
  },
  methods: {
    checkPropertyExists(todo, key) {
      return (key in todo) ? true : false;
    },
    changeStatus(todo) {
      todo.completed = !todo.completed;
    },
    editTodo(todo, value) {
      this.$set(todo, 'isEdit', value)
    },
    checkExistOrNot() {
      const idx = this.todos.findIndex(o => o.title === this.newTodo.trim());
      this.duplicateFound = (idx === -1) ? false : true;
    },
    onPressEnter() {
      const idx = this.todos.findIndex(o => o.title === this.newTodo);
      if (idx === -1) {
        this.todos.unshift({
          "userId": 1,
          "id": new Date().getTime(),
          "title": this.newTodo,
          "completed": false
        });
        this.newItemCss = 'li-added-css';
        setTimeout(() => {
          this.newItemCss = '';
        }, 1000);
      }
    },

  }
})