Vue push unique values to a list - javascript

var vue_app = new Vue({
el: '#id1',
data: {
v1:[],
},
methods:{
pushUnique: function() {
this.v1.push({'id':1,'name':'josh'});
this.v1.push({'id':1,'name':'josh'}); //this should not work.
},
},
});
In above code the second push should not execute. I would like to keep id unique. How can this be done in Vue.
THanks

I would move to storing data in an object (keyed by id) and use a computed property to produce your v1 array. For example
data: {
v1obj: {}
},
computed: {
v1 () {
return Object.keys(this.v1obj).map(id => ({ id, name: this.v1obj[id] }))
}
}
Then you can use methods like Object.prototype.hasOwnProperty() to check for existing keys...
methods: {
pushUnique () {
let id = 1
let name = 'josh'
if (!this.v1obj.hasOwnProperty(id)) {
this.v1obj[id] = name
}
}
}

Related

get index from watched array

I have a vue application where I watch an array for changes. This is working fine. But I'm not sure how to get the index of the array item which has changed, as the watch callback only passes in the old/new values.
Demo: http://jsfiddle.net/q3zd4fmv/
Simplified Example:
new Vue({
el: '#demo',
data: {
things: [{foo:1}, {foo:2}]
},
watch: {
things: {
handler: function (val, oldVal) {
alert('a thing changed')
},
deep: true
}
},
methods: {
change: function () {
this.things[0].foo = 5
}
}
})
Unfortunately, not out of the box. Using a combination of argument destructuring and a custom watch function, you can achieve something that should do it. For example;
new Vue({
el: '#demo',
data: {
things: [{foo:1}, {foo:2}]
},
methods: {
change: function (...args) {
let [thing, after, before] = args;
console.log(thing);
}
},
mounted: function(){
this.things.forEach(thing => {
this.$watch(() => thing, this.change.bind(null, thing))
});
}
})

Bootstrap-vue - Setting table variant dynamically

So I'm using Bootstrap Vue with this test app. I'm trying to change the variant of a table cell depending on the value of it. Unfortunately, the variant parameter will not take a function, so I'm out of ideas on how to achieve this.
This is my code:
var app = new Vue({
el: '#app',
data: {
items: [], //Will be populated through AJAX
fields: [
{
key: 'Vendedor',
label: 'Vendedor'
},
{
key: 'OBJETIVO',
label: 'Objetivo',
formatter: (value) => { return parseFloat(value).toFixed(2)},
variant: estiloObjetivo //THIS IS NOT WORKING
}
]
},
methods: {
Cargar: function () {
var salesperson = getCookie('salespersonCode');
var url_servicio = 'http://MywebService/';
var self = this;
$.ajax({
type: 'GET',
url: url_servicio + 'ventas/' + salesperson,
dataType: "json", // data type of response
success: function(data){
self.items = data
}
});
},
estiloObjetivo (value) {
if value > 0 //I need my cell variant to change depeding on this value
return 'danger'
else
return 'success'
}
}
})
This is my HTML part:
<div id="app">
<button v-on:click="Cargar">Cargar</button>
<b-table striped hover :fields="fields" :items="items"></b-table>
</div>
Any ideas on how to style a Bootstrap-vue cell dynamically?
This is the way it's done in the docs, it's actually set in the "items" array, but how is this useful in cases like mine where I get the data from a web service?:
{
salesperson: 'John',
Objetivo: 2000,
_cellVariants: { salesperson: 'success', Objetivo: 'danger'}
},
So I guess what I need is a way to set the I need is to set the _cellVariants parameter of each element in the 'items' array.
You likely need a computed property. Computed properties automatically update on changes to the reactive variables that they depend on.
The following example implements a computed property, styledItems, which you must use in place of items in the template. It returns a 1-deep copy of items, i.e. a new array containing a copy of each item, with the extra _cellVariants property added.
new Vue({
data: {
items: [ /* your data here */ ]
},
methods: {
estiloObjetivo: value => (value > 0) ? 'danger' : 'success'
},
computed: {
styledItems() {
return this.data.map(datum =>
Object.assign({}, datum, {
_cellVariants: {
Objetivo: this.estiloObjetivo(datum.Objetivo)
}
})
}
})
If you want to add variant to items you could use a computed property called cptItems and define it as follows:
computed:{
cptItems(){
return this.items.map((item)=>{
let tmp=item;
item.OBJETIVO>0?tmp.variant='danger':tmp.variant='success';
return tmp;
})
}
and use that property inside your template like :
<b-table .... :items="cptItems"></b-table>
I was sure the answers above would solve my own issue but they did not. I found a different way to color table cells: https://github.com/bootstrap-vue/bootstrap-vue/issues/1793
This is aside from using variants to color a table cell. Instead, we utilize tdclass and a function.
<script>
new Vue({
el: '#itemView',
data() {
return {
fields: [
{
key: 'Objetive',
sortable: true,
thClass: 'text-nowrap',
tdClass: (value, key, item) => {
return 'table-' + this.getColor(item);
}
}
],
};
},
methods: {
getColor(item) {
return item.Objetive > 0 ? 'danger' : 'success';
},
},
});
</script>
For my own use-case, I needed to compare two cells of the same row, then apply a class to one.
...
{
key: 'DEMAND_QTY',
sortable: true,
thClass: 'text-nowrap',
tdClass: (value, key, item) => {
return 'table-' + this.demandStatusColor(item);
},
},
{ key: 'TOTAL_DEMAND', sortable: true, thClass: 'text-nowrap' },
],
};
},
methods: {
demandStatusColor(item) {
return item.DEMAND_QTY < item.TOTAL_DEMAND ? 'danger' : 'success';
},
}
...
Perhaps this will help someone, if not OP.
#John answer worked for me. I don't have enough reputation to make comment or useful
tdClass: (type, key, item) => {
switch (type) {
case "value":
return "bg-warning text-white";
break;
case "value":
return "bg-danger text-white";
break;
case "value":
return "bg-info text-white";
break;
default:
break;
}
},

