reload vue component from outside - javascript

I have a laravel vue project that displays a list of items in a component. The component when mounted makes an ajax call to populate the data element. However, there's other items on the page (not in vue) that can add elements to the database, and I'd like to make sure the list is reactive in the component.
mounted() {
this.getTasks();
},
methods: {
getTasks() {
let self = this;
axios.get('/tasks').then(response => {
self.tasks = response.data;
})
.catch(function (error) {
console.log(error);
});
},
}
When the user does an action that would add a task to the list, is there a way to fire the getTasks method on the component from outside the component?

You can declare a global function (binding context to Vue component) when component is mounted:
mounted() {
windows.getTasks = this.getTasks.bind(this);
this.getTasks();
},
Then you can use it outside calling getTasks() or windows.getTasks()

You should use vuex actions.
Here's an example:
Vue.component('ChildB',{
template:`
<div class="child childB">
<h1> Score: {{ score }} </h1>
<button #click="changeScore">Change Score</button>
</div>`,
computed: {
score () {
return this.$store.getters.score
}
},
methods: {
changeScore () {
this.$store.dispatch('incrementScore', 3000)
}
}
})
full source:
https://codepen.io/tutsplus/pen/MXpPLz

Related

Data not refreshing from child component to parent

I have a child component that I add in links. Once completed I call the parent component to fetch the new links.
My links in the v-for aren't updating. They are only updating when I reload the page with the new entry. Upon submit, I want my child component to notify/call the parents fetchLinks function to update all the links on the screen (Which its not updating, unless I refresh page)
This is on the form success function from Child AddLinksComponent
// Get the new updated subscriptions
this.$parent.fetchLinks()
Parent Component
<div class="card card-body" v-for="link in links" v-bind:key="link.id">
<h2>{{ link.name }}</h2>
</div>
<add-links></add-links>
export default {
data() {
return {
links: [],
}
},
components: {
'add-links': AddLinksComponent,
},
methods: {
fetchLinks() {
fetch('/links/')
.then(res => res.json())
.then(res => {
this.links = res.data
}).catch(err => {
console.log('Error', err)
});
}
},
created() {
this.fetchLinks();
},
mounted() {
}
}
In the child component try to emit an event to the parent one when the task is done like :
this.$emit('fetch');
in the parent component :
<add-links #fetch="fetchLinks"></add-links>

Vue component doesn't update when state in store is updated

