Looping over vue Component - vue.js

Hi everyone.
I have a question about looping over a component in vuejs.
I defined a component and also a template tag which contains a table that fetches some data from database(in laravel framework).now i need to loop over this component more than 10 times and don't know how to do this!.
i used v-for in template tag but didn't work
<div id="app">
<csgo><csgo>
</div>
<template id="match-id" >
<table style="border: 2px solid black">
<tr >
<th>Ticket</th>
<th>Number</th>
<th>Match Type</th>
<th>Date</th>
<th>Time</th>
<th>Price</th>
<th>Sold</th>
</tr>
<tr>
<td> <button #click='buy({{$user->profile->account_money}})' v-bind:style="objectStyle" :disabled=Bstate >BUY</button> </td>
<td><input type="number" min="1" max="10" step="1" v-model="ticketNumber"></td>
<td>{{$matches[0]->matchType}}</td>
<td>{{$matches[0]->matchDate}}</td>
<td>{{$matches[0]->matchTime}}</td>
<td>{{$matches[0]->price}}</td>
<td>#{{soldTicket}}#{{sold}}</td>
</tr>
</table>
</template>
<script>
Vue.component('csgo', {
template: '#match-id',
data: function () {
return {
money: '',
sold: '',
state: false,
Estate: false,
Bstate: false,
error: '',
ticketNumber: 1,
objectStyle: {
background: 'lightgreen'
},
}
},
props: ['matches'],
methods: {
buy: function (num) {
tempNum = num
num -= 1000
if (num < 0) {
this.money = tempNum
}
vm = this
axios.post('/matches/csgo-buy-ticket', {tickets:
Math.floor(vm.ticketNumber)}).then(function (response) {
if (typeof (response.data) == 'string') {
vm.error = response.data
vm.state = false
vm.Estate = !vm.state
vm.Bstate = false
}
else {
vm.money = response.data[0]
vm.sold = response.data[1]
vm.state = true
vm.Estate = !vm.state
vm.Bstate = true
vm.objectStyle.background = 'darkred'
}
})
},
},
computed: {
soldTicket: function () {
vm = this
axios.get('/sold-ticket').then(function (response) {
return vm.sold = response.data
})
},
account_money: function () {
var vm = this
axios.get('/user-account-money').then(function
(response) {
vm.money = response.data
})
},
},
})
new Vue({
el:'#app',
data:{
list:[''],
},
created:function () {
vm = this
axios.get('/csgo-matches').then(function (response) {
console.log(response.data)
vm.list = response.data
})
},
})
</script>

Related

:class not updating when clicked in vuejs v-for

