Not able to remove a user from a table - vue.js

I have a nested table where I'm grouping users by the department that they belong to. The issue I'm having is that if I click on the delete button, that specific user that I've deleted isn't being removed from the list and I'm not getting any errors in my console.
Here is my code
<template>
<div>
<div class="row">
<div class="col-lg-12">
<table class="table table-bordered table-sm">
<thead>
<tr>
<th>Name</th>
<th>Job Title</th>
</tr>
</thead>
<tbody v-for="(users, department) in personnal">
<tr>
<td>{{ department }}</td>
</tr>
<tr v-for="user in users">
<td>{{ user.name }}</td>
<td>{{ user.job_title }}</td>
<td>
<div class="btn btn-danger btn-sm" #click="removeUser(user)">
<i class="fa fa-trash"></i>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</template>
export default
{
props: [],
data() {
return {
personnal: {},
}
},
computed: {
},
methods: {
getUsers(){
axios.get(`/api/users`).then(response => {
this.personnal = response.data.users;
});
},
removeUser(user){
axios.delete(`/api/users/${user.id}/delete`).then(response => {
Object.keys(this.personnal).forEach(user => {
this.personnal[user].filter(u => u.id !== response.data.id);
this.personnal[user].slice(response.data);
});
});
}
},
mounted() {
this.getUsers();
}
}
</script>

First of all, you have to pass both department and user in the click event:
<div class="btn btn-danger btn-sm" #click="removeUser(department, user)">
<i class="fa fa-trash"></i>
</div>
Then, in the removeUser method:
removeUser(department, user){
axios.delete(`/api/users/${user.id}/delete`).then(response => {
const index = this.personnal[department].findIndex(u => u.id === user.id);
this.personnal[department].splice(index, 1);
});
}

Related

Props passed to child component do not render well

I am attempting to pass user id's fetched from an API trough props from parent (App) to child (Modal). The problem is that when I pass the props down to the modal they don't render as they should in the div with the modal-body class, in fact all of them display an id of 1.
App.vue:
<template>
<div class="container mt-3">
<table class="table">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Name</th>
<th scope="col">Username</th>
</tr>
</thead>
<tbody v-for="user in users" :key="user.id">
<tr>
<th scope="row">{{ user.id }}</th>
<td>{{ user.name }}</td>
<td>{{ user.username }}</td>
<td>
<Modal :id="user.id" />
</td>
</tr>
</tbody>
</table>
</div>
<pre>{{ user }}</pre>
</template>
<script>
import axios from "axios";
import Modal from "#/components/Modal.vue";
export default {
name: "App",
components: {
Modal,
},
data() {
return {
users: null,
};
},
methods: {
async load_users() {
try {
const { data } = await axios.get(
"https://jsonplaceholder.typicode.com/users"
);
this.users = data;
} catch (error) {
console.log("error");
}
},
},
mounted() {
this.load_users();
},
};
</script>
Modal.vue:
<template>
<!-- Button trigger modal -->
<button
type="button"
class="btn btn-danger"
data-bs-toggle="modal"
data-bs-target="#exampleModal"
>
Delete
</button>
<!-- Modal -->
<div
class="modal fade"
id="exampleModal"
tabindex="-1"
aria-labelledby="exampleModalLabel"
aria-hidden="true"
>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
</div>
<div class="modal-body">
Are you sure you want to delete user: {{ id }}
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-secondary"
data-bs-dismiss="modal"
>
Close
</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Modal",
props: ["id"],
};
</script>
The page preview is the following:
It does not matter on which Delete button I click the id is always 1.
But when I inspect the props in the Vue Devtools they are different, for the second for example it appears 2 as it should:
Any help is appreciated.
Thank you.
You are rendering 10 modals each with the same id - exampleModal, so it's always the first one which is opened. This is why you are experiencing the behaviour you describe.
However - the real problem is with your structure.
Why are you rendering 10 modals? Why not render one and pass in the respective props?
Something like this:
<template>
<Modal v-if="modal.isActive" :id="modal.userId" />
<tbody v-for="user in users" :key="user.id">
<tr>
<th scope="row">{{ user.id }}</th>
<td>{{ user.name }}</td>
<td>{{ user.username }}</td>
<td>
<button
type="button"
class="btn btn-danger"
#click="onClick(user.id)"
>
Delete
</button>
</td>
</tr>
</tbody>
</template>
<script>
import Modal from '#/components/Modal.vue'
export default {
name: 'App',
components: {
Modal,
},
data() {
return {
users: null,
modal: {
isActive: false,
userId: null,
},
}
},
methods: {
onClick(userId) {
Object.assign(this.modal, { isActive: true, userId })
},
},
}
</script>