I'm trying to do a simple todo list in Vue but I want to abstract everything out and use a dummy REST API so I can start to get used to production-level projects in Vue and it's all making my head spin. GET, PUT, and POST requests seem to be working, but I can't figure out why the list of todos doesn't update automatically when I do a successful POST request to the back end.
I've got a TodoList component that loops through a todosFiltered() computed property to show the todos. The computed property refers back to the getter todosFiltered in the Vuex store. I also use the created() lifecycle hook here to dispatch an action in the store that makes the initial GET request and then populates an array called todos in the store when the page is first loaded. The getter todosFiltered in the store returns state.todos, so I assumed that when my component re-renders, it would have the new todos array from the state grabbed from todosFiltered, only that's not happening. What am I missing here? Any advice would be greatly appreciated.
TodoList.vue
(I know I'll have to work out a solution for the ids, it's on my list :p)
<template>
<div class="container">
<input v-model="newTodo" type="text" placeholder="What must be done?" class="todo-input" #keyup.enter="addTodo">
<transition-group name="fade" enter-active-class="animated zoomIn" leave-active-class="animated zoomOut">
<todo-item v-for="todo in todosFiltered" :key="todo.id" :checkAll="!anyRemaining" :todo="todo"></todo-item>
</transition-group>
<div class="extra-container">
<todos-filtered></todos-filtered>
</div>
</div>
</template>
<script>
import TodosFiltered from './TodosFiltered'
import TodoItem from './TodoItem'
export default {
name: 'todolist',
components: {
TodosFiltered,
TodoItem
},
data() {
return {
beforeEditCache: '',
newTodo: '',
idForTodo: 10,
}
},
// Methods
methods: {
addTodo() {
if (this.newTodo.trim().length == 0) {
return
}
this.$store.dispatch('addTodo', {
id: this.idForTodo,
title: this.newTodo,
completed: false
})
this.newTodo = ''
this.idForTodo++
}
},
computed: {
todosFiltered() {
return this.$store.getters.todosFiltered
},
},
created() {
this.$store.dispatch('loadTodos')
},
}
</script>
store.js
export const store = new Vuex.Store({
state: {
filter: 'all',
todos: []
},
getters: {
todosFiltered(state) {
if (state.filter == 'all') {
return state.todos
} else if (state.filter == 'active') {
return state.todos.filter(todo => !todo.completed)
} else if (state.filter == 'completed') {
return state.todos.filter(todo => todo.completed)
}
return state.todos
},
showClearCompleted(state) {
return state.todos.filter(todo => todo.completed).length > 0
}
},
mutations: {
addTodo(state, todo) {
state.todos.push(todo)
},
setTodos(state, todos) {
state.todos = todos
},
},
actions: {
loadTodos(context) {
axios.get('http://localhost:3000/todos')
.then(r => r.data)
.then(todos => {
context.commit('setTodos', todos)
})
},
updateTodo(context, todo) {
axios.put('http://localhost:3000/todos/' + todo.id, {
"id": todo.id,
"title": todo.title,
"completed": todo.completed
})
},
addTodo(context, todo) {
axios.post('http://localhost:3000/todos', {
"id": todo.id,
"title": todo.title,
"completed": todo.completed
})
.then(todo => {
context.commit('addTodo', todo)
})
},
}
})
EDIT: Here's what's going on in Vue Dev Tools when I add a todo -- todos in the store's state gets updated immediately, and the todosFiltered computed property in the TodoList component ALSO reflects that -- but the new todo doesn't appear in the list! Strange.
A way to solve this can be to create what I like to call a refresh() method.
Basically, you will have a local list of todos in your data() method, the refresh() method will load all the todos from the store into the local todos list, every time you do an action, such as creating, deleting, or updating, you would call the refresh method to re-load the list for you.
So, in your TodoList.vue:
<template>
<todo-item v-for="todo in todosFiltered" :key="todo.id"></todo-item>
</template>
<script>
export default {
data() {
return {
// Where we store the local list of Todos
// so the component will react when we do something to it
todosFiltered: []
}
},
methods {
refresh() {
// Get the todo list from the store.
// which in turn will trigger a change event so the component
// react to what we did.
this.todosFiltered = this.$store.getters.todosFiltered;
},
addTodo() {
this.$store.dispatch('addTodo').then(() => {
// Refresh the list after adding a new Todo
this.refresh();
})
},
updateTodo() {
this.$store.dispatch('updateTodo').then(() => {
// Refresh the list after updating a Todo
this.refresh();
})
},
deleteTodo() {
this.$store.dispatch('deleteTodo').then(() => {
// Refresh the list after deleting a Todo
this.refresh();
})
}
},
created() {
this.$store.dispatch('loadTodos').then( () => {
// Refresh the list when first loaded after the Todos been saved in Vuex
this.refresh();
})
}
}
</script>
Don't actually delete what you already have and replace it with
this, just apply what's here to your code.
The problem is using a $store.getter on the v-for loop.
Try the following:
Set your computed to
todos() {
return this.$store.todos;
}
Change your v-for loop to use todo in todos
Add a v-if condition to the loop like v-if="filtered(todo)"
Create a new method called filtered (or whatever you prefer), and add your "filteredTodos" logic there, returning true/false as needed
If you need to share this code, you can always use a mixin and share it between your components
Hope this works for you

Vuejs single file component (.vue) model update inside the <script> tag

