Vue Sorted Table (Sorted from highest to lowest) - vue.js

Hi i am a newbie in vuejs. I wanted to sort a table by Highest to lowest. However i install the library of vue-sorted-table. But when im trying run the code the data always return lowest to highest. Can anyone help me? Thank you..
Here is the code:
<template>
<div id="app">
<sorted-table :values="values" #sort-table="onSort">
<thead>
<tr>
<th scope="col" style="text-align: left; width: 10rem;">
<sort-link name="id">ID</sort-link>
</th>
</tr>
</thead>
<template #body="sort">
<tbody>
<tr v-for="value in sort.values" :key="value.id">
<td>{{ value.id }}</td>
</tr>
</tbody>
</template>
</sorted-table>
</div>
</template>
<script>
import { ChevronUpIcon } from "vue-feather-icons";
export default {
name: "App",
data: function() {
return {
values: [{ id: 2 }, { id: 1 }, { id: 3 }],
sortBy: "",
sortDir: ""
};
},
components: {
ChevronUpIcon
},
methods: {
onSort(sortBy, sortDir) {
this.sortBy = sortBy;
this.sortDir = sortDir;
}
}
};
</script>
<style>
.feather {
transition: 0.3s transform ease-in-out;
}
.feather.asc {
transform: rotate(-180deg);
}
</style>
code can access here:
https://codesandbox.io/s/pedantic-kirch-bx9zv

Add dir property to sorted-table, and make its value equals to desc
<template>
<div id="app">
<sorted-table :values="values" #sort-table="onSort" dir="desc">
<thead>
<tr>
<th scope="col" style="text-align: left; width: 10rem;">
<sort-link name="id">ID</sort-link>
</th>
</tr>
</thead>
<template #body="sort">
<tbody>
<tr v-for="value in sort.values" :key="value.id">
<td>{{ value.id }}</td>
</tr>
</tbody>
</template>
</sorted-table>
</div>
</template>
<script>
import { ChevronUpIcon } from "vue-feather-icons";
export default {
name: "App",
data: function() {
return {
values: [{ id: 2 }, { id: 1 }, { id: 3 }],
sortBy: "",
sortDir: ""
};
},
components: {
ChevronUpIcon
},
methods: {
onSort(sortBy, sortDir) {
this.sortBy = sortBy;
this.sortDir = sortDir;
}
}
};
</script>
<style>
.feather {
transition: 0.3s transform ease-in-out;
}
.feather.asc {
transform: rotate(-180deg);
}
</style>
check https://github.com/BernhardtD/vue-sorted-table and pay attention to dir property of the table, you'll find the answer

Related

vue selectall checkbox indeterminate binding

