VueJS - pass data to component with existing row - vue.js

Here's my fiddle
data: {
address_components: [
{ id:1, city_id:1, area_id: 100}
],
city_id:0,
area_id:0,
sort:1
},
I need to pass data to existing row, is that incorrect?
And if I add a new row, the id should be '2'
like this
what is the good way to do this? :'-(

modify your code like this:
addComponent() {
this.address_components.push({id:++this.sort})
},

How about creating a list in your data() and just push a object there.
<ul id="example-1">
<li v-for="item in items">
{{ item.message }}
</li>
</ul>
var example1 = new Vue({
el: '#example-1',
data: {
items: []
},
methods:
{
Add:function()
{
//process the inputs and push it to items list
this.items.push({ message: "Hi"})
}
}
})
Did not tested this and just created from example from Vue Documentation but you got the idea. Pushing a new item in list will automatically updates the component or the element

Related

Getting data from component VueJS

Very simple question. I'm learning VueJS and have created a simple component:
Vue.component('blog-post', {
props: ['title'],
template: '<h3>{{ title }}</h3>'
})
I then have parsed some data to it like this:
new Vue({
el: '#blog-post-demo',
data: {
posts: [
{ id: 1, title: 'My journey with Vue' },
{ id: 2, title: 'Blogging with Vue' },
{ id: 3, title: 'Why Vue is so fun' }
]
}
})
My question is how can get the title of a specefic element based on the id in my HTML? For now I can only render through the items and get them all, but I want to be able to specify which title I want to display based on the Id. Here is my HTML which gives me all the data:
<div id="blog-post-demo">
<blog-post
v-for="post in posts"
v-bind:key="post.id"
v-bind:title="post.title"
></blog-post>
</div>
You can achieve with COMPUTED property, like that
<template>
<div id="blog-post-demo">
<p v-for="post in thisOne" :key="post.id" >
{{post.title}}
</p>
</div>
</template>
<script>
export default {
el: '#blog-post-demo',
data() {
return {
posts: [
{ id: 1, title: 'My journey with Vue' },
{ id: 2, title: 'Blogging with Vue' },
{ id: 3, title: 'Why Vue is so fun' }
]
}
},computed: {
thisOne(){
return this.posts.filter(x => x.id === 3); /*choose your id*/
}
}
};
</script>
Or you can use event too to select the id of the posts to display (more dynamically)
Tip: If you start with VueJS, learn about the properties of VueJs (DATA, COMPUTED, CREATED, METHOD) and look at the uses and strengths of each one. For my part, the VueJS site is very very well done for beginners: https://v2.vuejs.org/v2/guide/
I'm not sure if I understand correctly what you want to do. But if you want to go through all posts and display title of particular post then you can try this way:
<blog-post
v-for="post in posts"
:key="post.id"
:title="setTitle(post)"
/>
(: instead of :v-bind it's a short form, also if you don't pass slots in your component you can go with self-closing tag)
Then in your methods section you can create a method:
setTitle(post) {
if(post.id === 2) return post.title
}

How to use function at Vue local component

at my example js code is:
new Vue({
el: '#app',
data: {
list: [1,2,3]
},
methods: {
change: function(number) {
return number === 2 ? 'a' : 'b';
}
},
components: {
'html-list': {
props: ['item'],
template: '<li :title='change(item)'> text </li>'
}
}
});
at my example html code is:
<ul id='app'>
<html-list
v-for:'num in list'
:item='num'
></html-list>
</ul>
I want the following result:
<ul id='app'>
<li title='b'> text </li>
<li title='a'> text </li>
<li title='b'> text </li>
</ul>
But it's not working.
I tried several different ways but couldn't find a way to dynamically bind data by calling a function on the component.
I don't want to use global components because there are so many components to create.
What am I doing wrong?
You trying to use function change, which is not declared in component.
There are several solutions for this:
declare function in component
components: {
'my-component': {
props: [...],
template: '...',
methods: {
change: function() {...}
}
}
}
if you want use function from parent component template: '<li :title='$parent.change(item)'> text </li>'
Use Vuex

Data attribute bound to external variable not reactive to variable changes

