I have component like this:
Vue.component('mcp-item', {
template: '#mcp-item-template',
data() {
return {
name: "MCP v2",
version: "2.0",
imei: 'XXXXXX XX XXXXXX X',
relays: [
{ name : "REL1", state : 0 },
{ name : "REL2", state : 0 }
],
inputs: [
{ name: "BP1", state: 0, color: "#CC0000" },
{ name: "BP2", state: 0, color: "#CC0000" },
{ name: "BP3", state: 1, color: "#00CC00" },
{ name: "BP4", state: 0, color: "#CC0000" },
{ name: "BP5", state: 0, color: "#CC0000" },
{ name: "BP6", state: 0, color: "#CC0000" }
],
}
},
methods: {
reboot: function (event) { alert( this.imei) }
}
})
And somewhere in compoment template:
<table>
<thead>
<tr>
<th>Input</th>
<th>State</th>
</tr>
</thead>
<tbody v-for="input in inputs" :key="input.name">
<tr>
<td :style="{ 'color': input.color}">{{input.name}}</td>
<td>{{input.state}}</td>
</tr>
</tbody>
</table>
As you can see, now I have dedicated color field in my object (which is element of inputs array in data):
JS:
{ name: "BP1", state: 0, color: "#CC0000" }
HTML:
<td :style="{ 'color': input.color}">{{input.name}}</td>
I want to get rid of this extra property, but I can't figure out how can I use computed property inside of v-for loop to make red color for state==0 and green for state==1.
Rather than creating a computed property or adding the logic into the template, I would create a method getColor(state) which looks like this:
getColor(state) {
let color = '';
if (state === 0) {
color = 'red';
} else if (state === 1) {
color = 'green';
}
return { color };
}
Or if the only values are 0 and 1 you could shorten this to something like:
getColor(state) {
const color = state === 0 ? 'red' : 'green';
return { color };
}
then call it like this:
<td :style="getColor(input.state)">{{input.name}}</td>
maybe computed property is an overkill - what about simple condition:
<td :style="{ 'red': input.state === 0, 'green': input.state === 1}">...// </td>
Related
These are the issues i'm getting
Here below is the code that produces the problems, this part in particular:
When ever i;m trying to filter campaigns using company_id and product_id with v-if the problem occurs. Almost the same exact code works a few lines above filtering products. I have no idea what to do next. I tried refs and putting the mocked that into reactive variable and computeing it with a function but it didn;t work out.
<script setup>
import CompanyItem from "./CompanyItem.vue";
import ProductItem from "./ProductItem.vue";
import CampaignItem from "./CampaignItem.vue";
import { useCurrentCompanyStore } from "../stores/currentCompanyStore.js"
import { useCurrentProductStore } from "../stores/currentProductStore.js"
const companyStore = useCurrentCompanyStore();
const productStore = useCurrentProductStore();
const companies =
[
{
company_id: 1,
name: 'Domain of Man',
fund_balance: 100000,
products_list: [
{
product_id: 1,
name: 'gate'
},
{
product_id: 2,
name: 'exploration ship'
},
{
product_id: 3,
name: 'artifacts'
}
]
},
{
company_id: 2,
name: 'Hegemony',
fund_balance: 200000,
products_list: [
{
product_id: 1,
name: 'toothbrash'
},
{
product_id: 2,
name: 'ore'
},
{
product_id: 3,
name: 'food'
}
]
},
];
const campaigns = [
{
campaign_id: 1,
company_id: 1,
product_id: 1,
campaign_name: "Gates for everyone",
keywords: [
"one for each",
"limited offer"
],
bid_amount: 25000,
status: true,
town: "Tarnow",
radius: "10"
},
{
campaign_id: 2,
company_id: 1,
product_id: 3,
campaign_name: "Get them while they last",
keywords: [
"rare",
"one for each",
"limited offer"
],
bid_amount: 25000,
status: false,
town: "Tarnow",
radius: "10"
},
{
campaign_id: 3,
company_id: 3,
product_id: 1,
campaign_name: "Let the shine power your ship",
keywords: [
"electricity",
"green technology",
],
bid_amount: 25000,
status: true,
town: "Tarnow",
radius: "10"
}
];
</script>
<template>
<div class="container">
<div class="companies" >
<CompanyItem v-for="company in companies" v-bind:key="company.company_id" :company-id="company.company_id">
<template #name>
{{ company.name }}
</template>
<template #budget>
{{ company.fund_balance }}
</template>
</CompanyItem>
</div>
<div class="products">
<template v-for="company in companies">
<ProductItem
v-for="product in company.products_list"
v-bind:key="product.product_id"
:id="company.company_id"
v-if="companyStore.companyId === company.company_id"
:product-id="product.product_id">
<template #name>
{{ product.name }}
</template>
</ProductItem>
</template>
</div>
<div class="campaigns">
<CampaignItem
v-for="campaign in campaigns"
v-if="companyStore.companyId === campaign.company_id"
v-bind:key="campaign.campaign_id"
:id="campaign.campaign_id"
>
<template #name>
{{campaign.campaign_name}}
</template>
</CampaignItem>
</div>
</div>
</template>
<style scoped>
.container {
width: 100%;
height: 100%;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: auto;
grid-template-areas:
"companies products campaigns";
}
.companies {
grid-area: companies;
display: flex;
flex-direction: column;
overflow: hidden;
}
.products {
grid-area: products;
}
.campaigns {
grid-area: campaigns;
}
</style>
Here are stores:
import { defineStore } from 'pinia'
export const useCurrentCompanyStore = defineStore({
id: 'currentComapny',
state: () => ({
companyId: -1
}),
getters: {
getCompanyId: (state) => state.companyId
},
actions: {
change(newCompanyId) {
this.companyId = newCompanyId;
}
}
})
import { defineStore } from 'pinia'
export const useCurrentProductStore = defineStore({
id: 'currentProduct',
state: () => ({
productId: -1
}),
getters: {
getCompanyId: (state) => state.productId
},
actions: {
change(newProductId) {
this.productId = newProductId;
}
}
})
Btw. if anybody wants to run it themself here is the git repo, its feature/frontend branch:
https://github.com/kuborek2/campaign_planer
You must not use v-if and v-for on the same element because v-if will always be evaluated first due to implicit precedence.
And exactly because of that, you are facing this error of undefined company_id as v-for is not executed yet and v-if is trying to access it.
Make the changes as suggested below and it should fix your error.
<CampaignItem
v-for="campaign in campaigns"
:key="campaign.campaign_id"
:id="campaign.campaign_id"
>
<template v-if="companyStore.companyId === campaign.company_id" #name>
{{campaign.campaign_name}}
</template>
</CampaignItem>
Click here for the reference
Here's a part of my grid (CRUD) component:
<template>
<table class="MyComponent table">
<thead>
<tr>
<th width="30px">
<b-form-checkbox v-model="allChecked" />
</th>
</tr>
</thead>
<tbody>
<tr v-for="(record, index) in records" :key="index">
<td width="30px">
<b-form-checkbox :value="record['id']" v-model="checkedRows" />
</td>
</tr>
</tbody>
</table>
</template>
<script>
export default {
name: "MyComponent",
components: {
},
props: ['config'],
data() {
return {
records: [{
id: 1
}, {
id: 2
}, {
id: 3
}, {
id: 4
}, {
id: 5
}, {
id: 6
}],
checkedRows: []
}
},
computed: {
allChecked: {
get() {
return this.records.length == this.checkedRows.length
},
set(v) {
if(v) {
this.checkedRows = [];
for(var i in this.records) {
this.checkedRows.push(this.records[i]['id'])
}
}
else {
this.checkedRows = [];
}
}
}
}
};
</script>
As you can see, I would like to achive a standard, widely used functionality: The user can check multiple rows and do some operation with the selected rows. The problem is with the "check all" checkbox on the top of the table. When I check all, then I remove the tick from only one checkbox below, it unchecks all the checkboxes on page.
I understand why its happening: When I remove a tick from on of the checkboxes below, the "this.records.length == this.checkedRows.length" condition will no longer be true, so the "allChecked" computed variable will be set to false, therefore the top checkbox will set to unchecked. The problem is: when the top checkbox will be unchecked, then all of the checkboxes will be unchecked as well, because of the "set" part of the computed variable.
Is there a clean way to solve this problem in Vue?
I'm not sure what you want to do with the checked rows, but maybe this will be better:
<b-form-checkbox :value="record['id']" v-model="record.checked" />
Then add to your objects in records a checked property.
records: [
{
id: 1,
checked: false
},
...
]
and if you need a list of checked records you might do a computed property:
computed: {
checkedRecords() {
return this.records.filter(record => record.checked);
}
}
and for checking-unchecking all you just iterate over all records:
<b-form-checkbox #change="clickedAll" />
methods: {
clickedAll(value) {
this.records = this.records.map(record => {
record.checked = value
return record
}
}
}
OK, meanwhile I solved the problem. Here's my solution. Thanks #Eggon for your help, you gave the idea to use the #change method.
<template>
<table class="MyComponent table">
<thead>
<tr>
<th width="30px">
<b-form-checkbox v-model="allChecked" #change="checkAll" />
</th>
</tr>
</thead>
<tbody>
<tr v-for="(record, index) in records" :key="index">
<td width="30px">
<b-form-checkbox :value="record['id']" v-model="checkedRows" />
</td>
</tr>
</tbody>
</table>
</template>
<script>
export default {
name: "MyComponent",
components: {
},
props: ['config'],
data() {
return {
records: [{
id: 1
}, {
id: 2
}, {
id: 3
}, {
id: 4
}, {
id: 5
}, {
id: 6
}],
checkedRows: []
}
},
methods: {
checkAll(value) {
if(!value) {
this.checkedRows = [];
return ;
}
var newCheckedRows = [];
for(var i in this.records) {
newCheckedRows.push(this.records[i].id)
}
this.checkedRows = newCheckedRows;
}
},
computed: {
allChecked: {
get() {
return this.records.length == this.checkedRows.length
},
set() {
}
}
}
};
</script>
I have a list of users. Click on a specific user the user edit form is populated but the only way I can get a value in the input is by putting the return in the
<input v-model="this.cl.data.USER_RLTNP_SCTY_ACCS_GRP.User_Name" ref=User_Name>
if I do
<input v-model="User_Name "v-bind:value="this.cl.data.USER_RLTNP_SCTY_ACCS_GRP.User_Name">
Nothing appears in the input.
If I use the v-model="this.cl.data....." the value of the user_name is in the input I'm not sure how the value is passed to my updateUser() function because normally I would use username = this.User_Name
Using apollo and Graphgl for the querying
<template>
<div>
<v-text-field label="User Name" ref="User_Name" v-model="User_Name" v-bind:value="this.cl.data.USER_RLTNP_SCTY_ACCS_GRP[0].User_Name" id="" placeholder="User Name"></v-text-field>
<v-btn v-on:click="editUser()">Edit</v-btn>
</div>
</template>
<script>
import gql from graphql-tag import { SCTY_ACCS_GRP, SCTY_ACCS_GRP_USER }
from './gqlqueries'
export default {
data: () => ({
users: [],
cl: '',
user_form: false,
user_name: ''
}),
methods: {
editForm: async function(userid){
this.cl = await this.$apollo.query({query : SCTY_ACCS_GRP_USER, variables: {id : userid}})
this.user_form = true
console.log(this.cl)
alert(this.cl.data.USER_RLTNP_SCTY_ACCS_GRP[0].User_Name)
},
editUser(){
this.user_name = this.User_Name
alert(this.user_name)
}
},
mounted: async function() {
this.users = await this.$apollo.query({ query: SCTY_ACCS_GRP })
// console.log(this.users.data.USER_RLTNP_SCTY_ACCS_GRP)
} }
</script>
I would assume the <input v-bind:value="this.cl.data...."> Would populate once The editForm() function is triggered. So how do I get the value of the User_Name when the editUser button is clicked
You could try list rendering.
A little example :
https://jsfiddle.net/L4xu9r5g/7/
var mapp = new Vue({
el: "#app",
data: {
currentuserinfo : undefined,
users: [
{ name: "John Doe", id : 12},
{ name: "Black Jack", id : 932},
{ name: "White Jack", id: 342}
],
userinfo:
[
{ userid: 12, favcolor: 'green', age : 23},
{ userid: 932, favcolor: 'red', age : 11},
{ userid: 342, favcolor: 'blue', age : 12}
]
},
methods:
{
selectuser : function(id)
{
//your query here
this.currentuserinfo = this.userinfo.filter(u => u.userid ==id)[0];
}
}
})
td, th
{
padding: 5px;
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<table>
<tbody>
<tr>
<th>Name</th>
<th>Select User</th>
</tr>
<tr v-for="user in users" :key="user.id">
<td>{{user.name}}</td>
<td><button #click="selectuser(user.id)">Select User</button></td>
</tr>
</tbody>
</table>
<div v-if="currentuserinfo === undefined">
Please select a user
</div>
<div v-if="currentuserinfo != undefined">
<span>Favourite color : {{currentuserinfo.favcolor}}</span><br/>
<span>Age : {{currentuserinfo.age}}</span><br/>
</div>
</div>
I am trying to create a reusable "Check All" solution for displaying a list of objects retrieved from an API.
I really like the get/set methods of computed properties that I use in this example here, https://codepen.io/anon/pen/aLeLOZ but I find that rewriting the same function over and over again and maintaining a seperate checkbox state list is tedious.
index.html
<div id="app">
<input type="checkbox" v-model="selectAll1"> Check All
<div v-for="person in list1">
<input type="checkbox" v-model="checkbox" :value="person.id">
<span>{{ person.name }}</span>
</div>
<hr/>
<input type="checkbox" v-model="selectAll2"> Check All
<div v-for="person in list2">
<input type="checkbox" v-model="checkbox2" :value="person.id">
<span>{{ person.name }}</span>
</div>
</div>
main.js
new Vue({
el: '#app',
data () {
return {
list1: [
{ id: 1, name: 'Jenna1'},
{ id: 2, name: 'Jenna2'},
{ id: 3, name: 'Jenna3'},
{ id: 4, name: 'Jenna4'}
],
list2: [
{ id: 1, name: 'Mary1'},
{ id: 2, name: 'Mary2'},
{ id: 3, name: 'Mary3'},
{ id: 4, name: 'Mary4'}
],
checkbox: [],
checkbox2: []
}
},
computed: {
selectAll1: {
get: function () {
return this.list1 ? this.checkbox.length === this.list1.length : false
},
set: function (value) {
let selected = []
if (value) {
this.list1.forEach(function (bf) {
selected.push(bf.id)
})
}
this.checkbox = selected
}
},
selectAll2: {
get: function () {
return this.list2 ? this.checkbox2.length === this.list2.length : false
},
set: function (value) {
let selected = []
if (value) {
this.list2.forEach(function (bf) {
selected.push(bf.id)
})
}
this.checkbox2 = selected
}
},
}
});
How can I make a resuable selectAll() function that will work in this example that can be included as often as needed?
Is it possible to make a class that can maintain the check box state for each list and still function as a computed property to make use of the v-model directive?
It's not the same at all, but a method based solution would be
methods: {
selectUs: function(){
if (this.checkbox.length <= this.list1.length) {
this.checkbox = Array.from(Array(this.list1.length + 1).keys())
} else {
this.checkbox = []
}
}
}
with #click="selectUs" instead of v-model="selectAll1"
(you could keep the get part of your computed properties to keep track of whether all are selected, and then use if (selectAll1) { etc } in the method)
I want to make pagination for my table but, when I run, my data does not appear anything. I am confused my code which is wrong perhaps here anyone can help me.
var newVue = new Vue({
el: '#app',
data: {
items: [
{ name: "item #1" }, { name: "item #2" }, { name: "item #3" }, { name: "item #4" },
{ name: "item #5" }, { name: "item #6" }, { name: "item #7" }, { name: "item #8" },
{ name: "item #9" }, { name: "item #10" }, { name: "item #11" }, { name: "item #12" }
],
start: 0,
limit: 2,
pagination: null,
total: 12
},
computed: {
filtered: function(){
return this.items.slice(0, this.limit)
}
},
mounted: function() {
this.limit = parseInt(this.pagination);
},
watch: {
pagination: function() {
this.limit = parseInt(this.pagination);
if(this.limit != this.start && this.start > 0)
this.start = parseInt(this.pagination);
this.limit = this.start + parseInt(this.pagination);
}
},
methods: {
paginate: function(direction) {
if(direction === 'next') {
this.start += parseInt(this.pagination);
this.limit += parseInt(this.pagination);
}
else if(direction === 'back') {
this.limit -= parseInt(this.pagination);
this.start -= parseInt(this.pagination);
}
},
},
filters: {
paginate: function(array, start, limit) {
return array.slice(start, limit);
}
}
});
.pager button {
background: #fff;
border: 1px solid #ddd;
border-radius: 15px;
color: #337AB7;
padding: 7px 13px;
text-align: center;
}
.pager button:hover {
background: #eee;
cursor: pointer;
outline: none;
}
.pager button:disabled {
background: #eee;
color: #bbb;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha/css/bootstrap.min.css" rel="stylesheet"/>
<div id="app">
<table class="table table-striped table-advance table-table-hover table-bordered">
<thead>
<th>Item</th>
</thead>
<tbody>
<tr v-for="item in filtered | paginate">
<td>{{ item.name }}</td>
</tr>
</tbody>
</table>
<div class="row">
<div class="col-md-12 center">
<ul class="pager">
<li>
<button #click="paginate('previous')" :disabled="start <= 0">
Previous
</button>
</li>
<li>
<button #click="paginate('next')" :disabled="limit >= total">
Next
</button>
</li>
</ul>
</div>
</div>
</div>
<script src="https://unpkg.com/vue#2.1.10/dist/vue.js"></script>
There are few problems with your code, after removing those your code works as can be seen here in fiiddle.
You dont need | paginate with v-for as filtered is computed property which will give you paginated result
<tbody>
<tr v-for="item in filtered | paginate">
<td>{{ item.name }}</td>
</tr>
</tbody>
in filtered you needed to pass correct params in slice method.
computed: {
filtered: function(){
return this.items.slice(this.start, this.limit)
}
},`