Vue JS delete objects in array by unique id? - javascript

I'm using Vue.js to remove an object in my object array, problem is I can't find a way to delete the object by it's unique id. I'm using vue.js version 1. I also need a way to update that same object (it has to be reactive, so my view gets updated automatically).
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue Js - components</title>
</head>
<body>
<!-- index.html -->
<div id="app">
<div class="container-fluid">
<ul class="list-group">
<post v-for="posty in posts" :posty="posty" track-by="uuid"></post>
</ul>
</div>
</div>
<template id="my-component">
<div v-if="posty.votes === '15'">
<button v-on:click="testFunc(posty.uuid)">{{posty.title}}</button>
</div>
<div v-else>
<button v-on:click="testFunc(posty.uuid)">No</button>
</div>
</template>
<script src="vue-v1.js"></script>
<script>
Vue.component('post', {
template: "#my-component",
props: ['posty'],
methods: {
testFunc: function(index){
this.$parent.parentMethod(index);
}
}
});
var vm = new Vue({
el: "#app",
data: {
posts: [{ uuid: '88f86fe9d',
title: "hello",
votes: '15'
},
{
uuid: '88f8ff69d',
title: "hello",
votes: '15'
},
{
uuid: '88fwf869d',
title: "hello",
votes: '10'
}]
},
methods: {
parentMethod: function(index){
Vue.delete(this.posts, index);
}
}
});
</script>
</body>
</html>

https://jsbin.com/fafohinaje/edit?html,js,console,output
I don't know what should your Vue.delete method represent here, so instead you can go with array splice
methods: {
parentMethod: function(index){
this.posts.splice(index, 1)
}
}

Related

Vue.js does not update HTML