V-If and V-else questions (Vue and Firebase Real-time Database)

I'm working on a web application using Vue and Firebase-Real-time Database. I managed to include all the CRUD functions following several tutorials in Youtube. The problem right now is that i am unable to show or hide a certain element when changing the {Edit:} condition of the user via v-if/v-else. Below will be the code;
<template>
<div class="row">
<div class="container">
<table class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>ID</th>
<th>Actions</th>
</tr>
</thead>
<tbody v-for="userProfile in Users" :key="userProfile.key">
<tr v-if="userProfile.edit">
<td>{{userProfile.username}}</td>
<td>{{userProfile.email}}</td>
<td>{{userProfile.userId}}</td>
<td>
<button #click.prevent="updateUser(userProfile.key)" class="btn btn-primary"> Update </button>
<button #click.prevent="removeUser(userProfile.key)" class="btn btn-danger">Delete</button>
</td>
</tr>
<tr v-else>
<td> <input type="text" v-model="userProfile.username"> </td>
<td> <input type="text" v-model="userProfile.email"> </td>
<td> <input type="text" v-model="userProfile.userId"> </td>
<td>
<button #click.prevent="saveUser(userProfile)" class="btn btn-primary"> Save </button>
<button #click.prevent="cancel(userProfile.key)" class="btn btn-danger"> Cancel </button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
As for my script
import { usersRef } from '../main.js';
export default {
data() {
return {
Users: [],
}
},
created() {
usersRef.once('value', (Users) => {
Users.forEach((doc) => {
this.Users.push({
key: doc.key,
email: doc.child('email').val(),
username: doc.child('username').val(),
userId: doc.child('userId').val(),
})
})
})
},
methods:{
removeUser: function(key) {
usersRef.child(key).remove()
.then(function() {
console.log("deleted Sucessfully");
alert("Sucessfully Deleted");
}).catch(function(error) {
console.log(error);
});
},
updateUser: function(key) {
usersRef.child(key).update({edit:false})
.then(function() {
console.log("update Sucessfully");
alert("Sucessfully Update");
}).catch(function(error) {
console.log(error);
});
},
cancel:function(key){
usersRef.child(key).update({edit:false})
},
saveUser:function(userProfile) {
const key = userProfile.key;
usersRef.child(key).set({username:userProfile.username,email:userProfile.email,userId:userProfile.userId,edit:true})
.then(function() {
console.log("Saved Sucessfully");
alert("Successfully Saved");
}).catch(function(error) {
console.log(error);
});
}
}
}
am i doing something wrong? thanks in advance!

How to redirect to a page with an object in vue

I have a couple of vue pages, one page is a list of users, the second is a list of permissions and the third is a vue component
that displays the list of users and another vue component that shows the details of a user.
What I'm trying to do is, if I'm on the permissions page and I would like to go to a specific user's details I would like to redirect
with that panel open.
I am able to redirect the the users page by doing window.location.href = '/admin/users'; but I'm not sure how I'm going to get
the user panel open.
I thought that if I could do $emit then that could pass the user object over to the users main page, but that won't work if I do
a window.location.href = '/admin/users';
Here is my code. This has 2 components displays a list of users and their details
<template>
<div class="row">
<div class="col-md-6">
<users :emit="emit" :users="users"></users>
</div>
<div class="col-md-6">
<detail v-if="userDetails" :emit="emit" :user="manageUser"></detail>
</div>
</div>
</template>
<script>
export default {
props: ['emit', 'users'],
data() {
return {
userDetails: false,
manageUser: null
}
},
mounted() {
this.emit.$on('user', payload => {
this.manageUser = payload.user;
this.userDetails = true;
});
}
}
</script>
This is my users lists
<template>
<div class="card">
<div class="card-header">
<h3 class="card-title text-success" style="cursor: pointer;" #click="createUser"> New User</h3>
</div>
<div class="card-body">
<table class="table table-sm table-striped">
<thead>
<tr>
<th>Name</th>
<th style="width:120px;"></th>
</tr>
</thead>
<tbody>
<tr v-for="user in users">
<td>{{user.name}}</td>
<td style="text-align: right;">
<button class="btn btn-sm btn-outline-info" #click="detail(user)">Details</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script>
export default {
props: ['emit', 'users'],
data() {
return {
}
},
methods: {
detail(user) {
this.emit.$emit('user', { user: user });
}
}
}
</script>
and this is my permissions pages
<template>
<div class="card">
<div class="card-header">
<h3 class="card-title">{{permission.name}}</strong></h3>
</div>
<div class="card-body p-0">
<table class="table table-sm table-striped">
<thead>
<tr>
<th>User Name</th>
<th style="width:120px;"></th>
</tr>
</thead>
<tbody>
<tr v-for="user in users">
<td>{{user.name}}</td>
<td style="text-align: right;">
<button class="btn btn-sm btn-outline-info" #click="detail(user)">Details</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script>
export default {
props: ['emit'],
data() {
return {
users: []
}
},
mounted() {
this.allUsers();
},
methods: {
allUsers() {
this.loading = true;
axios.get('/users').then(response => {
this.users = response.data.users;
});
},
detail(user) {
window.location.href = '/admin/users';
}
}
}
</script>