I want to add a css class to a item in v-for when the td in clicked
<template>
<div>
<h1>Forces ()</h1>
<section v-if="errored">
<p>We're sorry, we're not able to retrieve this information at the moment, please try back later</p>
</section>
<section v-if="loading">
<p>loading...</p>
</section>
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>#</th>
<th>ID</th>
<th
#click="orderByName = !orderByName">Forces</th>
</tr>
<th>Delete</th>
</thead>
<tbody>
<tr v-for="(force, index) in forces" #click="completeTask(force)" :class="{completed: force.done}" v-bind:key="index">
<td>
<router-link :to="{ name: 'ListForce', params: { name: force.id } }">Link</router-link>
</td>
<th>{{ force.done }}</th>
<th>{{ force.name }}</th>
<th><button v-on:click="removeElement(index)">remove</button></th>
</tr>
</tbody>
</table>
<div>
</div>
</div>
</template>
<script>
import {APIService} from '../APIService';
const apiService = new APIService();
const _ = require('lodash');
export default {
name: 'ListForces',
components: {
},
data() {
return {
question: '',
forces: [{
done: null,
id: null,
name: null
}],
errored: false,
loading: true,
orderByName: false,
};
},
methods: {
getForces(){
apiService.getForces().then((data, error) => {
this.forces = data;
this.forces.map(function(e){
e.done = false;
});
this.loading= false;
console.log(this.forces)
});
},
completeTask(force) {
force.done = ! force.done;
console.log(force.done);
},
removeElement: function (index) {
this.forces.splice(index, 1);
}
},
computed: {
/* forcesOrdered() {
return this.orderByName ? _.orderBy(this.forces, 'name', 'desc') : this.forces;
}*/
},
mounted() {
this.getForces();
},
}
</script>
<style>
.completed {
text-decoration: line-through;
}
</style>
I want a line to go through the items when clicked, but the dom doesn't update. If I go into the vue tab in chrome I can see the force.done changes for each item but only if I click out of the object and click back into it. I'm not to sure why the state isn't updating as it's done so when I have used an click and a css bind before.
I've tried to make minimal changes to get this working:
// import {APIService} from '../APIService';
// const apiService = new APIService();
// const _ = require('lodash');
const apiService = {
getForces () {
return Promise.resolve([
{ id: 1, name: 'Red' },
{ id: 2, name: 'Green' },
{ id: 3, name: 'Blue' }
])
}
}
new Vue({
el: '#app',
name: 'ListForces',
components: {
},
data() {
return {
question: '',
forces: [{
done: null,
id: null,
name: null
}],
errored: false,
loading: true,
orderByName: false,
};
},
methods: {
getForces(){
apiService.getForces().then((data, error) => {
for (const force of data) {
force.done = false;
}
this.forces = data;
this.loading= false;
console.log(this.forces)
});
},
completeTask(force) {
force.done = ! force.done;
console.log(force.done);
},
removeElement: function (index) {
this.forces.splice(index, 1);
}
},
mounted() {
this.getForces();
}
})
.completed {
text-decoration: line-through;
}
<script src="https://unpkg.com/vue#2.6.10/dist/vue.js"></script>
<div id="app">
<div>
<h1>Forces ()</h1>
<section v-if="errored">
<p>We're sorry, we're not able to retrieve this information at the moment, please try back later</p>
</section>
<section v-if="loading">
<p>loading...</p>
</section>
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>#</th>
<th>ID</th>
<th
#click="orderByName = !orderByName">Forces</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
<tr v-for="(force, index) in forces" #click="completeTask(force)" :class="{completed: force.done}" v-bind:key="index">
<th>{{ force.done }}</th>
<th>{{ force.name }}</th>
<th><button v-on:click="removeElement(index)">remove</button></th>
</tr>
</tbody>
</table>
<div>
</div>
</div>
</div>
The key problem was here:
this.forces = data;
this.forces.map(function(e){
e.done = false;
});
The problem here is that the property done is being added to the data after it has been made reactive. The reactivity observers get added as soon as the line this.forces = data runs. Adding done after that counts as adding a new property, which won't work.
It's also a misuse of map so I've switched it to a for/of loop instead.
for (const force of data) {
force.done = false;
}
this.forces = data; // <- data becomes reactive here, including 'done'
On an unrelated note I've tweaked the template to move the Delete header inside the top row.
Make sure force.done is set to false in data before initialization so it is reactive.
data() {
return {
force:{
done: false;
}
}
}
If force exists but has no done member set, you can use Vue.set instead of = to create a reactive value after initialization.
Vue.set(this.force, 'done', true);

How to trigger a component transition when the components data has loaded in vue js?

