how to pass data between different template - vue.js

I'm trying to make a chat and I do not know how to click an item in one template
get the data and transfer it to another.
from #template1 to #template2
const app = new Vue({
el: '#content',
data:{
users: [],
messages: []
}
});
template1
<template lang="html">
<li v-on:click="getMessages()">
<div class="content-container">
<span class="name">{{ user.first_name}} {{user.last_name}}</span>
<span class="txt"></span>
</div>
</li>
</template>
<script>
export default {
props: ['user'],
data: function() {
return {
messages: []
};
},
methods: {
getMessages(id) {
this.$http.get('/admin/chat/messages').then(function(res){
this.messages = res.data;
}
},
}
}
</script>
template2
<template lang="html">
<ul class="chat">
<chat-message v-for="message in messages" :key="+message" :message="message"></chat-message>
</ul>
</template>
<script>
export default {
props: ['messages']
}
</script>
how to pass data between this template
The structure is:
el: #content
<div id="content">
<div class="list-text" id="chatbox">
<user-list :users="users">#template1</user-list>
</div>
<div class="list-chat">
<chat-log :messages="messages">#template2</chat-log>
<chat-composer v-on:messagesent="addMessage"></chat-composer>
</div>
</div>
When I click on the user in the user-list
i'm trying to load messages from this user
and I do not know how to pass them in chat-log

There are a few ways to achieve the communication between the components
Emit and listen to event (if the component are siblings, you will have to go through the parent) - this solution is fine if you have very little components that need to communicate with each other
Use event bus -> every event will go through this bus (good tutorial on how to create an event bus)
Use vuex - state management (just like event bus, but better, if you have a lot of component communication)

Related

VUE3 use different v-for for the same component

I have a JobComponent.vue component where I fetch data from a VUEX Store. This component is used on two separate pages, first page Home.vue and second page AllJobs.vue.
In AllJobs.vue I used JobComponent.vue and everything is works fine, it's rendering all the jobs, but, here comes the problem...
In Home.vue I want to render only the last 5 jobs, so in store I make a getter that slice me only the latest 5 jobs.
How can I use this latestJobs from getters on the same component?
When I import the component in Home.vue page I can't use another v-for direct on the component...
here you can see my project structure and files
Home.vue
<template>
<div class="cards-container">
<JobComponent />
</div>
</template>
JobComponent.vue
<template>
<div v-for="job in allJobs" :key="job.id" class="card">
<div class="position">{{ job.position }}</div>
<div class="department">{{ job.department }}</div>
<div class="location">
<span class="material-symbols-outlined">location_on</span>
{{ job.location }}
</div>
<span class="material-symbols-outlined right-arrow">arrow_right_alt</span>
<span #click="deleteJob(job.id)" class="material-symbols-outlined right-arrow">delete</span>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
export default {
methods: {
...mapActions(['fetchJobs', 'deleteJob']),
},
computed: mapGetters(['allJobs']),
created() {
this.fetchJobs();
}
}
</script>
store.js (vuex)
const getters = {
allJobs: (state) => state.jobs,
latestJobs: (state) => {
const response = state.jobs.slice(0, 5);
return response;
}
};
Your component should be as independent as possible from the store. It's role is to display what ever is provided so it could be reused as you want, using props :
JobComponent.vue
<template>
<div class="card">
<div class="position">{{ position }}</div>
<div class="department">{{ department }}</div>
<div class="location">
<span class="material-symbols-outlined">location_on</span>
{{ location }}
</div>
<span class="material-symbols-outlined right-arrow">arrow_right_alt</span>
<span #click="$emit('deleteJob', id)" class="material-symbols-outlined right-arrow">delete</span>
</div>
</template>
<script>
export default {
props: {
id: string,
position: string,
department: string,
location: string
}
}
</script>
In this component you only display the provided data, and leave the responsibility of the parent component to choose how many components to display.
Home.vue
<template>
<div class="cards-container">
<JobComponent v-for="job in jobs" :key="job.id" :id="job.id" :position="job.position" :department="job.department" :location="job.location" #delete-job="deleteJob" />
</div>
</template>
<script>
export default {
created() {
this.$store.dispatch('fetchJobs')
},
computed: {
jobs() {
return this.$store.getters['latestJobs'] // Or allJobs, just make sure your getter returns an array even if no jobs are loaded yet.
}
},
methods: {
deleteJob() {
// Your logic for job delete
}
}
}
</script>

VueJS - template variables not reactive with data variable

I'm making a chat system with socket.io and VueJS, so customers can talk to an admin. But when a client connects to the server, the variable in the data() updates. But the template is not updating.
Here is my code:
<template>
<div>
<div class="chats" id="chat">
<div class="chat" v-for="chat in chats">
<b>{{ chat.clientName }}</b>
<p>ID: {{ chat.clientID }}</p>
<div class="jens-button">
<img src="/icons/chat-bubble.svg">
</div>
</div>
</div>
</div>
</template>
<script>
let io = require('socket.io-client/dist/socket.io.js');
let socket = io('http://127.0.0.1:3000');
export default {
name: 'Chats',
data() {
return {
chats: [],
}
},
mounted() {
this.getClients();
this.updateClients();
},
methods: {
getClients() {
socket.emit('get clients', true);
},
updateClients() {
socket.on('update clients', (clients) => {
this.chats = clients;
console.log(this.chats);
});
}
},
}
</script>
Then I get this, the box is empty:
But I need to get this, this will only appear when I force reload the page. I don't know what I'm doing wrong...
Oke, I've found out where the problem was, in another component I used plain javascript which brokes the whole reactive stuff.

Is it possible to call a function in one vue component from another vue component?

LogoutModal.vue
<template>
<div class="modal LogoutModal" v-bind:class="{'is-active':confirmLogout}">
<div class="modal-background"></div>
<div class="modal-content">
</div>
<button class="modal-close is-large" aria-label="close" #click="closeModal"></button>
</div>
</template>
<script>
export default {
name: "LogoutModal",
data() {
return {
confirmLogout: false
};
},
methods: {
closeModal: function() {
this.confirmLogout = false;
},
showModal: function() {
this.confirmLogout = true;
}
}
};
</script>
Navigation.vue
<template>
<aside class="menu">
<ul class="menu-list">
<li>
<a #click="showModal">Logout</a>
</li>
</ul>
<LogoutModal />
</aside>
</template>
<script>
import LogoutModal from "#/components/LogoutModal.vue";
export default {
name: "Navigation",
components: {
LogoutModal
}
};
</script>
I want to call the showModal function when i click on the Logout link. How can i achive that?
Or is that possible to change the variable in LogoutModal.vue from Navigation.vue. I have a variable called confirmLogout in LogoutModal.vue it has to be updated from Navigation.vue. How can I do that?
You should have confirmLogout in the navigation component, then pass that into your modal, you will also need a way to close the modal so in your modal you should this.$emit('close') when your user has signaled they want to close it.
<LogoutModal :open="confirmLogout" #close="confirmLogout=false" />
In your logout modal add a prop
<script>
export default {
name: "LogoutModal",
props: ['open'],
...
then in the template
<div class="modal LogoutModal" v-bind:class="{'is-active':open}">
You could define a dynamic property in LogoutModal, that sets it's state.
Here's more about that: https://v2.vuejs.org/v2/guide/components-props.html

Vue.JS Passing object to child for use inside slot

I am trying to pass an object that is loaded within DataContainer into a slot, so that the user can customise the view.
<data-container silo-id="5">
<div slot="content"> <!-- I tried :data="siloData" here but no luck -->
Your current balance is {{data.balance}}
</div>
</data-container>
So DataContainer loads the resource via http and sets the value to its 'siloData' property.
DataContainer's template has no content of its own just a placeholder for the slot.
<template>
<div>
<slot name="content"></slot>
</div>
</template>
When I try this the text is not interpolated and just remains as {{siloData.balance}} to the browser.
I have tried some examples from Vue.JS site like the todo list, but I must admit utterly confused, maybe because this is not a collection, but just a single (albeit complex) object.
Hopefully someone can point me in the right direction.
Many thanks
Phil
You can use a $emit event
Vue.component('data-container', {
template: '#data-container',
data() {
return {
siloData: {}
}
},
mounted() {
this.siloData = { name: "Silo", balance: 10 } // loading data
this.$emit('silo-loaded', this.siloData)
}
})
new Vue({
el: '#app',
data() {
return {
data: {}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app">
<data-container class="card" #silo-loaded="val => data = val">
<div slot="content">
Your current balance is {{ data.balance }}
</div>
</data-container>
</div>
<template id="data-container">
<div>
<slot name="content"></slot>
</div>
</template>

VueJS - Ajax communication between templates

I'm very new to VueJS and i'm having a difficult to share a result from Two template, that come from AJAX Request.
This is the home page:
<div>
<search-bar></search-bar>
<tracking-results></tracking-results>
</div>
This is the search-bar component, where i have a text input field and after press the button, it will do an Ajax Request:
<template>
<div class="row">
<div class="col-lg-8 col-lg-offset-3">
<div class="col-lg-5">
<div class="input-group">
<input type="text" class="form-control" placeholder="Numero Spedizione" v-model="trackingNumber">
<span class="input-group-btn">
<button class="btn btn-default"
type="button"
#click.prevent="search">Ricerca</button>
</span>
</div><!-- /input-group -->
</div><!-- /.col-lg-3 -->
</div>
</div><!-- /.row -->
</template>
<script>
export default {
data() {
return {
trackingNumber: '',
}
},
methods: {
search() {
Vue.http.options.emulateJSON = true;
this.$http.post('/endpoint').then(function (response) {
var parsedResponse = JSON.parse(response.data) || undefined;
/* HERE I WANT TO SEND THE RESPONSE TO ANOTHER COMPONENT */
}, function (err) {
console.log('ERROR', err);
});
}
}
}
</script>
I did tried with $broadcast, but my components arent child, are sibling.
I did see a way can be Vuex, but my application will not be written entirely with Vue. I will use this framework just to "simplify some Javascript process".
The only alternative i did find is to "merge" the search-bar and tracking-result in a single component. In this way the data will be "shared", and i can communicate with the state.
[Update: sync is removed in Vue 2, so you would need to follow the standard props-down, events-up design pattern]
You can have the parent viewmodel pass a prop to each of the components, using sync for the search bar. The search bar would populate the value in the ajax call, it would sync up to the parent and down to the tracking-results.
Some example code:
Vue.component('child1', {
props: ['ajaxData'],
methods: {
loadData: function () {
this.ajaxData = 'Some data is loaded';
}
},
template: '<div>Child1: {{ajaxData}} <button v-on:click="loadData">Load data</button></div>'
});
Vue.component('child2', {
props: ['ajaxData'],
template: '<div>Child2: {{ajaxData}}</div>'
});
new Vue({
el: 'body',
data: {
hi: 'Hello Vue.js!'
}
})
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<child1 :ajax-data.sync='hi'></child1>
<child2 :ajax-data='hi'></child2>
Ideally, you can send data to the parent, then the parent send data to the component via props. The parent handles the communication between the siblings.
Another way of doing it is using state management or vuex. But that depends on the complexity of your project. If it's a simple thing, I suggest to let the parent handle the communication.