i have a table like this what i want to do is apply indeterminate.prop, so if all are selected, checkbox is checked,if even 1 checkbox is checked :indeterminate.prop
I want to apply. How can I write this? I couldn't write the condition.
Frankly, not being able to write a simple and easy condition with vue js made me a little nervous.
stackoverflow asks for a lot of details but I don't know what to write
new Vue({
el: "#app",
data: {
selected: [],
messages: [{
id: 1,
text: "Learn JavaScript",
status: 'read'
},
{
id: 2,
text: "Learn Vue",
status: 'unread'
},
{
id: 3,
text: "Play around in JSFiddle",
status: 'read'
},
{
id: 4,
text: "Build something awesome",
status: 'unread'
}
]
},
computed: {
selectAll: {
get: function() {
return this.messages ? this.selected.length == this.messages.length : false;
},
set: function(value) {
var selected = [];
if (value) {
this.messages.forEach(function(item) {
selected.push(item.id);
});
}
this.selected = selected;
}
}
},
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<table class="table">
All,
<thead>
<tr>
<th><input type="checkbox" v-model="selectAll"></th>
<th>Status</th>
<th>Message</th>
</tr>
</thead>
<tbody>
<tr v-for="(item,index) in messages">
<td><input type="checkbox" v-model="selected" :value="item.id"></td>
<td>{{ item.status }}</td>
<td>{{ item.text }}</td>
</tr>
</tbody>
</table>
</div>
To select and unselect all checkboxes, you need to remove that computed properly and add a click event, which all ids, or remove all ids from selected.
new Vue({
el: "#app",
data: {
selected: [],
messages: [{
id: 1,
text: "Learn JavaScript",
status: 'read'
},
{
id: 2,
text: "Learn Vue",
status: 'unread'
},
{
id: 3,
text: "Play around in JSFiddle",
status: 'read'
},
{
id: 4,
text: "Build something awesome",
status: 'unread'
}
]
},
methods: {
selectAll(){
if(this.selected.length !== this.messages.length){
this.selected = this.messages.map(e=>e.id)
}else {
this.selected = []
}
}
}
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<h1>Selected ids: {{ selected }}</h1>
<button #click="selectAll">{{selected.length === messages.length ?'Unselect all' : 'Select All' }}</button>
<table class="table">
<thead>
<tr>
<th>Status</th>
<th>Message</th>
</tr>
</thead>
<tbody>
<tr v-for="(item,index) in messages">
<td><input type="checkbox" v-model="selected" :value="item.id"></td>
<td>{{ item.status }}</td>
<td>{{ item.text }}</td>
</tr>
</tbody>
</table>
</div>

: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 select All checkboxes based on condition with Vue.js

I am trying to select checkboxes based on some condition.
Select all checkboxes (select all checkboxes)
select all unread (select all unread where data array has status of
unread)
select all read (select all read where data array has status of read)
I am able to select all checkboxes on checkbox click,but unable to select it with links
I also need to fetch the ids of selected checkboxes,so i can perform action against these selected checkboxes.
Can you guys please have a look at this.
new Vue({
el: "#app",
data: {
selected: [],
messages: [{
id: 1,
text: "Learn JavaScript",
status: 'read'
},
{
id: 2,
text: "Learn Vue",
status: 'unread'
},
{
id: 3,
text: "Play around in JSFiddle",
status: 'read'
},
{
id: 4,
text: "Build something awesome",
status: 'unread'
}
]
},
methods: {
},
computed: {
selectAll: {
get: function() {
return this.messages ? this.selected.length == this.messages.length : false;
},
set: function(value) {
var selected = [];
if (value) {
this.messages.forEach(function(item) {
selected.push(item.id);
});
}
this.selected = selected;
}
}
},
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<h2>Message App:</h2>
<table class="table">
All,
Read,
Unread,
<thead>
<tr>
<th><input type="checkbox" v-model="selectAll"></th>
<th>Status</th>
<th>Message</th>
</tr>
</thead>
<tbody>
<tr v-for="(item,index) in messages">
<td><input type="checkbox" v-model="selected" :value="item.id"></td>
<td>{{ item.status }}</td>
<td>{{ item.text }}</td>
</tr>
</tbody>
</table>
</div>
You might want to use click event on a element:
All,
and in javascript code:
methods: {
selectAllHandler() {
if (this.selected.length) {
this.selected = []
} else {
this.selected = this.messages.map(item => item.id);
}
}
}
Your Javascript:
new Vue({
el: "#app",
data: {
selected: [],
messages: [{
id: 1,
text: "Learn JavaScript",
status: 'read'
},
{
id: 2,
text: "Learn Vue",
status: 'unread'
},
{
id: 3,
text: "Play around in JSFiddle",
status: 'read'
},
{
id: 4,
text: "Build something awesome",
status: 'unread'
}
]
},
methods: {
selectAll() {
this.selected = this.messages.map((message) => message.id);
}
}
})
Your HTML:
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<h2>Message App:</h2>
<table class="table">
All,
Read,
Unread,
<thead>
<tr>
<th><input type="checkbox" v-model="selectAll"></th>
<th>Status</th>
<th>Message</th>
</tr>
</thead>
<tbody>
<tr v-for="(item,index) in messages">
<td><input type="checkbox" v-model="selected" :value="item.id"></td>
<td>{{ item.status }}</td>
<td>{{ item.text }}</td>
</tr>
</tbody>

Looping a component in another component Vue.js

The question here is how can I get this Task.vue file to loop in my tasklist.vue file knowing I'm willing to pass a json file so I can get the list of all the task to do.
Task.vue
<template>
<table :id="id" class="task_box">
<tr>
<td class="task_user">{{name}}</td>
<td class="task_date">{{date}}</td>
<td class="task_time">{{time}}</td>
</tr>
<tr>
<td colspan="3" class="task_description">
<div class="toto">{{description}}</div>
</td>
</tr>
</table>
</template>
<script>
export default {
name: "task",
data() {
return {
id: 1,
name: "Test",
date: new Date(),
time: "9:30 ",
description: "whatever"
};
}
};
</script>
So this task.vue is meant to be a container that I can use in the tasklist.vue.
tasklist.vue
<template>
<div>
<task v-for="task in tasks" :key="task.id"></task>
</div>
</template>
<script>
import Task from "./Task.vue";
export default {
name: "tasklist",
data() {
return {
tasks: []
};
},
components: {
Task
}
};
</script>
You need to use Properties to pass your task from your task list to your task. Code is untested.
#Task
<template>
<table :id="task.id" class="task_box">
<tr>
<td class="task_user">{{task.name}}</td>
<td class="task_date">{{task.date}}</td>
<td class="task_time">{{task.time}}</td>
</tr>
<tr>
<td colspan="3" class="task_description">
<div class="toto">{{description}}</div>
</td>
</tr>
</table>
</template>
<script>
export default {
name: "task",
props: ["task"],
};
</script>
#TaskList
<template>
<div>
<task v-for="task in tasks" :task="task" :key="task.id"></task>
</div>
</template>
<script>
import Task from "./Task.vue";
export default {
name: "tasklist",
data() {
return {
tasks: [{
id: 1,
name: "Test",
date: new Date(),
time: "9:30 ",
description: "whatever"
}]
};
},
components: {
Task
}
};
</script>
If task-component is reapeating, you should insert it's tag inside table tag.
Use props to pass data to task-component from tasklist-component
When tasklist-component is creating, you can load tasks via Ajax from json.
Full working example of code you can find here
TaskList.vue
<script>
import Task from "./Task.vue";
export default {
components: { Task },
data() {
return {
tasks: [],
isLoading: false,
doShowNewTaskAddingDialog: false,
};
},
created(){
// this.isLoading = true;
// $.ajax({
// url: '/some/tasks/url',
// method: 'GET',
// dataType: 'json',
// success: (tasks) => {
// this.isLoading = false;
// this.tasks = tasks;
// }
// });
this.tasks = [
{id: 1, name: "task 1", date: new Date(), time: "9:31", description: "descr 1" },
{id: 1, name: "task 2", date: new Date(), time: "9:32", description: "descr 2" },
{id: 1, name: "task 3", date: new Date(), time: "9:33", description: "descr 3" },
{id: 1, name: "task 4", date: new Date(), time: "9:34", description: "descr 4" },
]
},
methods:{
addButtonHandler(){
this.doShowNewTaskAddingDialog = true;
}
}
};
</script>
<template>
<div>
<div v-if="isLoading">Please wait, loading tasks...</div>
<table v-if="!isLoading">
<task
v-for="task in tasks"
:key="task.id"
:task="task"
:isNew="false"
/>
<task
v-if="doShowNewTaskAddingDialog"
:isNew="true"
/>
</table>
Add new?
</div>
</template>
<style>
table, td{
border-collapse: collapse;
border: 1px solid black;
}
</style>
Task.vue
<template>
<!--
I'd prefer use bootstrap row and col- divs here instead
of table and tbody-hack. See discussion here: https://github.com/vuejs/Discussion/issues/295
-->
<tbody>
<!-- display only -->
<tr v-if="!isNew">
<td class="task_user">{{name}}</td>
<td class="task_date">{{date}}</td>
<td class="task_time">{{time}}</td>
</tr>
<tr v-if="!isNew">
<td colspan="3" class="task_description">
<div class="toto">{{description}}</div>
</td>
</tr>
<!-- edit -->
<tr v-if="isNew">
<td class="task_user"><input type="text" v-model="name"></td>
<td class="task_date"><input type="text" v-model="date"></td>
<td class="task_time"><input type="text" v-model="time"></td>
</tr>
<tr v-if="isNew">
<td colspan="3" class="task_description">
<div class="toto"><input type="text" v-model="description"></div>
</td>
</tr>
</tbody>
</template>
<script>
export default {
props:{
task: { type: Object, required: false },
isNew: { type: Boolean, required: true },
},
created(){
if(!this.isNew){
this.id = this.task.id;
this.name = this.task.name;
this.date = this.task.date;
this.time = this.task.time;
this.description = this.task.description;
}
},
data() {
return {
id: 1,
name: "",
date: new Date(),
time: "0:00 ",
description: ""
};
}
};
</script>

Is it possible to use a component property as a data variable?

I've copied my current code below. I'm trying to dynamically generate table headers depending on what type I pass as a prop to my tables component (standings and status are what I have as the data arrays in my example).
I've accomplished this by using the if statement in the header computed value to return the proper list.
However I'd like to not have to add additional if statements for every type.
Is there a way that I can leverage the type prop to bind directly to the matching data?
<div id="root" class="container">
<tables type="stats">
</tables>
</div>
Vue.component('tables', {
template: `
<table class="table">
<thead>
<tr>
<th v-for="headers in header">{{ headers }}</th>
</tr>
</thead>
<tfoot>
<tr>
<th v-for="footers in header">{{ footers }}</th>
</tr>
</tfoot>
<tbody>
<tr>
<th>1</th>
<td>Leicester City <strong>(C)</strong>
<slot></slot>
</tr>
</tbody>
</table>
`,
data () {
return {
standings: ['Rank', 'Team', 'Wins', 'Losses'],
stats: ['Number', 'Player', 'Position', 'RBI', 'HR']
};
},
computed: {
header() {
if (this.type == 'standings') {
return this.standings;
} else {
return this.stats;
}
}
},
props: {
type: { required: true }
}
});
If you change up your data structure slightly, you can remove your if statements.
data () {
return {
types:{
standings: ['Rank', 'Team', 'Wins', 'Losses'],
stats: ['Number', 'Player', 'Position', 'RBI', 'HR']
}
};
},
computed: {
header() {
return this.types[this.type]
}
},
props: {
type: { required: true }
}
Here is an example.
Vue.component('tables', {
template: `
<table class="table">
<thead>
<tr>
<th v-for="headers in header">{{ headers }}</th>
</tr>
</thead>
<tfoot>
<tr>
<th v-for="footers in header">{{ footers }}</th>
</tr>
</tfoot>
<tbody>
<tr>
<th>1</th>
<td>Leicester City <strong>(C)</strong>
<slot></slot>
</td>
</tr>
</tbody>
</table>
`,
data () {
return {
types:{
standings: ['Rank', 'Team', 'Wins', 'Losses'],
stats: ['Number', 'Player', 'Position', 'RBI', 'HR']
}
};
},
computed: {
header() {
return this.types[this.type]
}
},
props: {
type: { required: true }
}
});
new Vue({
el: "#root"
})
<script src="https://unpkg.com/vue#2.2.6/dist/vue.js"></script>
<div id="root" class="container">
<tables type="stats">
</tables>
</div>