How to extract single record ( id ) from JSON response and display in DOM using VueJS?

I have created a List view to see records from a JSON API response. Now I want user to click on a icon eg. '>' to see only that record on the full page ( Detail View ). I am not sure which vue directive I need to use and how to display in the DOM.
I tried this http://localhost:8000/Patients/1/?format=json but then directive v-for is not working
<template>
<div id="app" class="container">
<p v-if="loading">Loading...</p>
<div v-else>
<h3 class="heading" style="text-align:left">Patients List</h3>
<input id="lens" v-model= "search" placeholder ="Search here">
<br></br>
</div>
<table class="table table-bordered">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Name</th>
<th scope="col">Mobile</th>
<th scope="col">Email</th>
</tr>
</thead>
<tbody>
<tr v-for="patient in filteredPatients" v-bind:key="patient">
<td>{{ patient.id }}</td>
<td>{{ patient.first_name + " " + patient.last_name }}</td>
<td>{{ patient.mobile }}</td>
<td>{{ patient.email }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script>
import axios from "axios";
export default {
name: 'app',
data () {
return {
loading: false,
patients: '',
search: '',
}
},
mounted () {
this.loading = true;
axios
.get('http://localhost:8000/Patients/?format=json')
.then(response => (this.patients = response.data))
.catch(error => console.log(error))
.finally(() => this.loading = false)
},
computed: {
filteredPatients() {
return this.patients.filter(patient => {
return `${patient.first_name} ${patient.last_name} ${patient.email} ${patient.mobile} ${patient.id}`.includes(this.search);
})
}
From the filteredPatients()method you are only returning three field i.e first_name,last_name and email but from your loop you are expecting to get five fields i.e id,first_name,last_name,email and mobile
How about you get your data directly from the patients value in the data section but you will first have to change it to an array.
After that have an onclick listener on the table rows and pass the specific product as an argument to the listener method.This way you will have the selected/clicked record.From here you can pass the record to the other page as props.
So the final code would look somehow like this:
<div id="app" class="container">
<p v-if="loading">Loading...</p>
<div v-else>
<h3 class="heading" style="text-align:left">Patients List</h3>
<input id="lens" v-model= "search" placeholder ="Search here">
<br></br>
</div>
<table class="table table-bordered">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Name</th>
<th scope="col">Mobile</th>
<th scope="col">Email</th>
</tr>
</thead>
<tbody>
<tr v-for="(patient,id )in patients" :key="id" #click="getOneRecord(patient)">
<td>{{ patient.id }}</td>
<td>{{ patient.first_name + " " + patient.last_name }}</td>
<td>{{ patient.mobile }}</td>
<td>{{ patient.email }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script>
import axios from "axios";
export default {
name: 'app',
data () {
return {
loading: false,
patients: {},
search: '',
}
},
mounted () {
this.loading = true;
axios
.get('http://localhost:8000/Patients/?format=json')
.then(response => (this.patients = response.data))
.catch(error => console.log(error))
.finally(() => this.loading = false)
},
computed: {
filteredPatients() {
return this.patients.filter(patient => {
return `${patient.first_name} ${patient.last_name} ${patient.email} ${patient.mobile} ${patient.id}`.includes(this.search);
})
},
methods:{
getOneRecord(record):{
}
}

Vue 2 filter is not a function

trying to filter an array of objects with an input. I have tried multiple ways with no luck so far. it is telling me that my function "clients.filter" is not a function. get the same message if i set the code as "list.filter", the input for the search is actually part of a separate component and i'm not sure if maybe that is my issue. see below
this is my component where the clients are being gathered from api, and listed into data table
<template>
<table class="table table-bordered table-light table-striped table-hover">
<thead class="thead-primary">
<tr>
<th scope="col">#</th>
<th scope="col">Name</th>
<th scope="col">Type</th>
<th scope="col">Email</th>
<th scope="col">Phone</th>
<th scope="col">Profile</th>
</tr>
</thead>
<tbody class="client-info">
<tr v-for="(client, index) in filterBy(clients, searchClient)" :key="index">
<td>{{ index }}</td>
<td>{{ client.name }}</td>
<td>{{ client.type }}</td>
<td>{{ client.email }}</td>
<td>{{ client.phone }}</td>
<td><router-link v-bind:to="'/client/'+client.id"><i class="far fa-eye"></i></router-link></td>
</tr>
</tbody>
</table>
</template>
<script>
export default {
name: 'client-info',
props: {
clients: {
type: Array,
default: () => []
}
},
data() {
return {
searchClient: ''
}
},
created() {
this.$store.dispatch('retrieveClients')
},
computed: {
filterBy (clients, value) {
return clients.filter( client => {
console.log(this.clients)
return client.name.indexOf(value) > -1;
})
}
}
}
</script>
next is the parent component where the list is actually being shown. I believe my structure is off because im using vuex. But anyways im not sure why it is telling my "clients.filter" is not a function.
<template>
<div>
<!-- this is the buttons and filter above the list -->
<div class="d-flex mb-3">.
<input class="form-control w-25" placeholder="Filter By Name" v-model="searchClient" type="search">
<div class="mr-auto ml-2">
<button class="btn btn-outline-primary dropdown-toggle dropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span>Type:</span>
All
</button>
<div class="dropdown-menu dropdown-menu-left">
<a class="dropdown-item" href="#">Business</a>
<a class="dropdown-item" href="#">Individual</a>
</div>
</div>
<div class="btn-group ml-auto">
<button class="btn btn-outline-secondary"><i class="fas fa-print"></i></button>
<button class="btn btn-outline-success">Import <span><i class="fas fa-download"></i></span></button>
<button class="btn btn-outline-danger">Export <span><i class="fas fa-upload"></i></span></button>
<router-link to="/add" class="btn btn-primary pt-2">Add Client</router-link>
</div>
</div>
<!-- the clients data is imported from client info file -->
<div>
<client-info :clients="allClients"></client-info>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import ClientInfo from '#/components/clientslistexperience/ClientInfo'
export default {
name: 'ClientsList',
components: {
ClientInfo
},
data() {
return {
searchClient: ''
}
},
computed: {
...mapGetters(['allClients']),
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
</style>
another option i tried was defining my computed method as such
filterClient () {
return this.clients.filter( client => {
return !this.searchClient || client.name.toLowerCase().includes(this.searchClient.toLowerCase()) > -1
})
}
and then changing my v-for too
<tr v-for="(client, index) in filterClient" :key="index">
<td>{{ index }}</td>
<td>{{ client.name }}</td>
<td>{{ client.type }}</td>
<td>{{ client.email }}</td>
<td>{{ client.phone }}</td>
<td><router-link v-bind:to="'/client/'+client.id"><i class="far fa-eye"></i></router-link></td>
</tr>
but still no luck, all though i was not getting an alarm with that definition but i also wasn't getting any results either. any help would be awesome!!
When using
props: {
list: {
type: Array,
default: () => []
}
},
with default value an empty array means that if you define a component without passing a list prop then vue will use that default value.
For example, if you have defined a component like this:
<template>
<ul>
<li v-for="(item, index) in list" :key="index">{{item}}</li>
</ul>
</template>
export default {
name: 'MyList',
props: {
list: {
type: Array,
default: () => [1, 2, 3],
},
},
};
and then define a component instance like:
<my-list></my-list>
your component would render:
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
However, if you defined your component instance like this:
<my-list :list="['a', 'b', 'c']"></my-list>
your component would render:
<ul>
<li>a</li>
<li>b</li>
<li>c</li>
</ul>
Given that, and without knowing what your retrieveClients action does, what I can only assume is that the 'allClients' value that you pass to 'clients' prop is not an Array therefore the filter function does not exist.
Make sure your action mutates the state to at least an empty array if allClients is a state property. Else if, allClients is a getter make sure that returns at least an empty Array.