Vuex syntax error with localcomputed function in combination with getter and setter

I get syntax error when combining Vuex localcomputed object with get/set together with store mappings.
As seen in the Vuex docs you can map your store methods like this with the object spread operater like:
computed: {
localComputed () { /* ... */ },
// mix this into the outer object with the object spread operator
...mapState({
// ...
})
}
https://vuex.vuejs.org/en/state.html##object-spread-operator
Also you can create computed setters like:
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
https://v2.vuejs.org/v2/guide/computed.html#Computed-Setter
I can create either a computed object with get set or have mapState/mapGetters etc. - but not in combination. It break the syntax (error is: expected function name after the function declarations).
computed: {
localComputed () {
localMethod: {
get: function () {
// retrieve
},
set: function (newContent) {
// set
}
}
}, ...mapState([
]), ...mapGetters([])
}
How do i combine these two?
You are trying to define localMethod inside localComputed.
In the docs, localComputed is just an example name for a computed property in your component. You don't have to put all of your other local computed properties within it.
Therefore, you should be able to do the following:
computed: {
localComputed: {
get: function () {
// retrieve
},
set: function (newContent) {
// set
}
},
anotherLocalComputed: {
get: function () {
// retrieve
},
set: function (newContent) {
// set
}
},
...mapState([]),
...mapGetters([])
}
Here is the working sample. I have been using this approach for more than a year
// in some utils/vuex.js file
export const mapSetter = (state, setters = {}) => (
Object.keys(state).reduce((acc, stateName) => {
acc[stateName] = {
get: state[stateName],
};
// check if setter exists
if (setters[stateName]) {
acc[stateName].set = setters[stateName];
}
return acc;
}, {})
);
In your component.vue file
import { mapSetter } from 'path/to/utils/vuex.js';
...
export default {
name: 'ComponentName',
computed: {
...mapSetter(
mapState({
result: ({ ITEMS }) => ITEMS.result,
total: ({ ITEMS }) => ITEMS.total,
current: ({ ITEMS }) => ITEMS.page,
limit: ({ ITEMS }) => ITEMS.limit,
}),
{
limit(payload) {
this.$store.dispatch({ type: TYPES.SET_LIMIT, payload });
},
},
)
},
}
now v-model binding should work.

normalizr v3 and JSON api

I want to normalise the responses I receive from an API. A typical response could look something like this:
// Get all projects
{data:[
{
id: 1
...
team:{
data: {
id:15
...
}
}
},
{
id:2,
....
},
{
id:3,
...
}
]}
How do I write my schemas so that it removes the 'data' container?
Currently, my schema looks like:
export const project = new schema.Entity('projects', {
team: team, // team omitted
},
{
processStrategy: (value, parent, key) => parent.data
}
)
export const arrayOfProjects = new schema.Array(project)
And I am using it like:
const normalizedProjects = normalize(jsonResponse, arrayOfProjects)
normalizedProjects then looks like this:
{
entities:{
projects:{
undefined:{
0:{
team:{
data:{
id:15,
...
}
}
},
1:{...},
2:{...}.
...
50:{...},
}
}
},
result:[] // length is 0
}
I'm not sure why the list of projects is contained in 'undefined', either?
I also use json_api schema.
How about like this?
const projectsSchema = new schema.Entity('projects', {}, {
processStrategy: processStrategy
});
export const processStrategy = (value, parent, key) => {
const attr = value.attributes;
delete value.attributes;
return { ...value, ...attr };
};
export const fetchProjectsSchema = {
data: [projectsSchema]
}
Each of your entity schema that you want to have the data omitted (or anything else fundamentalyl changed) needs to include a processStrategy that you write to remove or change any data. (see more examples in the tests)

Moving items between arrays in vue.js

I have two arrays with custom components in each.
List a is a search result. List b is a list of selected items.
Each component has a template that renders the item in the array.
So... the trouble I'm having is, once I have my list in list a, i want to click a link and have it add to list b. But when I try to add the item, I'm being told that Cannot read property 'push' of undefined
Here's my entire Vue. What am I doing wrong?
new Vue({
el: '#search',
data: {
query: '',
listA: '',
listB: ''
},
methods: {
search: function(event) {
if (this.query != "") {
this.$http({url: '/list-a?search=' + this.query, method: 'GET'}).then(function(response) {
this.listA = response.data
});
};
event.preventDefault();
}
},
components: {
listaitem: {
template: '#listaitem-template',
props: ['lista-item'],
methods: {
selected: function(listaitem) {
// When clicked, this will add this listaitem to listB
this.listB.push(listaitem);
}
}
},
listbitem: {
template: '#listbitem-template',
props: ['listbitem']
}
}
});
You should initialize listA and listB as empty arrays instead of empty strings like
data: {
query: '',
listA: [],
listB: []
}
This will allow you use this.listB.push(listaitem); in the listaitem component without throwing an error

Categories

Resources