I'm new to vuejs and I'm trying to build a simple single file component for testing purpose.
This component simply displays a bool and a button that change the bool value.
It also listen for a "customEvent" that also changes the bool value
<template>
{{ mybool }}
<button v-on:click="test">test</button>
</template>
<script>
ipcRenderer.on('customEvent', () => {
console.log('event received');
this.mybool = !this.mybool;
});
export default {
data() {
return {
mybool: true,
};
},
methods: {
test: () => {
console.log(mybool);
mybool = !mybool;
},
},
};
</script>
The button works fine. when I click on it the value changes.
but when I receive my event, the 'event received' is displayed in the console but my bool doesn't change.
Is there a way to access the components data from my code?
Thanks and regards,
Eric
You can move ipcRenderer.on(...) into vuejs's lifecycle hooks like created.
See: https://v2.vuejs.org/v2/guide/instance.html#Instance-Lifecycle-Hooks
You are setting up the event listener outside of the component's options which you export by using
export default{ //... options }
Set up the event listener inside the vue options so the vue instance has control over it, in your case modifying dara property
As choasia suggested move the event listener to `created() life cycle hook:
<template>
{{ mybool }}
<button v-on:click="test">test</button>
</template>
<script>
export default {
data() {
return {
mybool: true,
};
},
methods: {
test: () => {
console.log(mybool);
mybool = !mybool;
},
},
created(){
ipcRenderer.on('customEvent', () => {
console.log('event received');
this.mybool = !this.mybool;
});
}
};
</script>
Now you component will starting listening for that particular even when the component is created

Setting props for components using v-for in Vue JS

I have a PhoneCard.vue component that I'm trying to pass props to.
<template>
<div class='phone-number-card'>
<div class='number-card-header'>
<h4 class="number-card-header-text">{{ cardData.phone_number }}</h4>
<span class="number-card-subheader">
{{ cardData.username }}
</span>
</div>
</div>
</template>
<script>
export default {
props: ['userData'],
components: {
},
data() {
return {
cardData: {}
}
},
methods: {
setCardData() {
this.cardData = this.userData;
console.log(this.cardData);
}
},
watch: {
userData() {
this.setCardData();
}
}
}
The component receives a property of userData, which is then being set to the cardData property of the component.
I have another Vue.js component that I'm using as a page. On this page I'm making an AJAX call to an api to get a list of numbers and users.
import PhoneCard from './../../global/PhoneCard.vue';
export default {
components: {
'phone-card': PhoneCard
},
data() {
return {
phoneNumbers: [],
}
},
methods: {
fetchActiveNumbers() {
console.log('fetch active num');
axios.get('/api').then(res => {
this.phoneNumbers = res.data;
}).catch(err => {
console.log(err.response.data);
})
}
},
mounted() {
this.fetchActiveNumbers();
}
}
Then once I've set the response data from the ajax call equal to the phoneNumbers property.
After this comes the issue, I try to iterate through each number in the phoneNumber array and bind the value for the current number being iterated through to the Card's component, like so:
<phone-card v-for="number in phoneNumbers" :user-data="number"></phone-card>
However this leads to errors in dev tools such as property username is undefined, error rendering component, cannot read property split of undefined.
I've tried other ways to do this but they all seem to cause the same error. any ideas on how to properly bind props of a component to the current iteration object of a vue-for loop?
Try
export default {
props: ['userData'],
data() {
return {
cardData: this.userData
}
}
}
Answered my own question, after some tinkering.
instead of calling a function to set the data in the watch function, all I had to do was this to get it working.
mounted() {
this.cardData = this.userData;
}
weird, I've used the watch method to listen for changes to the props of components before and it's worked flawlessly but I guess there's something different going on here. Any insight on what's different or why it works like this would be cool!

What is a satisfiable approach to let a computed value to wait for asynchronous data in Vue.js?

Let's say I get a products list from my state manager inside my Vue.js component and a computed property needs that list to process it. Finally, my DOM depends on the proceeded list.
<template>
<span>{{ computedproducts }}</span>
</template>
<script>
export default {
created () {
this.$store.dispatch('getProducts')
},
computed: {
products () {
// Following is asynchronous
return this.$store.state.products
},
computedProducts () {
// Processing products in some way
return this.products
}
}
}
</script>
With this I get an error because the computedProducts doesn't exist at start.
A possible solution would be to set:
<span v-if="products.length > 0">{{ computedproducts }}</span>
And also return a Promise from the products state and process the products at the created hook. But it feels hacked. Is there a better approach?
I would do something like this:
export default {
data() {
return { products: [] }
},
created () {
this.$store.dispatch('getProducts').then(res => { this.products = res })
},
computed: {
computedProducts () {
// Processing products in some way
return this.products
}
}
}
Live Demo.

Categories

Resources