I have a fade-in transition that is working with all my components, the problem is a few of my components that are making api calls - transition in before the data is fully loaded.
So if I have a table with each row being populated with data from the api call, the table headers will transition initially and then a few seconds later - many rows with data will suddenly appear. What I want is for the table/data to fade-in. How can I trigger or delay the transition until the job_execs array gets populated with data from the API call?
views/releases.vue
<script>
import NavBar from "../components/NavBar.vue";
import Releases from "../components/releases/Releases.vue";
import Footer from "../components/Footer.vue";
export default {
name: "releases",
data() {
return {
loading: true
};
},
components: {
NavBar,
Releases,
Footer
},
};
</script>
<template>
<div id="vue-main">
<NavBar></NavBar>
<h1><b>Releases</b></h1>
<transition name="fade" appear mode="out-in">
<Releases></Releases>
</transition>
<Footer></Footer>
</div>
</template>
components/releases/Releases.vue
<template>
<div class="releases">
<table>
<template>
<tr>
<td><b>Version</b></td>
<td><b>Platform</b></td>
<td><b>Status</b></td>
</tr>
<tr v-for="(item, index) in orderedReleases">
<td :style="tdStyle">{{ item.version }}</td>
<td :style="tdStyle">{{ item.platform }}</td>
<td :style="tdStyle">{{ item.status }}</td>
</tr>
</template>
</table>
</div>
</template>
<script>
import moment from "moment";
import sortBy from "lodash/sortBy";
export default {
name: "Releases",
props: ["loading"],
data() {
return {
job_execs: []
};
},
computed: {
orderedReleases: function() {
let newlist = this.job_execs.sort(this.naturalCompare).reverse()
for ( var i = 0; i < newlist.length; i++) {
if (typeof newlist[i].version === "string") {
if (newlist[i].version.startsWith("iPad")) {
console.log(newlist[i].version)
newlist.splice(i,1);
i--;
}
}
}
return newlist;
},
},
methods: {
calculateDuration: function(time_start, time_end) {
this.theDuration = moment.duration(time_end.diff(time_start));
if (this.theDuration.seconds() == 0) {
this.cleanDuration = "N/A";
} else {
this.cleanDuration =
this.theDuration.hours() +
" hrs " +
this.theDuration.minutes() +
" min " +
this.theDuration.seconds() +
" sec";
}
return this.cleanDuration;
},
naturalCompare: function(a, b) {
var ax = [], bx = [];
a.version.replace(/(\d+)|(\D+)/g, function(_, $1, $2) { ax.push([$1 || Infinity, $2 || ""]) });
b.version.replace(/(\d+)|(\D+)/g, function(_, $1, $2) { bx.push([$1 || Infinity, $2 || ""]) });
while(ax.length && bx.length) {
var an = ax.shift();
var bn = bx.shift();
var nn = (an[0] - bn[0]) || an[1].localeCompare(bn[1]);
if(nn) return nn;
}
return ax.length - bx.length;
}
},
created() {
this.jobExecEndpoint = process.env.VUE_APP_UATU_URL + "/api/v1/release/";
fetch(this.jobExecEndpoint)
.then(response => response.json())
.then(body => {
for (let i = 0; i < body.length; i++) {
this.cleanStartTime = moment(body[i].start_date);
this.job_execs.push({
version: body[i].version,
status: body[i].status.name,
start: this.cleanStartTime.format("LLL")
});
}
})
.catch(err => {
console.log("Error Fetching:", this.jobExecEndpoint, err);
return { failure: this.jobExecEndpoint, reason: err };
});
}
};
</script>
<style>
</style>
Add a v-if inside your Releases component on the div tag like so:
<div class="releases" v-if="job_execs"></div>
and change your data object like this:
data() {
return {
job_execs: null
};
},
The pattern I use:
loading: smth = null
loaded: smth = [...]
empty state: smth.length === 0
This way you don't need a separate loading property.

vue select all checkboxes with value generated by computed methods

