DEV Community

vuejstest
vuejstest

Posted on

vue js crud using api

app.vue
<script>
import AddProduct from './components/AddProduct.vue';
import ShowProduct from './components/ShowProduct.vue';
import EditProduct from './components/EditProduct.vue';

 export default{
 components:{
  AddProduct,
  ShowProduct,  
  EditProduct
  },
 data(){
  return {
  products:[],
  categories:[],
  currentPage:1,
  pageSize:5,
  selectedCategory:'',
  searchQuery:'',
  searchDescription:'',
  selectedProduct:null,
  showAddModal: false,
  showDetailsModal:false,
  showEditModal:false,
  sortBy:'',
  orderBy:''
 };
 }
 ,
  async created(){
  try {
  const response = await fetch('https://fakestoreapi.com/products');
  this.products = await response.json();
  this.categories=[ ...new Set(this.products.map(product =>product.category))]
  } catch (error) {
  console.error(error);
 }
 },
 watch: {
  sortBy(newValue) {
 if (newValue) {
 this.orderBy = '';
 }
 },
 orderBy(newValue) {
 if (newValue) {
 this.sortBy = '';
 }
 }
 },
 computed:{
 totalPrice(){
  return this.filteredProducts.reduce((sum,product) => sum+product.price,0);
 },
 totalPerPage(){
  return this.paginatedProducts.reduce((sum,product)=> sum + product.price, 0);
 },
 totalPages(){
  return Math.ceil(this.filteredProducts.length/this.pageSize);
},

 paginatedProducts(){
  const start=(this.currentPage-1)*this.pageSize;
  const end=start+this.pageSize;
  return this.filteredProducts.slice(start,end)
 },
 filteredProducts(){
  let filtered=this.products;
  if(this.selectedCategory){
    filtered=filtered.filter(product=>product.category===this.selectedCategory)
  }
  if(this.searchQuery){
    filtered=filtered.filter(product=>product.title.toLowerCase().includes(this.searchQuery.toLowerCase()))
  }
  if(this.searchDescription){
    filtered=filtered.filter(product=>product.description.toLowerCase().includes(this.searchDescription.toLowerCase()))
  }
  if(this.sortBy){
    filtered=filtered.slice().sort((a,b)=>{
      return this.sortBy==='asc'
      ?a.title.localeCompare(b.title)
      :b.title.localeCompare(a.title)
    })
  }

  if(this.orderBy){
  filtered=filtered.slice().sort((a,b)=>{
  return this.sortBy==='asc'
  ?a.price-b.price
  :b.price-a.price;
  })
  }
  return filtered;
 }
 },
 methods:{
 addProduct(newProduct){
 this.products.unshift({id:Date.now(), ...newProduct});
 if(!this.categories.includes(newProduct.category)){
 this.categories.push(newProduct.category);
  }
  this.showAddModal=false;
 this.currentPage=1;
 },
 showProduct(product){
 this.selectedProduct=product;
 this.showDetailsModal=true;
 },
 editProduct(product){
 this.selectedProduct={ ...product};
 this.showEditModal=true;
 },
 updateProduct(updatedProduct){
 const index=this.products.findIndex(p=>p.id === updatedProduct.id);
 if(index !== -1){
 this.products.splice(index,1,updatedProduct);
 }
 this.showEditModal=false;
 },
 deleteProduct(id){
 if(confirm("do you want to delete")){
  this.products=this.products.filter(product=>product.id!==id)
 }
 },
 changePage(page){
 if(page>=1 && page<=this.totalPages){
  this.currentPage=page;
 }
 }
 }}
 </script> 

 <template>
 <div class="container"> 
 <h5 style="text-align: center;" >product listing</h5>
 <div>
  <select v-model="selectedCategory">
    <option value="">select category</option>
    <option :value="category" :key="category" v-for="category in categories">{{ category }}</option>
  </select>
  <select style="margin-left: 350px;" v-model="sortBy">
      <option value="" selected>Sort By:</option>
      <option value="title">title</option>
      <option value="price">price</option>
    </select>
    <select style="margin-left: 416px;" v-model="orderBy">
      <option value="" selected>Order by:</option>
      <option value="asc">ascending</option>
      <option value="desc">descending</option>
    </select>
 </div>
 <br>

 <div>
  <input type="text" v-model="searchQuery" placeholder="enter title to search" > 
 </div>
 <div>
  <input style="margin-left: 848px;" type="text" v-model="searchDescription" placeholder="enter description to search">
 </div>
 <br>
  <button style="margin-left: 12px;" @click.prevent="showAddModal=true">add new data</button>
  <div class="container">
    <table style="margin-left: auto; margin-right: auto;" class="table table-striped" border="1">
      <thead>
      <tr>
        <th>Sr no</th>
        <th>Title</th>
        <th>Price</th>
        <th>Image</th>
        <th>Category</th>
        <th>Description</th>
        <th rowspan="3">Actions</th>
      </tr>
      </thead>
      <tbody>
      <tr v-for="(product) in paginatedProducts" :key="product.id">
        <td>{{ product.id }}</td>
        <td>{{ product.title }}</td>
        <td>{{ product.price }}</td>
        <td>
          <img :src="product.image" alt="product Image" style="width: 40px; height: 40px;" />
        </td>
        <td>{{ product.category }}</td>
        <td>{{ product.description }}</td>
        <td>
          <button @click="showProduct(product)"><img style="height: 25x; width: 25px;" src="/view.png"></button>
          <button @click="editProduct(product)"><img style="height: 25x; width: 25px;" src="/edit.png"></button>
          <button @click="deleteProduct(product.id)"><img style="height: 25x; width: 25px;" src="/delete.png"></button>
        </td>
      </tr>
      </tbody>
     </table>
     <div style="display: flex">
      <h6>total products {{ products.length }}</h6>
      <h6 style="margin-left: 346px">total price {{ totalPrice.toFixed(3) }}</h6>
      <h6 style="margin-left: auto">total price per page {{ totalPerPage.toFixed(3) }}</h6>
     </div>

     <nav aria-label="Page navigation example">
     <ul style="margin-left: 472px;" class="pagination">
     <li>
     <button @click="changePage(currentPage-1)"><</button>
     </li>
     <li
     v-for="page in totalPages"
     :class="{active:currentPage===page}"
     :key="page"
     >
     <button @click="changePage(page)">{{ page }}</button>
      </li>
     <li>
     <button @click="changePage(currentPage+1)">></button>
     </li>
     </ul>
     </nav>
     </div>
     </div>
     <AddProduct 
     :showModal="showAddModal"
     @add-product="addProduct"
     @close="showAddModal=false"
     />
     <ShowProduct 
     :showDetailsModal="showDetailsModal"
     :product="selectedProduct"
     @close="showDetailsModal=false"
     />
     <EditProduct
     :showEditModal="showEditModal"
     :product="selectedProduct"
     :categories="categories"
     @update-product="updateProduct"
     @close="showEditModal=false"
     />
    </template> 

