I'm a beginner in vue.js and I'm trying to get an api result and show it to my vue page. my Api is built with Laravel 5.7.
I've just installed Vue axios package to work with http.
Here is my code:
TaskController
public function index()
{
return response(Task::all()->jsonSerialize(), Response::HTTP_OK);
}
App.vue
<template>
<div class="app-component">
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>Task Title</th>
<th>Priority</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<task-component v-for="task in tasks" :key="task.id" :task="task"></task-component>
<tr>
<td><input type="text" id="task" class="form-control"></td>
<td>
<select id="select" class="form-control">
<option>Low</option>
<option>Medium</option>
<option>High</option>
</select>
</td>
<td><button class="btn btn-primary">Add</button></td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
import TaskComponent from './Task.vue';
export default{
data(){
return{
tasks: [],
}
},
methods: {
getTasks(){
window.axios.get('/api/tasks').then(({data})=>{
data.forEach(task =>{
this.tasks.push(task)
});
});
},
created(){
this.getTasks();
}
},
components:{TaskComponent}
}
</script>
Task Page
<template>
<tr>
<td>{{ task.id }}</td>
<td>{{ task.title }}</td>
<td>{{ task.priority }}</td>
<td><button class="btn btn-danger">Remove</button></td>
</tr>
</template>
<script>
export default{
data(){
return{
}
},
props: ['task']
}
</script>
I got no errors but no result appeared to my vue although the controller returns json data correctly
the created() hook should not be in methods:
export default {
methods: {},
created() {}
}
Related
Trying to display Total records. Students.length works the first time on page load thanks to the created() method. However, calling filteredStudents(), is out of date. What is the easiest way to make this reactive?
<template>
<div class="d-inline-flex flex-row p-4 col-2">
Total record(s): {{ recordCount }}
</div>
<table class="table border table-striped table-hover">
<thead class="bg-secondary">
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
</tr>
</thead>
<tbody>
<tr v-for="student in filteredStudents()" :key="student._id">
<td>{{ student.firstName }}</td>
<td>{{ student.lastName }}</td>
<td>{{ student.email }}</td>
</tr>
</tbody>
</table>
</template>
<script>
import MixinCommon from '#/mixins/common.js'
export default {
data() {
return {
searchTerm: '',
Students: [],
studentcount: 0
}
},
created() {
this.Students = this.getSutdentList()
},
computed: {
recordCount() {
return this.Students.length
}
},
mixins: [MixinCommon],
methods: {
filteredStudents() {
return this.searchStudentList(this.searchTerm.toUpperCase(), this.Students)
},
}
}
</script>
I don't know the implementation of the searchStudentsList method, but you could try using the filteredStudents as a computed property, or making a watch property on the searchTerm in order to make the search again:
Using computed:
<template>
<div class="d-inline-flex flex-row p-4 col-2">
Total record(s): {{ recordCount }}
</div>
<table class="table border table-striped table-hover">
<thead class="bg-secondary">
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
</tr>
</thead>
<tbody>
<tr v-for="student in filteredStudents" :key="student._id">
<td>{{ student.firstName }}</td>
<td>{{ student.lastName }}</td>
<td>{{ student.email }}</td>
</tr>
</tbody>
</table>
</template>
<script>
import MixinCommon from '#/mixins/common.js'
export default {
data() {
return {
searchTerm: '',
Students: [],
studentcount: 0
}
},
created() {
this.Students = this.getSutdentList()
},
computed: {
recordCount() {
return this.Students.length
},
filteredStudents() {
return this.searchStudentList(this.searchTerm.toUpperCase(), this.Students)
},
},
mixins: [MixinCommon],
}
</script>
Using watch property:
<template>
<div class="d-inline-flex flex-row p-4 col-2">
Total record(s): {{ recordCount }}
</div>
<table class="table border table-striped table-hover">
<thead class="bg-secondary">
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
</tr>
</thead>
<tbody>
<tr v-for="student in filteredStudents" :key="student._id">
<td>{{ student.firstName }}</td>
<td>{{ student.lastName }}</td>
<td>{{ student.email }}</td>
</tr>
</tbody>
</table>
</template>
<script>
import MixinCommon from '#/mixins/common.js'
export default {
data() {
return {
searchTerm: '',
Students: [],
filteredStudents: [],
studentcount: 0
}
},
created() {
this.Students = this.getSutdentList()
this.filteredStudents = this.searchStudentList(this.searchTerm.toUpperCase(), this.Students)
},
computed: {
recordCount() {
return this.Students.length
}
},
watch: {
searchTerm(newValue) {
this.filteredStudents = this.searchStudentList(newValue.toUpperCase(), this.Students)
}
}
mixins: [MixinCommon],
}
</script>
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);
});
}
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):{
}
}
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.
I'm really new to vuejs and I was wondering if it is possible to trigger the checkbox by clicking the table row.
here's a fiddle for you to play.
https://jsfiddle.net/50wL7mdz/265410/
HTML
<div id="app">
<table>
<tbody>
<tr v-for="cat in categories" #click="selectCat(cat)">
<td><input type="checkbox" :value="cat" v-model="selected" name="" id=""></td>
<td>{{ cat.code}}</td>
<td>{{ cat.name }}</td>
</tr>
</tbody>
</table>
<button #click="checkData()">Check</button>
</div>
VUEJS
new Vue({
el: '#app',
data() {
return {
categories: [
{code:'HW', name:'Hardware'},
{code:'SW', name:'Software'},
{code:'OS', name:'Office Supplies'}
],
selected:[]
}
},
methods:{
selectCat(cat){
this.selected.push(cat);
},
checkData(){
alert(1);
console.log(this.selected);
}
}
})
Thanks in advance.
Add a selected model to your categories and switch that attribute on row click like so:
<div id="app">
<table>
<tbody>
<tr v-for="(cat, index) in categories" :key="index" #click="cat.selected = !cat.selected">
<td><input type="checkbox" v-model="cat.selected"></td>
<td>{{ cat.code}}</td>
<td>{{ cat.name }}</td>
</tr>
</tbody>
</table>
</div>
new Vue({
el: '#app',
data() {
return {
categories: [
{code:'HW', name:'Hardware', selected: false},
{code:'SW', name:'Software', selected: false},
{code:'OS', name:'Office Supplies', selected: false}
]
}
},
methods:{
}
})
Manipulating the state of natives components should always be done by changing their v-model, rather than going into the DOM and and setting a selected attribute. Basically let your model define the state of your view.
Here's another version that'll use a separate array for handling the selected state:
<div id="app">
<table>
<tbody>
<tr v-for="(cat, index) in categories" :key="index" #click="toggleSelect(cat)">
<td><input type="checkbox" :checked="selected.includes(cat.code)"></td>
<td>{{ cat.code}}</td>
<td>{{ cat.name }}</td>
</tr>
</tbody>
</table>
</div>
new Vue({
el: '#app',
data() {
return {
categories: [
{code:'HW', name:'Hardware'},
{code:'SW', name:'Software'},
{code:'OS', name:'Office Supplies'}
],
selected: ['HW']
}
},
methods:{
toggleSelect (cat) {
if (this.selected.includes(cat.code)) {
this.selected.splice(this.selected.findIndex(v => v === cat.code), 1)
} else {
this.selected.push(cat.code)
}
}
}
})