Home > Mobile >  How to add edit function to todolist application
How to add edit function to todolist application

Time:11-15

I am currently working on a todolist application.

What I want to do is to add an edit function to this app as shown in the image below.

First of all, I tried to pass only this.item.name from the child component (listItem.vue) to the parent component (addItemForm.vue) to be displayed in the input field when the edit icon is clicked, but it did not work.

If anyone can help me understand what I am trying to do, please let me know.

enter image description here

here is vue file.

app.vue

<template>
  <div >
    <div >
      <h2 id="title">TodoList</h2>
      <add-item-form v-on:reloadlist="getList()" />
    </div>
    <list-view :items="items" v-on:reloadlist="getList()" />
  </div>
</template>
<script>
import addItemForm from "./addItemForm.vue";
import listView from "./listView.vue";
export default {
  components: {
    addItemForm,
    listView,
  },
  data: function () {
    return {
      items: [],
    };
  },
  methods: {
    getList() {
      axios
        .get("api/items")
        .then((response) => {
          this.items = response.data;
          console.log(this.items);
        })
        .catch((error) => {
          console.log(error);
        });
    },
  },
  created() {
    this.getList();
    console.log("create success!");
  },
};
</script>

<style scoped>
.todoListContainer {
  width: 350px;
  margin: auto;
}

.heading {
  background: #e6e6e6;
  padding: 10px;
}

#title {
  text-align: center;
}
</style>

addItemForm.vue

<template>
  <div >
    <input type="text" v-model="item.name" />
    <font-awesome-icon
      icon="plus-square"
      @click="addItem()"
      :
    />
  </div>
</template>
<script>
export default {
  data: function () {
    return {
      item: {
        name: "",
      },
    };
  },
  methods: {
    addItem() {
      if (this.item.name == "") {
        return;
      }
      axios
        .post("api/item/store", {
          item: this.item,
        })
        .then((response) => {
          if (response.status == 201) {
            this.item.name = "";
            this.$emit("reloadlist");
          }
        })
        .catch((error) => {
          console.log(error);
        });
    },
  },
};
</script>

<style scoped>
.addItem {
  display: flex;
  justify-content: center;
  align-items: center;
}

input {
  background: #f7f7f7;
  border: 0px;
  outline: none;
  padding: 5px;
  margin-right: 10px;
  width: 100%;
}

.plus {
  font-size: 20px;
}

.active {
  color: #00ce25;
}

.inactive {
  color: #999999;
}
</style>

listView.vue

<template>
  <div>
    <div v-for="(item, index) in items" :key="index">
      <list-item
        :item="item"
        
        v-on:itemchanged="$emit('reloadlist')"
      />
    </div>
  </div>
</template>
<script>
import listItem from "./listItem.vue";
export default {
  props: ["items"],
  components: {
    listItem,
  },
};
</script>

<style scoped>
.item {
  background: #e6e6e6;
  padding: 5px;
  margin-top: 5px;
}
</style>

listItem.vue

<template>
  <div >
    <input type="checkbox" @change="updateCheck()" v-model="item.completed" />
    <span :>{{
      item.name
    }}</span>
    <button @click="removeItem()" >
      <font-awesome-icon icon="trash" />
    </button>
    <button @click="editItem" >
      <font-awesome-icon icon="pencil" />
    </button>
  </div>
</template>
<script>
export default {
  props: ["item"],
  methods: {
    updateCheck() {
      axios
        .put("api/item/"   this.item.id, {
          item: this.item,
        })
        .then((response) => {
          if (response.status == 200) {
            this.$emit("itemchanged");
          }
        })
        .catch((error) => {
          console.log(error);
        });
    },
    removeItem() {
      axios
        .delete("api/item/"   this.item.id)
        .then((response) => {
          if (response.status == 200) {
            this.$emit("itemchanged");
          }
        })
        .catch((error) => {
          console.log(error);
        });
    },

    editItem() {
      const text = this.item .name;
      this.$emit('custom-click', text);
    }
  },
};
</script>

<style scoped>
.completed {
  text-decoration: line-through;
  color: #999999;
}
.itemText {
  width: 100%;
  margin-left: 20px;
}
.item {
  display: flex;
  justify-content: center;
  align-items: center;
}
.trashcan {
  background: #e6e6e6;
  border: none;
  color: #ff0000;
  outline: none;
}
.pencil {
  background: #e6e6e6;
  border: none;
  color: #1df548;
  outline: none;
}
</style>

items_table

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateItemsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('items', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->boolean('completed')->default(false);
            $table->timestamp('completed_at')->nullable();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('items');
    }
}

First of all, I tried to pass only this.item.name from the child component (listItem.vue) to the parent component (addItemForm.vue) to be displayed in the input field when the edit icon is clicked, but it did not work.

So I want the target text to appear in the input field when I first click on the edit symbol.

Postscript
enter image description here

enter image description here

enter image description here

enter image description here

Postscript2
I debugged it by placing a breakpoint at the corresponding point in chrome's developer tools. I confirmed that the value is passed up to @custom-click="$emit('custom-click', $event)" in listView.vue, but it does not seem to reach the edit function. Also, I am getting a warning error as shown in the image. enter image description here

enter image description here

enter image description here

enter image description here

PostScript3
Vue.js devtools enter image description here

enter image description here

enter image description here

enter image description here

CodePudding user response:

You emit 'custom-click' from listItem, that's good. Next is to listen to the event in the parent (listView) and re-emit to the next parent, app.vue. Once app.vue has the item, you need to pass it back down as a prop to your AddItemForm.vue component and set it as the <input> v-model.

This is basically what you need to add to each component:

listView.vue (re-emit)

<list-item
  :item="item"
  
  v-on:itemchanged="$emit('reloadlist')"
  @custom-click="$emit('custom-click', $event)"
/>

App.vue (listen to emit, find matching item in this.items, send it down as prop to addItemForm)

<div >
  <h2 id="title">TodoList</h2>
  <add-item-form v-on:reloadlist="getList()" :editItem="editItem" />
</div>
<list-view :items="items" v-on:reloadlist="getList()" @custom-click="edit" />
data: function () {
  editItem: null
},
methods: {
  edit(item) {
    this.editItem = this.items.find(i => i.name === item);
  }
}

AddItemForm.vue (set v-model using prop)

props: {
  editItem: {
    type: Object,
    default() {
      return null;
    }
  }
},
watch: {
  editItem(newItem) {
    this.item = newItem;
  }
}
  • Related