add
<script>
export default{
 props: {
 showModal: Boolean,
 },
 data(){
 return{
    product:{
        title:'',
        price:'',
        image:'',
        category:'',
    },
 };
 },
 methods:{
 handleFileUpload(event){
 const file = event.target.files[0];
 if(file){
 const reader = new FileReader();
 reader.onload = ()=>{
 this.product.image = reader.result;
     };
 reader.readAsDataURL(file);
 }
 },
 submitProduct(){
 this.$emit('add-product',{ ...this.product});
  this.product={
    title:'',
    price:'',
    image:'',
    category:'',
 };
 this.$emit('close');
 }
 }
 };
 </script>

<template>
 <div v-if="showModal" tabindex="-1" class="modal d-block">
 <div class="modal-dialog modal-lg">
  <div class="modal-content">
    <div class="modal-header" >
      <h5>add new product</h5>
      <button type="button" @click="$emit('close')">X</button>
    </div>
    <form @submit.prevent="submitProduct">
    <div class="mb-3">
      <div class="row">
        <div class="col">
    <label class="form-label">title</label>
    <input type="text" v-model="product.title" placeholder="enter title" class="form-control" required />
  </div>
  <div class="col">
    <label class="form-label">price</label>
    <input type="number" v-model="product.price" placeholder="enter price" class="form-control" required />
  </div>
  </div>
  </div>
    <div class="mb-3">
      <div class="row">
        <div class="col">
        <label class="form-label">category</label>
        <input type="text" v-model="product.category" placeholder="enter category" class="form-control" required />
        </div>
        <div class="col">

    <label class="form-label">description</label>
    <input type="text" v-model="product.description" placeholder="enter description" class="form-control" required />
    </div>
   </div>
   <br>
    <div class="mb-3">
    <label class="form-label">image</label>
    <input type="file" @change="handleFileUpload" required />
    <img v-if="product.image" :src="product.image" alt="product image" style="height: 50px; width: 50px;" />
   </div>
    <div>
      </div>
      <div>
    <button style="margin-left: 343px;"  type="submit">add</button>  
  </div>
    </div>
  </form>
 <div v-if="showModal"></div>
 </div>
 </div>
 </div>
 </template>
 <style scoped>
 .form-control{
 width: 307px;
 }
 </style>

 edit
 <script>
 export default{
  props:{
    showEditModal:Boolean,
    product: Object,
    Categories:Array

  },
 data(){
 return{
    productData:{...this.product}
  };
 },
 watch:{
 product(newProduct){
    this.productData={ ...newProduct};
 }
 },
 methods:{

    handleFileUpload(event) {
  const file = event.target.files[0];
  if (file) {
    const reader = new FileReader();
    reader.onload = () => {
      this.productData.image = reader.result;
    };
    reader.readAsDataURL(file);
  }
 },
    updateProduct() {
  this.$emit('update-product', this.productData);
 },
 }};
 </script>

 <template>
 <div v-if="showEditModal" class="modal d-block">
    <div class="modal-dialog">
  <div class="modal-content" >
    <div class="modal-header">
 <h5>edit product</h5>
 <button style="margin-left: 300px;" type="button" @click="$emit('close')">X</button>
    </div>
    <form @submit.prevent="updateProduct">
    <div class="mb-3">
      <div class="row">
        <div class="col">
    <label class="form-label">title</label>
    <input type="text" v-model="productData.title" placeholder="enter title" class="form-control"  />
  </div>
  <div class="col">
  </div>
  <div class="col">
    <label class="form-label">price</label>
    <input type="number" v-model="productData.price" placeholder="enter price" class="form-control"  />
  </div>
  </div>
  </div>
    <div class="mb-3">
      <div class="row">
        <div class="col">
    <label class="form-label">category</label>
    <input type="text" v-model="productData.category" placeholder="enter category" class="form-control"  />
  </div>
  <div class="col">
    <label class="form-label">description</label>
    <input type="text" v-model="productData.description" placeholder="enter category" class="form-control"  />
  </div>
 </div>
    <label class="form-label">image</label>
    <input type="file" @change="handleFileUpload"  />
    <img v-if="product.image" :src="product.image" alt="product image" style="height: 50px; width: 50px;" />
   </div>
    <div>
    <button type="submit">update</button>  

    </div>
  </form>
 <div v-if="showEditModal"></div>
 </div>
 </div>
 </div>
 </template>

 <style> 
 </style>

 show
 <script>
 export default{
 props: {
 showDetailsModal: Boolean,
 product: Object,
  },
 };
 </script>

 <template>
 <div v-if="showDetailsModal" class="modal d-block" tabindex="-1">
    <div class="modal-dialog">
       <div class="modal-content">
      <div class="modal-header">
         <h5 class="modal-title">product Details</h5>
         <button type="button" @click="$emit('close')">X</button>
         </div>
         <div class="modal-body">
          <div v-if="product">
            <p>title: {{ product.title }}</p>
            <p>price: {{ product.price }}</p>
            <p>category: {{ product.category }}</p>
            <p>image: </p>
            <div>
                <img :src="product.image" alt="product image" style="height: 50px; width: 50px;"/>
            </div>
          </div>
         </div>
         <div class="modal-footer">

        </div>
       </div>
     </div>    
  </div>

 </template>
 <div v-if="showDetailsModal" ></div>

 <style scoped>
 </style>
Enter fullscreen mode Exit fullscreen mode

Top comments (0)