My page has a Select All checkbox at the top where upon clicking it, it should have checked all the checkboxes. Here's my code:
<div class="columns bottom-border">
<div class="column">Student</div>
<div><a v-on:click="revokePoints()">Revoke</a><br/><input type="checkbox" v-model="selectAll">Select All</div>
</div>
<div class="columns" v-for="(behavior) in sortBehaviors(behaviorList)" :key="behavior._id">
<div class="column">{{ behavior.studentID.firstName }} </div>
<div class="column is-1"><center><input type="checkbox" :value="setCheckedValue(behavior.dataType,behavior._id,behavior.studentID._id,behavior.actionDate)" :id="setCheckedValue(behavior.dataType,behavior._id,behavior.studentID._id,behavior.actionDate)" v-model="checkedIDs"></center></div>
</div>
data() {
return {
positiveName: '',
behaviorList: [],
checkedIDs: [],
selected: []
};
},
computed:{
selectAll: {
get: function () {
return this.behaviorList ? this.selected.length == this.behaviorList.length : false;
},
set: function (value) {
var mySelected = [];
let self = this;
if (value) {
this.behaviorList.forEach(function (behavior) {
var getDataType = behavior.dataType
var getID = behavior._id
var getStudentID = behavior.studentID._id
var getActionDate = behavior.actionDate
var getGeneratedID = self.setCheckedValue(getDataType,getID,getStudentID,getActionDate);
mySelected.push(getGeneratedID);
});
}
self.selected = mySelected;
console.log("self selected")
console.log(self.selected)
}
}
},
methods: {
setCheckedValue(dataType,id,studentID,actionDate){
return "1:" + dataType + "|2:" + id + "|3:" + studentID + "|4:" + actionDate
},
revokePoints(){
var pointsToRevoke = this.checkedIDs;
console.log("pointsToRevoke")
console.log(pointsToRevoke)
}
When I click on the Select All checkbox, console will display that self.selected will have the id of all the checkboxes. But the issue is the checkbox for all the values displayed are not checked...
It is difficult to help because your code is not completed. But I would approach that a bit differently. I hope this codepen can help you.
const list = [
{ id: 1, name: 'New York', checked: true },
{ id: 2, name: 'Sydney', checked: false },
{ id: 3, name: 'London', checked: false },
{ id: 4, name: 'Chicago', checked: true }
]
new Vue({
el: '#app',
data() {
return {
list,
isAllChecked: false
};
},
methods: {
checkAll: function() {
this.list = this.list.map(city => ({ ...city,
checked: !this.isAllChecked
}))
this.isAllChecked = !this.isAllChecked
}
},
computed: {
getAllCheckedIDs: function() {
return this.list.filter(city => city.checked).map(city => city.id)
},
getNotAllCheckedIDs: function() {
return this.list.filter(city => !city.checked).map(city => city.id)
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<ul>
<li v-for="city in list" :key="city.id">
<label>
{{city.name}}
<input type="checkbox" v-model="city.checked" />
</label>
</li>
</ul>
<button #click="checkAll">Check all</button>
<br/>
<div>Checked IDs: {{getAllCheckedIDs}}</div>
<div>Not Checked IDs: {{getNotAllCheckedIDs}}</div>
</div>

Vuetify Using datatable with external data from an API with Vuex

I want to use the vuetify framework with Vuex , but there is limited documentation about using it with Vuex.
I want to:
Get data from an external API ( but only the data needed )
Then Save the data in state and edit or whatever
Then push any changes back to the api
I have tried some of the external pagination and sorting examples with vuetify , but I can't get it to show all record count unless I hard code it.
I am quite new to Vue and Vuetify , so maybe I am misunderstanding something.
<template>
<div>
<v-data-table
:headers='headers'
:items='items'
:length='pages'
:search='search'
:pagination.sync='pagination'
:total-items='totalItemCount'
class='elevation-1'
>
<template slot='items' slot-scope='props'>
<td class='text-xs-right'>{{ props.item.id }}</td>
<td class='text-xs-right'>{{ props.item.first_name }}</td>
<td class='text-xs-right'>{{ props.item.last_name }}</td>
<td class='text-xs-right'>{{ props.item.avatar }}</td>
</template>
</v-data-table>
</div>
</template>
<script>
import moment from 'moment'
import axios from 'axios'
export default {
name: 'test-table',
watch: {
pagination: {
async handler () {
const rowsPerPage = this.pagination.rowsPerPage
// const skip = (this.pagination.page - 1) * rowsPerPage
const pageNumber = this.pagination.page
const res = await axios.get(`https://reqres.in/api/users?page=${pageNumber}&per_page=${rowsPerPage}`)
this.items = res.data.data
this.$store.commit('saveTableData', this.items)
},
deep: true
}
},
computed: {
pages () {
return 171
},
totalItemCount () {
return 400
}
},
async mounted () {
const rowsPerPage = this.pagination.rowsPerPage
const skip = (this.pagination.page - 1) * rowsPerPage
const res = await axios.get(`https://reqres.in/api/users?page=${skip}&per_page=${rowsPerPage}`)
this.items = res.data.data
this.$store.commit('saveTableData', this.items)
},
methods: {
nzDate: function (dt) {
return moment(dt).format('DD/MM/YYYY')
}
},
data: () => ({
search: '',
// totalItems: 0,
items: [],
pagination: {
sortBy: 'Date'
},
headers: [
{ text: 'ID', value: 'id' },
{ text: 'First Name', value: 'first_name' },
{ text: 'Last Name', value: 'last_name' },
{ text: 'Avatar', value: 'avatar' }
]
})
}
This is my working setup:
<template>
<v-data-table
:total-items="pagination.totalItems"
:pagination.sync="pagination"
:items="rows"
:headers="columns">
<template slot="headers" slot-scope="props">
<tr :active="props.selected">
<th v-for="column in props.headers">
{{ column.value }}
</th>
</tr>
</template>
<template slot="items" slot-scope="props">
<tr>
<td v-for="cell in props.item.row">
<v-edit-dialog lazy>
{{ cell.value }}
<v-text-field
:value="cell.value"
single-line
counter>
</v-text-field>
</v-edit-dialog>
</td>
</tr>
</template>
</v-data-table>
</template>
<script>
export default {
data: () => ({
pagination: {
page: 1,
rowsPerPage: 10,
totalItems: 0
},
selected: []
}),
computed: {
columns: {
get () {
return this.$store.state.columns
}
},
rows: {
get () {
return this.$store.state.rows
}
}
},
methods: {
async getRowsHandler () {
try {
const {total} = await this.$store.dispatch('getRows', {
tableIdentifier: this.$route.params.tableIdentifier,
page: this.pagination.page,
size: this.pagination.rowsPerPage
})
this.pagination.totalItems = total
} catch (error) {
// Error
}
}
}
}
</script>
I didn't implement everything. If you miss a specific part ask again and I will update my example. One more tip: You should avoid watch deep wherever possible. It can result in heavy calculations.
Assuming this is Vuetify v1.5, the documentation on the total-items prop on data-tables states:
Caution: Binding this to a blank string or using in conjunction with
search will yield unexpected behaviours
If you remove the 'search' prop from your table the record count will show again. If you're doing external stuff anyway, you'll won't want the default search functionality.

paging using knockout js

At first I have displlay data using knockout js successful,here is my code:
Js
var viewMode = {
lookupCollection: ko.observableArray()
};
$(document).ready(function () {
$.ajax({
type: "GET",
url: "/Home/GetIndex",
}).done(function (data) {
$(data).each(function (index, element) {
viewModel.lookupCollection.push(element);
});
ko.applyBindings(viewMode);
}).error(function (ex) {
alert("Error");
});
});
View:
<table class="paginated">
<tr>
<th>
Name
</th>
<th>
Price
</th>
<th>
Category
</th>
<th></th>
</tr>
<tbody data-bind="foreach: lookupCollection">
<tr>
<td data-bind="text: Name"></td>
<td data-bind="text: price"></td>
<td data-bind="text: Category"></td>
<td>
<button class="btn btn-success">Edit</button>
<button class="btn btn-danger">Delete</button>
</td>
</tr>
</tbody>
</table>
However, I need more code to paging the list items, I follow this site http://knockoutjs.com/examples/grid.html and replay my code but It has not display my list items:
JS:
var initialData = {
lookupCollection: ko.observableArray()
};
var PagedGridModel = function (items) {
this.items = ko.observableArray(items);
this.sortByName = function () {
this.items.sort(function (a, b) {
return a.name < b.name ? -1 : 1;
});
};
this.jumpToFirstPage = function () {
this.gridViewModel.currentPageIndex(0);
};
this.gridViewModel = new ko.simpleGrid.viewModel({
data: this.items,
columns: [
{ headerText: "Name", rowText: "Name" },
{ headerText: "Category", rowText: "Category" },
{ headerText: "Price", rowText: function (item) { return "$" + item.price.toFixed(2) } }
],
pageSize: 4
});
};
$(document).ready(function () {
$.ajax({
type: "GET",
url: "/Home/GetIndex",
}).done(function (data) {
$(data).each(function (index, element) {
viewModel.lookupCollection.push(element);
});
ko.applyBindings(new PagedGridModel(initialData));
}).error(function (ex) {
alert("Error");
});
});
View:
<div data-bind='simpleGrid: gridViewModel'> </div>
<button data-bind='click: sortByName'>
Sort by name
</button>
<button data-bind='click: jumpToFirstPage, enable: gridViewModel.currentPageIndex'>
Jump to first page
</button>
thankyou very much your answer:
The "simpleGrid" binding u try to use is a custom one, not available by default in knockout.
Here's a simple example for pagination using a computed observable :
Fiddle : http://jsfiddle.net/RapTorS/qLHwx/
var viewModel = function () {
var self = this;
self.pageSize = 4;
self.currentPage = ko.observable(1);
self.lookupCollection = ko.observableArray([]);
self.currentCollection = ko.computed(function () {
var startIndex = self.pageSize * (self.currentPage() - 1);
var endIndex = startIndex + self.pageSize;
return self.lookupCollection().slice(startIndex, endIndex);
});
};