I am using a v-for inside a template to show a list from a computed property. But even though it works when i refresh, i cannot see the listed items when i firstly get on the page. If i v-for my contacts, i cannot filter.
So my data and computed methods look like this:
export default {
data() {
return {
contacts: this.$store.getters.loadedContacts,
search: ""
};
},
computed: {
filterContacts(contacts) {
return this.contacts.filter(contact => {
return contact.name.toLowerCase().match(this.search.toLowerCase());
});
}
};
And i call it in my HTML like this (filterContacts):
<v-list two-line v-if="contacts">
<template v-for="(contact, index) in filterContacts">
<v-list-tile-content>
<v-list-tile-title>{{ contact.name }}</v-list-tile-title>
<v-list-tile-action-text>{{ contact.position }}</v-list-tile-action-text>
</v-list-tile-content>
</template>
</v-list>
So the actual question is this: Why do need to refresh my page to see the results from the for loop ?
If i don't call the filterContacs, i cannot use my filter.
Any suggestions how to solve both filtering and v-for loop?
Thanks and sorry if this is a novice one!
Any help is much appreciated
Data of the component is set upon creation. The getter in the store probably doesn't return any data yet.
You can safely replace this.contacts in your computed with this.$store.getters.loadedContacts.
Other thing you can choose for, perhaps more elegant, is to use vuex's mapGetter helper. It reactively maps a vuex getter to your component's computed property (read more here: https://vuex.vuejs.org/guide/getters.html#the-mapgetters-helper).
With mapGetters you would:
...mapGetters({
contacts: 'loadedContacts'
})
And then just remove contacts from your data declaration.
Thanks! I actually observed at what you said. Indeed by replacing this.contacts with this.$store.getters.loadedContacts did fixed my problem. I removed also "contacts" from data().
Thanks!
Related
So I'm trying to change a list based on a whether the elements are considered active or not. I do this through a computed data array. Basically a Search Function. However my template does not rerender and update automatically, even though I try to force it with this.forceUpdate().
This is my v-for in template:
<ion-list>
<div v-for="project in activeProjects" :key="project">
<ion-item v-if="checkemail!=project.creator">
<ion-button #click="openProjectPage(project.id)">{{ project.name }}</ion-button>
</ion-item>
</div>
</ion-list>
This is my computed array. The Log returns the correct things.
computed: {
activeProjects: function() {
return this.myprojects.filter(function(u){
console.log(u);
return u.active
})
}
}
And this is where I update the activity. The Log also returns the correct things.
search: function(){
for(var i=0; i<this.myprojects.length; i++){
if(this.myprojects[i].name.includes(this.searchinput)){
this.myprojects[i].active=true;
console.log(this.myprojects[i])
}
}
this.$forceUpdate();
}
Grateful for any help
I understand what you're attempting with the $forceUpdate, but I'm not certain that's the intended behavior here. In particular, by directly modifying the property of an Object in an Array, I believe Vue is missing the changes completely, so it doesn't know what to forceUpdate.
(See these links to read more on when Vue does / doesn't recognize mutations to Objects and Arrays)
TBH I've never attempted to use forceUpdate in this way, but I have done some Array mutation in a spreadsheet-like scenario before and it was a pain... I would avoid it if at all possible.
Rather than modifying a property in the array, I'd compute the filter on-the-fly using a method. You should get the reactivity you want because you're calculating, not mutating, the properties of the list of projects.
<script>
export default {
props: ['myprojects'],
data() {
return {
searchinput: ''
}
},
computed: {
activeProjects() {
return this.myprojects.filter(this.isInSearch)
}
},
methods: {
isInSearch(project) {
return project.name.includes(this.searchinput)
}
}
}
</script>
Vue caches nodes based on :key value. You're passing the entire object, you should be using a unique property on your project.
Try yo use name or an unique id if you have one.
<ion-list>
<div v-for="project in activeProjects" :key="project.name">
<ion-item v-if="checkemail!=project.creator">
<ion-button #click="openProjectPage(project.id)">{{ project.name }}</ion-button>
</ion-item>
</div>
</ion-list>```
Is there a way to use Vue.Draggable lists with computed variables in a Vuex Store? I'm fairly new to VueJS and this is my current component setup:
// Template
<draggable class="list-group" :list="list1" group="people">
<div
class="list-group-item"
v-for="(element, index) in list1"
:key="element.id"
>{{ element.name }} ({{ element.list }}) ({{ index }})</div>
</draggable>
// Script
computed: {
list1: {
return this.$store.getters.listItems.filter(item => item.list === 1)
}
}
// Store
const getters = {
listItems: (state) => {
return state.items
}
}
Without computed variables, everything works fine. When using computed variables, I can drag list items between lists, however the UI won't update because it's computed from the store.
Basically, what I need is to display multiple lists based on an input file (json). These lists have list items that can be dragged between different lists (with Vue.Draggable). Also, the user can create new list items / delete existing list items.
What's the best way to achieve this?
Thank you in advance.
In a complex draggable use-case, also using vuex, I ended up creating a variable in state called 'changes', and every time something changes, it gets incremented (through a vuex mutation). The data that's linked to dragging is in data, not in the store. I manually sync the two.
I use this - through a watcher - to trigger a rerender of my base component with completely fresh data.
This solution is ugly, and likely won't be necessary in vue3.
You probable need to do the following:
Have your draggable div into a new component
On this new component, have this #change.self="handleDragAndDrop($event)"
Handle the D&D
handleDragAndDrop: function (event){
const eventType = Object.keys(event)[0];
const chosenFrame = event[eventType].element;
store.dispatch(
"updateFramesOrder",
{
event: event,
listItemId: this.$props.itemId,
}
);
}
Finally in the store create the updateFramesOrder action that changes the list accordingly.
Hope it helps!
I am in a process to support multiple data source to show data in a component via props & store.
The idea is to build a list component, which will load the data from the store if no props present. Otherwise, show the data from props. Thus, I am ensuring reusability in the context of search functionality and normal listing view.
Here is the code looks like,
<template>
<div>
<li v-for="user in this.dataSource" :key="user.age">
{{ user.name }}
</li>
</div>
</template>
<script>
export default {
props: {
userData: {
type: Array,
default: null,
}
},
created() {
// dispatch action to get user data
this.$store.dispatch("GET_USER_DATA");
},
data() {
return {
dataSource: this.userData !== null ? this.userData: this.$store.state.users
};
}
};
As of now, the store holds just static data. But in the context of REST it will be async in nature. So sometimes, I see no data.
So my question is that is this logic of dataSource can be improved further?
Thanks
Robin.
You should change dataSource to computed property. It will automatically re-computed when this.userData is changed or this.$store.state.users is changed
computed: {
dataSource() {
return this.userData !== null ? this.userData: this.$store.state.users
}
}
For more information about computed in Vuejs, please check the document
This is a bad approach anyways.
Store should be used to contain the data and components should just "show" it and manipulate it through actions and mutations.
So in this case, I'd remove the props, created and data part and add a getter to the store which should be implemented in a computed in the component. Nothing fancy here.
You shouldn't need to manually load the data with a proper store setup. Always make a getter and "load" it into the component with a computed.
Is it possible to pass props as funcitions on VueJs?
<template>
<line-chart
class="card-content"
:chartData="lineData2('Temp')"
:options="options"
:width="800">
</line-chart>
</template>
The chartData prop can be used as a method? Is there any way to pass a method on a prop?
Cheers,
It depends on what you are trying to do. If you want to pass an actual function and not the result of a function, you can do:
<line-chart class="card-content" :chartData="() => lineData2('Temp')" :options="options" :width="800"></line-chart>
Which will pass the lineData2 function itself, however, it will still be executed in the parent scope and not in the component scope, so it won't have any access to the components this context.
Here's a JSFiddle: https://jsfiddle.net/rz8c1v4L/
If you just want to pass the result of the function then what you are doing is fine.
Yes you can. As long as your function returns a value. Everything should work.
Yes you can! You can achieve that by mentioning the type of your prop, as it is clearly written in Vue's documentation. For e.g.
Vue.component('example', {
props: {
propA: {
type: function
}
}
})
Also checkout this link
Still a little bit young in VueJS but I'm loving every bit of it. But now, fixated somewhere.
I want to initialize some values in data() using values passed via props. This is so that I can be able to mutate them later on, since it is not recommended to mutate props inside a component. In fact the official docs recommend this property initialization using prop values as shown below:
{
props: ['initialCounter'],
data: function () {
return { counter: this.initialCounter }
}
I have something like the one below:
<template>
<div class="well">
<!-- Use Prop value directly on the template: works (but of no help in initializing data) -->
Department: {{department.name}}
<!-- Use prop value but gotten via computed property: Works inside the template but not in the initialization -->
Department: {{fetchDepartment.name}}
<!-- Use the array I initialized with the prop value: Does not work -->
Department: {{this_department.name}}
</div>
</template>
<script>
export default {
name: 'test',
props: ['department'],
data() {
return {
this_department: this.department
// below does not work either
//this_department: this.fetchDepartment
}
},
created() {
// shows empty array
console.log(this.department)
},
mounted() {
// shows empty array
console.log(this.department)
},
computed: {
fetchDepartment() {
return this.department
}
}
}
</script>
As seen in the commented sections above, the initialization is not successful. Neither does the value of this.department appear either from the created() or the mounted() hooks. And note, I can see it is defined using the Chrome Vue Devtools. So my question is, how exactly should I initialize data() attributes using props values, or which is the best way of going around this issue?
I know my answer comes in late but it helps me and hopefully someone else coming here. When props' data are async:
// in the parent component
<template>
<child :foo="bar" v-if="bar" />
</template>
That way, you render the component when props are already available making it safer to follow the guide's recommended ways to initialize data value with props as so:
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}
Happy coding!
You CAN modify a prop. Use the '.sync' modifier. I use it frequently because it is convenient and intuitive. This requires emitting an event to update the value on the parent. I am not really sure the warning of how it results in maintenance issues.
Another method I use if I want to modify a value and not update the parent is using Lodash clone. For example (assuming its available on mounted)
mounted(){
this_department = _.clone(this.department)
}
If you consistently want to mutate the prop and have it change with the parent, then use a computed property. However, in most cases you will want to depend on the state of that data within the component and change it using other functions and thus a computed property will not be what you need.
A computed property is the simplest way to provide a mutable version of a prop, but you might not want to lose data when the prop is updated. You could use an explicit watch.
Watchers
While computed properties are more appropriate in most cases, there
are times when a custom watcher is necessary. That’s why Vue provides
a more generic way to react to data changes through the watch option.
This is most useful when you want to perform asynchronous or expensive
operations in response to changing data.
This is most useful when you want to perform asynchronous or expensive
operations in response to changing data.