I'm beginning to incorporate Vue.js (v2.5.17) for a few small things on an ecommerce website, but I'm new to Vue and having some troubles. Due to some Laravel blade structure that I don't have the option of refactoring at this time, I have to split the various parts of this functionality into distinct components, and I'm having trouble with the data in one Vue instance reacting to events on the other.
Here's a CodePen with a stripped down version of the issue: https://codepen.io/jgabrielsen/pen/bxRroM/
For easy reference, the JS:
var cartStore = {
state: {
products: [PRODUCT ARRAY],
active: false
},
}
var cartHeader = new Vue({
el: '#cartHeader',
data: {
products: cartStore.state.products,
},
methods: {
setActiveTrue: function() {
cartStore.state.active = true;
console.log('Show Cart');
},
setActiveFalse:function() {
cartStore.state.active = false;
console.log('Hide Cart');
},
},
})
var cartPreview = new Vue({
el: '#cartPreview',
data: {
products: cartStore.state.products,
active: cartStore.state.active,
},
methods: {
total: function() {
var sum = 0;
for (var i = 0; i < this.products.length; i++) {
sum += this.products[i][1]
}
return sum
},
},
})
And the HTML
<div class="header">
<a id="cartHeader" #mouseover="setActiveTrue" #mouseout="setActiveFalse">CART({{ this.products.length }})</a>
</div>
<div id="cartPreview" v-show="active">
<ul>
<li v-for="product in products">
<div class="col-80">
{{ product[0] }}
</div>
<div class="col-20" style="text-align:right;">
${{ product[1] }}
</div>
</li>
</ul>
<div class="row">
<div class="col-100" style="text-align:right;">
Total Products: {{ this.products.length }}<br>
Total: ${{ this.total() }}
</div>
</div>
</div>
Long story short, I need the cartPreview instance to show up when cartHeader instance is hovered over (a basic popover effect).
Here's how I'm currently trying to do this:
I have a var cartStore that contains an array of all the cart preview data, as well as an active state.
cartPreview has a data attribute active bound to cartStore.state.active (which is false by default) and v-show="active", so it's hidden until something sets it's data attribute active to true.
cartHeader has #mouseover="setActiveTrue" and #mouseout="setActiveFalse", to toggle cartPreview's active attribute by way of the bound state in cartStore.
I can tell that the mouseover and mouseout events are firing because cartStore.state.active does change to true and false correctly and the console logs fire, but the corresponding data attribute in the cartPreview is not reactive to these changes.
I feel like I must be overlooking something super simple and/or making some major noob mistakes, but after poring over my code dozens of times and searching high and low, I'm stumped as to why it's not reactive.
I finally figured out a solution.
cartStore.state.products was reactive between cartHeader and cartPreview for adding and removing the products (methods I removed from my example because I didn't think they were relevant), but cartStore.state.active wasn't. The only difference I could see was that the cart data in products was stored in an array and active wasn't, so I made cartStore.state.active an array:
var cartStore = {
state: {
products: [PRODUCT ARRAY],
active: [ false ]
},
}
...updated it with splice:
methods: {
setActiveTrue: function() {
this.active.splice(0,1,true);
},
setActiveFalse:function() {
this.active.splice(0,1,false);
},
},
...and added v-show="active[0]" to the component, and lo and behold, it works.

Marking a Vue Todo List Item as done

This may have been answered before, but I have been unable to find an answer that works in this specific situation.
I'm new to Vue and trying to build a Todo list in which I can click on a list item when it is complete, changing or adding a class that will change the style of the item.
I guess I don't fully understand how the scopes work together between the main Vue and a component. The code I have right now does absolutely nothing. I have tried moving methods between the main and component, but it always gives me some error.
I guess I'm just looking for some guidance as to how this should be done.
Vue.component('todo-item', {
props: ['todo'],
template: '<li>{{ todo.id + 1 }}. {{ todo.text }}</li>'
})
var app = new Vue({
el: '#app',
data: {
isDone: false,
todos: [
{ id: 0, text: 'This is an item to do today' },
{ id: 1, text: 'This is an item to do today' },
{ id: 2, text: 'This is an item to do today' },
{ id: 3, text: 'This is an item to do today' },
{ id: 4, text: 'This is an item to do today' },
{ id: 5, text: 'This is an item to do today' }
]
},
methods: {
markDone: function(todo) {
console.log(todo)
this.isDone = true
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<div class="content">
<ul class="flex">
<todo-item
v-for="todo in todos"
:todo="todo"
:key="todo.id"
#click="markDone"
:class="{'done': isDone}"
></todo-item>
</ul>
</div>
</div>
Thanks for the help, guys.
You were getting so close! You simply had your :class="{'done': isDone}" #click="markDone" in the wrong place!
The important thing to remember with components is that each one has to have their own data. In your case, you were binding all todo's to your root Vue instance's done variable. You want to instead bind each one to their own done variable in their own data.
The way you do this is by creating a function version of data that returns individual data for each component. It would look like this:
data () {
return {
isDone: false,
}
},
And then you move the :class="{'done': isDone} from the todo to the li internal to it:
<li :class="{'done': isDone}">{{ todo.id + 1 }}. {{ todo.text }}</li>
Now we have the 'done' class depending on an individual data piece for each individual todo element. All we need to do now is be able to mark it as complete. So we also want each todo component to have it's own method to do so. Add a methods: object to your todo component and move your markDone method there:
methods: {
markDone() {
this.isDone = true;
},
}
Now move the #click="markDone" to the li as well:
<li :class="{'done': isDone}" #click="markDone">{{ todo.id + 1 }}. {{ todo.text }}</li>
And there you go! Now you should be able to create as many todo's as you want, and mark them all complete!
Bonus:
Consider changing your function to toggleDone() { this.isDone = !this.isDone; }, that way you can toggle them back and forth between done and not done!
Full code below :)
Vue.component('todo-item', {
props: ['todo'],
template: `<li :class="{'done': isDone}" #click="toggleDone">{{ todo.id + 1 }}. {{ todo.text }}</li>`,
data () {
return {
isDone: false,
}
},
methods: {
toggleDone() {
this.isDone = !this.isDone;
},
}
})
var app = new Vue({
el: '#app',
data: {
isDone: false,
todos: [
{ id: 0, text: 'This is an item to do today' },
{ id: 1, text: 'This is an item to do today' },
{ id: 2, text: 'This is an item to do today' },
{ id: 3, text: 'This is an item to do today' },
{ id: 4, text: 'This is an item to do today' },
{ id: 5, text: 'This is an item to do today' }
]
},
methods: {
markDone: function(todo) {
console.log(todo)
this.isDone = true
}
}
})
.done {
text-decoration: line-through;
}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<div class="content">
<ul class="flex">
<todo-item
v-for="todo in todos"
:todo="todo"
:key="todo.id"
></todo-item>
</ul>
</div>
</div>
First of all, your current implementation will affect all items in the list when you mark one item as done because you are associating a single isDone property to all the items and when that property becomes true, it will be applied to all the items in your list.
So to fix that, you need to find a way to associate done to each item. And because your item is an object, you just assign a new property done dynamically and set the value to true which means it is marked as done. It will be very confusing to just explain it, so I included a full example using your existing code.
See this JS Fiddle: https://jsfiddle.net/eywraw8t/205021/
In your code, which are handling with the click event is the <li> element, but your are trying to handle it in the root of your component, there're a few ways to solve this
Use native modifier
<todo-item
v-for="todo in todos"
:todo="todo"
:key="todo.id"
#click.native="markDone"
:class="{'done': isDone}"
>
</todo-item>
You can find more info here
https://v2.vuejs.org/v2/guide/migration.html#Listening-for-Native-Events-on-Components-with-v-on-changed
Emit from component the click event
Vue.component('todo-item', {
props: ['todo'],
template: '<li #click="click()">{{ todo.id + 1 }}. {{ todo.text }}</li>',
methods: {
click () {
this.$emit('click')
}
}
})
By the way, in your current code once you click in one todo all the todos will be "marked as done" as you are using just one variable for all of them.

Vue slot-scope with v-for, data changed but not re-rendered

I have a question while I'm studying vuejs2
I made an example with slot-scope && v-for, but it has an error which I can't understand.
Here is example code
https://jsfiddle.net/eywraw8t/6839/
app.vue
<template id="list-template">
<div>
<ul>
<slot name="row" v-for="item in list" v-bind=item />
</ul>
</div>
</template>
<div id="app">
<list-component :list="list">
<li slot="row" slot-scope="item">
{{item.name}}
</li>
</list-component>
</div>
Vue.component('list-component', {
template: '#list-template',
props: {
list: {
type: Array
}
}
});
new Vue({
el: '#app',
data () {
return {
list: [
{id: 1, name: 'Apple'},
{id: 2, name: 'Banana'},
{id: 3, name: 'Cherry'},
{id: 4, name: 'Durian'},
{id: 5, name: 'Eggplant'}
]
}
},
methods: {
h (item) {
item.name = item.name.toUpperCase()
console.log('Changed!')
console.log(item)
}
}
});
Strange thing is, the method 'h' is triggered and then, the console said 'Changed!' and data also changed but, the view is not re-rendered.
What am I missing? I think slot-scoped object data is not referencing the original object data.
What should I do to modify the original data?
Thanks for reading this question.
You are directly trying to modify the value of an item in an array. Due to some limitations vue cannot detect such array modifications.
So update your code to use vm.$set() to make chamges to the array item.
methods: {
h (item) {
let i = this.items.findIndex((it) => it.id === item.id);
this.$set(this.list, i, {...item, name: item.name.toUpperCase()});
console.log(item.id)
}
Here is the updated fiddle