I have the following .html file in my local Vue.js project (it's a simplified version):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue.js</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<main>
<div id="myElement">
<div v-for="(properties, name) in list" v-bind:key="name">{{ name }}</div>
</div>
</main>
<script type="module">
import SomeClass from "./SomeClass.js";
const myClass = new SomeClass();
let app = new Vue({
el: "#myElement",
data: {
list: myClass.object
},
});
</script>
</body>
</html>
Inside the <script type="module"> tag I import SomeClass which includes a property called object:
export default class SomeClass {
constructor() {
this.object = {
name1: { ... },
name2: { ... },
...
};
}
}
The issue here is that Vue.js reacts to changes in object (if I check it using watch) but it does not update my HTML:
<div v-for"..." v-bind:key="...">...</div>.
What can I do to make Vue.js update my HTML when new properties added to object / old properties updated?
Vue has a method to add properties to items in the data attribute (so they keep reactivity). (More on this: https://v2.vuejs.org/v2/api/#Vue-set)
The snippet below may help you to see the method in action:
class SomeClass {
constructor() {
this.object = {
name1: {
name: 'name3'
},
name2: {
name: 'name4'
}
}
}
}
const myClass = new SomeClass()
new Vue({
el: "#app",
computed: {
// transforming array for reactive display
transformedObj() {
const ret = []
this.keyVal().forEach(e => {
e.forEach(el => {
ret.push(el)
})
})
return ret
}
},
data: {
object: myClass.object
},
methods: {
// transforming object for better display
keyVal() {
return Object.keys(this.object).map(e => {
return Object.entries(this.object[e]).map(([k, v]) => {
return `${k}: ${v}`
})
})
}
},
mounted() {
// add new property (reactive)
Vue.set(this.object.name1, 'newProperty', '2')
// or add new property (reactive)
this.$set(this.object.name2, 'localNewProperty', 45)
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<ul>
<li v-for="(item, key) in transformedObj">{{item}}</li>
</ul>
Object: {{transformedObj}}
</div>

How to bind input value to the return output of a method in Vue.js?

I am using a Vuetify checkbox component and I am trying to bind it's value to the output of my method but it is not working at all. I have tried it with a computed property but I could not pass and argument to it. And it is also not working if I use a method. Is there a way to dynamically assign a value to an input like this?
<div v-for="row in rows">
<v-checkbox :value="isSelected(row.id)"></v-checkbox>
</div>
data()
{
return {
rows: [{id: 22546}, {id: 3521}, {id: 15698}],
selected: [1259, 1898, 3521]
}
},
methods:
{
isSelected(id)
{
if (this.selected.indexOf(id) > -1) {
return true
} else {
return false
}
}
}
When I tried with v-model instead if :value it gave me this error:
<v-checkbox v-model="isSelected(row.id)"></v-checkbox>
isSelected(id)
{
return true
},
error
[Vue warn]: Failed to generate render function:
SyntaxError: missing ) after argument list in
Try with:
<v-checkbox :input-value="isSelected(row.id)"></v-checkbox>
You can use v-model with getter that encapsulates the logic that decides if checkbox needs to be checked:
let id = 1898;
new Vue({
el: '#app',
data() {
return {
rows: [{id: 22546}, {id: 3521}, {id: 15698}],
selected: [1259, 1898, 3521]
}
},
computed: {
val: {
get: function() {
return this.selected.indexOf(id) > -1;
},
set: function(newValue) {
console.log(newValue);
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.js"></script>
<link href="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.min.css" rel="stylesheet">
<div id='app'>
<div v-for="row in rows">
<v-checkbox v-model="val"></v-checkbox>
</div>
</div>
It works fine with v-model make sure your app is nested inside v-app tag :
Vue.config.productionTip = false;
Vue.config.devtools = false;
new Vue({ el: '#app',
data()
{
return {
rows: [{id: 22546}, {id: 3521}, {id: 15698}],
selected: [1259, 1898, 3521]
}
},
})
<html>
<head>
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
</head>
<body>
<div id="app">
<v-app>
<div v-for="(row,i) in rows">
<v-checkbox
v-model="selected[i]"
:label="`${selected[i] > -1 ? true : false} `"
></v-checkbox>
</div>
</v-app>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.js"></script>
</body>
</html>
Ok, I figure it out,
v-model is 2-way binding, you can only bind it to variable or computed property with getter and setter.
value provided by vue-checkbox component is not the value of checkbox checked/unchecked state. You can set initial state with :input-value.

what's wrong with this vuejs "prop down" example

i am new to vuejs, when i go over its document, I can't get this sample code from its "component" section work:
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="example">
<input type="text" v-model="parentMsg">
<br>
<child v-bind:message="parentMsg"></child>
</div>
Javascript:
Vue.component('child', {
props: ['message'],
template: '<span>testing: {{ message }}</span>'
})
new Vue({
el: '#example'
})
my understand: the value of the model can be passed on to the message properties of the child component, a string of the same content will be shown after "testing: " as soon as I key in anything in the input textbox. It didn't happen.
I tested the code from jsfiddle
The Vue instance of #example should have data parentMsg. Then, child and parent can use it. So, you need to add data at Vue instance.
new Vue({
el: '#example',
data: function() {
return { parentMsg: "Hi" };
}
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="https://unpkg.com/vue"></script>
</head>
<script type="text/javascript">
Vue.component('child', {
props: ['myMessage'],
template: '<div>{{ myMessage }}</div>'
});
Vue.component('two-items-child', {
props: ['firstName', 'lastName'],
template: '<div><div>{{ firstName }}</div><div>{{ lastName }}</div></div>'
});
</script>
<body>
<div id="app">
<input id="inputParent" type="text" placeholder="parent" v-model="parentMsg">
<br>
<child my-message="Hi Vue."></child>
<two-items-child v-bind="wholeObj"></two-items-child>
<child :my-message="parentMsg"></child>
</div>
<script type="text/javascript">
// root instance
var vm = new Vue({
el: '#app',
data: {
parentMsg: "first msg",
wholeObj: {
firstName: "Hong",
lastName: "Gil-dong"
}
}
});
</script>
</body>
</html>
Check above example at example.
The example has data as an object but it is not a good way. Also check it data must be a Function
The parent component needs to have parentMsg as a data attribute, otherwise it won't be reactive.
new Vue({
el: '#example',
data() {
return {
parentMsg: ''
}
}
})

Leveraging the v-for loop for places outside the v-for

While looping on array of sales , i need to capture the object of which salesPerson === "bar" and print its sellValue outside the v-for block.
Of course i can't access the array in hard-coded way. i have to assume that the position of the object i'm looking for is random.
also, i can't add another loop on top of the one loop that already exist here. (v-for is a loop obviously).
i need way to do achieve it.
here is an example component:
<template>
<div id="app">
<!-- i need to print here the sellValue of 'bar' -->
<p v-for="(sell,index) in sales"
:key="index">{{sell.sellValue}}</p>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
sales: [
{
salesPerson: 'foo',
sellValue: 1
},
{
salesPerson: 'bar',
sellValue: 2
}
]
}
}
}
</script>
You can try using a custom HTML tag (not registered as a component for Vue), it's quite "ugly" but that is the only solution I could think of (beware of Vue's warnings if not disabled) :
<template>
<div id="app">
<uglyTag v-for="(sell,index) in sales" :key="index">
{{sell[ sell.findIndex( e=>e.salesPerson==="bar" ) ].sellValue}}
<p>{{ sell.sellValue }}</p>
</uglyTag>
</div>
</template>
Another solution would be to rethink the construction of your data so you could have (but still needs the uglyTag method) :
data(){
return {
salesTitle: 2,
sales: [
{
salesPerson: 'foo',
sellValue: 1
},
{
salesPerson: 'bar',
sellValue: 2
}
]
}
}
and
<template>
<div id="app">
<uglyTag v-for="(sell,index) in sales" :key="index">
{{ salesTitle }}
<p>{{ sell.sellValue }}</p>
</uglyTag>
</div>
</template>
Perhaps I didn't understand the question correctly, but you are still in the same scope of your component. Why don't you add a getter for the value you are interested in and display it where you want.
Vue.component('my-template', {
template: ' <div id="app">\
<!-- i need to print here the sellValue of \'bar\' -->\
<p>{{ saleValue }}</p>\
<p v-for="(sell,index) in sales" :key="index">{{sell.sellValue}}</p>\
</div>',
data: function() {
return {
sales: [{
salesPerson: 'foo',
sellValue: 1
}, {
salesPerson: 'bar',
sellValue: 2
}]
}
},
computed: {
saleValue: function() {
return this.sales.filter(function(val) {
return val.salesPerson === 'bar';
})[0].sellValue;
}
}
});
var vm = new Vue({
el: '#vm',
data: {}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="vm">
<my-template></my-template>
</div>

Property or method "value" is not defined on the instance in vue js 2 nested component

I wrote a basic vue js 2 basic example to test nested components.
Below is components and template structure.
Vue.component('form-com', {
template: '#form',
props: ['value'],
methods: {
onInput: function (event) {
this.$emit('input', event.target.value);
}
}
});
Vue.component('message-com', {
template: '#message',
data: function () {
return {
msg: 'Hello'
}
},
props: ['user']
});
Vue.component('welcome-com', {
template: '#welcome',
data: function () {
return {
user: 'ahmad'
}
}
});
new Vue({
el: '#container'
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.js"></script>
<!--Form Template-->
<template id="form">
<div>
<div class="form-control">
<label>Enter Your Name:</label>
<input type="text" v-bind:value="value" :input="onInput">
</div>
</div>
</template>
<!--Hello Message Template-->
<template id="message">
<div>
<h3>{{msg}} {{user}}</h3>
</div>
</template>
<template id="welcome">
<div>
<form-com :value="value"></form-com>
<br>
<message-com :user="user"></message-com>
</div>
</template>
<div id="container">
<welcome-com></welcome-com>
</div>
But when run app in browser this error is shown:
[Vue warn]: Property or method "value" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
found in
---> <WelcomeCom>
<Root>
what is problem?
Update:
I just Rewrite this Fiddle from one of chapters of Learning Vue.js 2. I just rename some parameters and component and templates names. but when I copy main fiddle to my code all things worked.
In your form-com component you can set up a v-model which binds the input value and set up a watcher to observer the changes in the input which in turn emits an custom-event which telss the parent comonent that a change has taken place.
Vue.component('form-com', {
template: '#form',
data(){
return{
myInput:''
}
},
watch: {
myInput: function (inputVal) {
this.$emit('input', inputVal);
}
}
});
Vue.component('message-com', {
template: '#message',
data: function () {
return {
msg: 'Hello'
}
},
props: ['user']
});
Vue.component('welcome-com', {
template: '#welcome',
data: function () {
return {
user: 'ahmad'
}
},
methods:{
updateUser(value){
this.user = value;
}
}
});
new Vue({
el: '#container'
})
You can listen to the events emitted from the child **form-com ** component using v-on:input or shorthand #input directly in the parent template (welcome component) where the child component is used.
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.js"></script>
<!--Form Template-->
<template id="form">
<div>
<div class="form-control">
<label>Enter Your Name:</label>
<input type="text" v-model="myInput">
</div>
</div>
</template>
<!--Hello Message Template-->
<template id="message">
<div>
<h3>{{msg}} {{user}}</h3>
</div>
</template>
<template id="welcome">
<div>
<form-com #input="updateUser($event)" ></form-com>
<br>
<message-com :user="user"></message-com>
</div>
</template>
<div id="container">
<welcome-com></welcome-com>
</div>
Here is the jsFiddle
If you don't want to use a watcher then you can do it using computed setter . Have a look at the fiddle which is using a computed-setter
You are missing in your Component 'welcome-com' the value object:
Vue.component('welcome-com', {
template: '#welcome',
data: function () {
return {
value: '',
user: 'ahmad'
}
}
});

Categories

Resources