A hand note of Pinia journey.....
Key Note | |||
---|---|---|---|
Emoji | Description | Emoji | Description |
🌴 | Main Topic | 📌 | Regular Note |
🌿 | Paragraph | 💎 | High Value info |
📕 | Heavy Note | 🧨 | Careful this |
🍂 | Attention Note | ✋ | Stop! check the point |
🏷️ | Regular Note | 🎯 | Focus |
##Expand Table of Contents
-
Vue use
data: {}
for storing data, on the other hand pinia usestate: {}
-
Vue use
methods: {}
for updating data of local state , on the other hand pinia useactions: {}
-
Vue use
computed: {}
for returning process version data without effecting original data, on the other hand pinia usestate: {}
A Store (like Pinia) is an entity holding state and business logic that isn't bound to your Component tree. In other words, it hosts global state. It's a bit like a component that is always there and that everybody can read off and write to. It has three concepts, the state, getters and actions and it's safe to assume these concepts are the equivalent of data, computed and methods in components.
A store should contain data that can be accessed throughout your application. This includes data that is used in many places, e.g. User information that is displayed in the navbar, as well as data that needs to be preserved through pages, e.g. a very complicated multi-step form.
On the other hand, you should avoid including in the store local data that could be hosted in a component instead, e.g. the visibility of an element local to a page.
Not all applications need access to a global state, but if yours need one, Pinia will make your life easier.
🌿 Step 01: Install pinia
yarn add pinia
# or with npm
npm install pinia
🌿 Step 02: Create a pinia instance (the root store) and pass it to the app as a plugin
<!-- main.js -->
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
app.mount('#app')
🌿 Step 01: Now create components of TodoAPP.vue, TodoForm.vue, TodoList.vue.
🌿 Step 02: import TodoForm.vue, TodoList.vue. in TodoApp.Vue.
🌿 Step 03: and import TodoApp.vue in App.vue. and delete every thing of App.Vue rest.
🌿 Step 04 Store Create: create stores folder and create in the todoList.js
import { defineStore } from 'pinia'
export const useTodoListStore = defineStore('todoList', {
// State
// getters
// actions
})
🌿 Step 05: now create Global state object state: () => ({ todoList: []})
And final code will be like below,
import { defineStore } from 'pinia'
export const useTodoListStore = defineStore('todoList', {
state: () => ({
todoList: [],
})
})
🌿 Step 1: now create action: where we will add toDO item into the state,
actions: {
addTodo(item) {
this.todoList.push({item, id: this.id++})
}
}
🌿 Step 02: also add id:0 property to the state: Since we want to has id in each item while increment in pushing into the array,
import { defineStore } from 'pinia'
export const useTodoListStore = defineStore('todoList', {
state: () => ({
todoList: [],
id: 0
})
})
🌿 Step 03: and each toDo will start out as incomplete completed: false
🌿 Step 04: we can also write to action for deleting item by filtering the id on the toDo object.
deleteTodo(itemId) {
this.todoList = this.todoList.filter((object) => {
return object.id !== itemId
})
}
Final all codes:
import { defineStore } from 'pinia'
export const useTodoListStore = defineStore('todoList', {
state: () => ({
todoList: [],
id: 0
}),
actions: {
addTodo(item) {
this.todoList.push({ item, id: this.id++, completed: false })
},
deleteTodo(itemId) {
this.todoList = this.todoList.filter((object) => {
return object.id !== itemId
})
},
}
})
🌿 5. Now create simple form:
<script setup>
import { ref } from 'vue'
import { useTodoListStore } from '@/stores/todoList'
const todo = ref('')
const store = useTodoListStore()
function addItemAndClear(item) {
if (item.length === 0) {
return
}
// invokes function in the store:
store.addTodo(item)
todo.value = ''
}
</script>
<template>
<div>
<form @submit.prevent="addItemAndClear(todo)">
<input v-model="todo" type="text" /><button>Add</button>
</form>
</div>
</template>
<style scoped>
</style>
- ✋ in here we use ``` ref('') ``` to reactive the form. which will update the value of the user types in the input.
- now we have to access the store so we use:
import { useTodoListStore } from '@/stores/todoList'
const store = useTodoListStore()
- then we validate the input, if ok we sent the item and clear the form:
function addItemAndClear(item) {
if (item.length === 0) {
return
}
// invokes function in the store:
store.addTodo(item)
todo.value = ''
}
- then we add the function in the form submit handler:
<form
@submit.prevent="addItemAndClear(todo)"
>
<from/>
Now go to TodoList.Vue.
🌿 1. import store:
import { useTodoListStore } from '@/stores/todoList'
const store = useTodoListStore()
🌿 2. StoreToRefs
we will import that storeToRefs: its help to bring property from the store with ensuring reactivity each item.
// storeToRefs lets todoList keep reactivity:
const { todoList } = storeToRefs(store)
now render the code in
<script setup>
import { useTodoListStore } from '@/stores/todoList'
import { storeToRefs } from 'pinia'
const store = useTodoListStore();
// storeToRefs lets todoList keep reactivity:
const { todoList } = storeToRefs(store)
// destructuring action method doesn't require using storeToRefs:
const { toggleCompleted } = store
</script>
<template>
<div v-for="todo in todoList" :key="todo.id" class="list">
<div class="item">
<span :class="{ completed: todo.completed }">{{ todo.item }}</span>
<span @click.stop="toggleCompleted(todo.id)">✔</span>
</div>
</div>
</template>
<style scoped>
.completed {
text-decoration: line-through;
}
</style>
Now go to store.js and remember that we use ``` completed: false ``` in action. we want to check the id and find the item is completed or not completed.
toggleCompleted(idToFind) {
const todo = this.todoList.find((obj) => obj.id === idToFind);
if (todo) {
todo.completed = !todo.completed
}
}
Now again go to the TodoList.vue,
import the toggleCompleted action. add a event lister to listen the click of check mark which will invoke the toggle action in store.
<span @click.stop="toggleCompleted(todo.id)">✔</span>
we already write the delete action in store,
deleteTodo(itemId) {
this.todoList = this.todoList.filter((object) => {
return object.id !== itemId
})
},
now import that in toDoList.vue, and add a event lister which also invoke the delete function:
<span @click="deleteTodo(todo.id)" class="x">❌</span>
DONE!!!!!!
<!-- main.js -->
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const app = createApp(App)
app.use(createPinia())
app.mount('#app')
<!-- App.vue -->
<script setup>
import TodoApp from './components/TodoApp.vue'
</script>
<template>
<todo-app />
</template>
<style>
* {
margin: 0;
padding: 0;
}
#app {
background-color: #597d9a;
display: flex;
justify-content: center;
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
</style>
<!-- TodoApp.vue -->
<script setup>
import TodoForm from './TodoForm.vue'
import TodoList from './TodoList.vue'
</script>
<template>
<div class="todo-app">
<h1>To Do List</h1>
<todo-form />
<todo-list />
</div>
</template>
<style scoped>
.todo-app {
padding: 5vh;
min-height: 100vh;
width: 30vw;
background-color: #f7f9fc;
}
</style>
<!-- TodoForm.vue -->
<script setup>
import { ref } from 'vue'
import { useTodoListStore } from '@/stores/todoList'
const todo = ref('')
const store = useTodoListStore()
function addItemAndClear(item) {
if (item.length === 0) {
return
}
// invokes function in the store:
store.addTodo(item)
todo.value = ''
}
</script>
<template>
<div>
<form @submit.prevent="addItemAndClear(todo)">
<input v-model="todo" type="text" /><button>Add</button>
</form>
</div>
</template>
<style scoped>
form {
margin-bottom: 15px;
}
input {
margin-top: 25px;
margin-bottom: 15px;
height: 20px;
width: 50%;
}
button {
margin-left: 30px;
background-color: #2f6089;
padding: 5px 10px;
border: none;
border-radius: 5px;
font-weight: 800;
color: white;
width: 15%;
}
.alert-div {
min-height: 25px;
}
.alert {
color: #d1495b;
font-size: 1em;
font-weight: 600;
}
</style>
<!-- TodoList.vue -->
<script setup>
import { useTodoListStore } from '@/stores/todoList'
import { storeToRefs } from 'pinia'
const store = useTodoListStore()
// storeToRefs lets todoList keep reactivity:
const { todoList } = storeToRefs(store)
// destructuring action method doesn't require using storeToRefs:
const { toggleCompleted, deleteTodo } = store
</script>
<template>
<div v-for="todo in todoList" :key="todo.id" class="item">
<div class="content">
<span :class="{ completed: todo.completed }">{{ todo.item }}</span>
<div>
<span @click.stop="toggleCompleted(todo.id)">✔</span>
<span @click="deleteTodo(todo.id)" class="x">❌</span>
</div>
</div>
</div>
</template>
<style scoped>
span {
margin: 0 10px;
cursor: pointer;
}
.item {
display: flex;
justify-content: center;
}
.content {
display: flex;
font-size: 1.5em;
justify-content: space-between;
width: 80vw;
padding: 5px;
}
.completed {
text-decoration: line-through;
}
</style>
<!-- Store/ todoList.js -->
import { defineStore } from 'pinia'
export const useTodoListStore = defineStore('todoList', {
state: () => ({
todoList: [],
id: 0
}),
actions: {
addTodo(item) {
this.todoList.push({ item, id: this.id++, completed: false })
},
deleteTodo(itemId) {
this.todoList = this.todoList.filter((object) => {
return object.id !== itemId
})
},
toggleCompleted(idToFind) {
const todo = this.todoList.find((obj) => obj.id === idToFind)
if (todo) {
todo.completed = !todo.completed
}
}
}
})