简体   繁体   中英

How can I use props, $ref and $emit in Vue-cli?

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

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.

Sandbox

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM