<template>
<div class="row w-20 " >
<div class="card col-4 ">
<ul>
<li v-for="message in conversations.messages" :key="message.messages">
{{message.text}}
</li>
</ul>
</div>
</div>
</template>
<script>
export default {
props: ['conversation_index_route'],
data() {
return {
conversations: [],
url: 'http://127.0.0.1:8000/comms/conversation/',
}
},
beforeMount() {
this.$eventBus.$on('selectConversation', this.getConversations)
this.getConversations();
},
methods: {
getConversations(id) {
console.log(this.url+id);
axios.get(this.url+ id)
.then(response => {
this.conversations = response.data;
this.conversations = JSON.parse(this.conversations.message);
console.log(this.conversations);
});
}
}
}
</script>
conversation_index_route:"http://127.0.0.1:8000/comms/conversation"
conversations:Object
all_staff_attended:false
centre_id:5
children:""
classes:""
cover_image:"https://via.placeholder.com/150x150"
created_at:"2020-05-30 19:01:59"
exited_users:null
id:257
last_id:null
messages:Array[1]
0:"{"_id":1854,"text":"This is the beginning of this conversation","createdAt":"2020-05-30 19:01:59","system":true}"
parent_users:"3016"
parents:Array[1]
staff_users:"180,181"
staffs:Array[2]
status_id:1
title:"Test"
updated_at:"2020-05-30 19:01:59"
url:"http://127.0.0.1:8000/comms/conversation/"
So what shall I code to use the message text in my template to display the text messages?
You'd have to do JSON.parse(conversations.messages[0]).text. This way you parse the object inside messages and have access to its properties.
Simply JSON.parse the string
var myJson = "[{\"id\":72,\"food_item_id\":\"56\",\"variation_id\":\"20\",\"price\":\"50\",\"created_at\":\"2021-06-29T05:29:14.000000Z\",\"updated_at\":\"2021-06-29T05:29:14.000000Z\",\"variant\":null}]";
var myJson2 = JSON.parse(myJson);
console.log(myJson2);
Related
I am attempting to do a SPA using Vue.js but unfortunately I know almost nothing about it, I followed a tutorial and got something up and running. This should hopefully be relatively simple!
I'm trying to create a simple page that:
Does a REST API call and pulls some JSON
A list with links of a particular field in the list of results is displayed on the left side of the screen
(I've managed until here)
Now I would like to be able to click on one of the links and see on the right side of the screen the value of another field for the same record.
For instance, suppose my JSON is:
{
"jokes":{
[
"setup":"setup1",
"punchline":"punchline1"
],
[
"setup":"setup2",
"punchline":"punchline2"
],
[
"setup":"setup3",
"punchline":"punchline3"
]
}
}
So in my screen I would see:
setup1
setup2
setup3
So if I click in setup1 I see punchline1, setup2 displays punchline2 and so on.
Here is my code - I'm basically trying to display the punchline in the moduleinfo div. I realise the current solution does not work. I've been searching but can't find any similar examples. Any pointers would be greatly appreciated.
<template>
<div class="home">
<div class="module-list">
<input type="text" v-model.trim="search" placeholder="Search"/>
<div>
<ul>
<li class="modules" v-for="value in modulesList" :key="value.id">
{{ value.setup }}
</li>
</ul>
</div>
</div>
<div class="moduleinfo">
<h2>Module info</h2>
<!-- <p>{{ value.punchline }}</p> -->
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'Home',
data: function(){
return {
jokes: [],
search : ""
}
},
mounted() {
this.getModules();
},
methods: {
getModules() {
var self = this
const options = {
method: 'GET',
url: 'https://dad-jokes.p.rapidapi.com/joke/search',
params: {term: 'car'},
headers: {
'x-rapidapi-key': '...',
'x-rapidapi-host': 'dad-jokes.p.rapidapi.com'
}
};
axios.request(options)
.then(response => {
self.jokes = response.data;
console.log(response.data);
}).catch(function (error) {
console.error(error);
});
}
},
computed: {
modulesList: function () {
var jokes = this.jokes.body;
var search = this.search;
if (search){
jokes = jokes.filter(function(value){
if(value.setup.toLowerCase().includes(search.toLowerCase())) {
return jokes;
}
})
}
return jokes;
}
},
};
</script>
Thanks!
I was building a sample Single File Component in my Vue 2 CLI app, and when I came back to post it, Ryoko had already answered the question with the same approach that I recommend, adding a new property to track showing the punchline.
Since I already built it, I figured that I might as well post my component, which does change the layout, using a table instead of a list, but the functionality works.
<template>
<div class="joke-list">
<div class="row">
<div class="col-md-6">
<table class="table table-bordered">
<thead>
<tr>
<th>SETUP</th>
<th>PUNCHLINE</th>
</tr>
</thead>
<tbody>
<tr v-for="(joke, index) in jokes" :key="index">
<td>
{{ joke.setup }}
</td>
<td>
<span v-if="joke.showPunchline">{{ joke.punchline }}</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
jokes: [
{
setup: "setup1",
punchline: "punchline1"
},
{
setup: "setup2",
punchline: "punchline2"
},
{
setup: "setup3",
punchline: "punchline3"
}
]
}
},
methods: {
getPunchline(index) {
this.jokes[index].showPunchline = true;
},
addPropertyToJokes() {
// New property must be reactive
this.jokes.forEach( joke => this.$set(joke, 'showPunchline', false) );
}
},
mounted() {
this.addPropertyToJokes();
}
}
</script>
You can add a new property inside the data object and then make a new method to set it accordingly when you click the <a> tag. Have a look at the code below, it was a copy of your current solution, edited & simplified to show the addition that I made to make it easier for you to find it.
The select method will insert the object of the clicked joke to the selectedJoke so you can render it below the Module Info.
Because it's defaults to null, and it might be null or undefined, you have to add v-if to the attribute to check wether there is a value or not so you don't get error on the console.
<template>
<div class="home">
<div class="module-list">
<input type="text" v-model.trim="search" placeholder="Search"/>
<div>
<ul>
<li class="modules" v-for="value in modulesList" :key="value.id">
{{ value.setup }}
</li>
</ul>
</div>
</div>
<div class="moduleinfo">
<h2>Module info</h2>
<p v-if="selectedJoke">{{ selectedJoke.punchline }}</p>
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'Home',
data: function(){
return {
jokes: [],
search : "",
selectedJoke: null,
}
},
methods: {
select(joke) {
this.selectedJoke = joke;
},
},
};
</script>
I am creating commenting system using vue.js and laravel5.8.
I have done with models and seeding, so I have now 10 comments to one post (id is 51).
But I got this error,
Property or method "comment" is not defined on the instance but
referenced during render
and
Cannot read property 'user' of undefined
I have problems with fetching data.
I created a new endpoint for a comment function.
web.php
Route::get('results/{post}', 'ResultsController#show')->name('posts.show');
Route::get('results/{post}/comments', 'CommentsController#index');
I want to show comments in show.blade.php.
ResultsController.php
public function show(Post $post)
{
$recommended_posts = Post::latest()
->whereDate('date','>',date('Y-m-d'))
->where('category_id','=',$post->category_id)
->where('id','!=',$post->id)
->limit(7)
->get();
$posts['particular_post'] = $post;
$posts['recommended_posts'] = $recommended_posts;
$post->comments()->with('user')->get();
return view('posts.show',compact('posts'));
}
show.blade.php
<comments-component :post="{{ $posts['particular_post']->comments }}"></comments-component>
comments.vue
<div class="reply-comment" :v-for="comment in comments">
<div class="user-comment" >
<div class="user">
<!--<img src="" alt="" >-->
<avatar :username="comment.user.name" :size="30" ></avatar>
</div>
<div class="user-name">
<span class="comment-name">{{ comment.user.name }}</span>
<p> {{ comment.body }} </p>
</div>
</div>
<div class="reply">
<div class="seemorecomments">
see more
</div>
<button class="reply-button">
<i class="fas fa-reply"></i>
</button>
</div>
</div>
<script>
import Avatar from 'vue-avatar'
export default {
props: ['post'],
components: {
Avatar
},
mounted() {
this.fetchComments()
},
data: () => ({
comments: {
data: []
}
}),
methods: {
fetchComments() {
axios.get(`/results/${this.post.id}/comments`).then(({data}) => {
this.comments = data
})
}
}
}
CommentsController.php
public function index(Post $post)
{
return $post->comments()->paginate(5);
$post->comments()->with('user')->get();
}
comment.php
protected $with = ['user'];
I cannot get data object here.
Within axios, you may need to access data from the response that is returned (see console.log examples here), try the following within your comments component:
methods: {
fetchComments() {
axios.get(`/results/${this.post.id}/comments`).then((response) => {
this.comments = response.data.data
})
}
}
Note response.data.data is used.
I assume returning the ->paginate() will put the results within a data key in the returned array. If not, then just use response.data.
Also, in the controller getting the comments change to the following:
public function index(Post $post)
{
return $post->comments()->with('user')->paginate(5);
}
This will eager load the users with the queried comments.
Good afternoon, I have two child components Header and Pagination. In Header, I have an input search engine and two inputs (title and body) in order to be able to add a post to Pagination. I managed to transfer the search value to the Pagination component, but I don’t know how to transfer the value from two inputs (title, body). I use to transfer the event bus. Help me please pass the value of the two inputs (title, body) into the Pagination component when you click the AddPost button.
My code on GitHub
Screenshot of app
My code of component Header:
<template>
<div class="header">
<input type="text" v-model="search" class="header_input_search" placeholder="Search" #input="saveMessage" />
<img src="src/assets/milk.png">
<div class="header_div_inputs">
<input type="text" v-model="createTitle" class="created"/>
<p><input type="text" v-model="createBody" class="createBody"/></p>
</div>
<button #click="addPost()" class="addPost">AddPost</button>
</div>
</template>
<script>
import axios from 'axios';
import {eventEmitter} from './main'
export default {
name: 'Header',
data () {
return {
search: '',
createTitle: '',
createBody: '',
}
},
methods:{
saveMessage(){
eventEmitter.$emit('messageSave', this.search)
},
}
}
</script>
My code of component Pagination:
<template>
<div class = "app">
<ul>
<li v-for="(post, index) in paginatedData" class="post" :key="index">
<router-link :to="{ name: 'detail', params: {id: post.id, title: post.title, body: post.body} }">
<img src="src/assets/nature.jpg">
<p class="boldText"> {{ post.title }}</p>
</router-link>
<p> {{ post.body }}</p>
</li>
</ul>
<div class="allpagination">
<button type="button" #click="page -=1" v-if="page > 0" class="prev"><<</button>
<div class="pagin">
<button class="item"
v-for="n in evenPosts"
:key="n.id"
v-bind:class="{'selected': current === n.id}"
#click="page=n-1">{{ n }} </button>
</div>
<button type="button" #click="page +=1" class="next" v-if="page < evenPosts-1">>></button>
</div>
</div>
</template>
<script>
import {mapState} from 'vuex'
import {eventEmitter} from './main'
export default {
name: 'app',
data () {
return {
current: null,
page: 0,
visiblePostID: '',
pSearch: ''
}
},
mounted(){
this.$store.dispatch('loadPosts')
},
computed: {
...mapState([
'posts'
]),
evenPosts: function(posts){
return Math.ceil(this.posts.length/6);
},
paginatedData() {
const start = this.page * 6;
const end = start + 6;
return this.filteredPosts.slice(start, end);
},
filteredPosts() {
return this.posts.filter((post) => {
return post.title.match(this.pSearch);
});
},
},
created(){
eventEmitter.$on('messageSave', (string) => {
this.pSearch = string
})
}
}
</script>
You can wrap title and body in an object
addPost() {
const post = {
title: this.createTitle,
body: this.createBody
}
eventEmitter.$emit('postAdd', post)
}
and then listen as normal
created(){
eventEmitter.$on('postAdd', (post) => {
console.log(post)
// do whatever you want
})
}
I have not worked on vue js but agreed with #ittus answer. You can make an object consisting of your required data which you want to share across the component and pass it as an event data.
I Created my API with PHP and here is the link: https://monstajams.co/streaming/rest/api/album/read.php
But anytime i put it in my Vue.js (Home.vue) file using axios No data is displayed on the front-end.
Here is my code below:
<ul class="ta-track-list" v-if="faqs && faqs.length">
<li class="ta-track-card column col-2 flex-column" v-for="faq of faqs">
<div class="inner">
<div class="artwork" role="link">
<span role="link" style="background-image: url(http://localhost/mymusic/assets/images/artwork/Wizkid-Soco.jpg);">
</span>
<div class="hover flex align-center justify-center">
<button id="webutton" class="ta-secondary play" onclick='playSong()'>
<i class="material-icons">play_arrow</i>
</button>
</div>
</div>
<div class="info">
<div class="title white-primary-hover" role="lin">{{ faqs }}</div>
<div class="username light-white-hover" role="link">{{ faq.artist }}</div>
<div class="released">{{ faq.duration }}</div>
</div>
</div>
</li>
</ul>
<script>
import axios from 'axios';
export default {
name: 'home',
data: () =>({
faqs: [],
errors: []
}),
created() {
axios.get('https://monstajams.co/streaming/rest/api/album/read')
.then(response => {
this.faqs = response.data;
})
.catch(e => {
this.errors.push(e)
})
}
}
</script>
The problem is your code incorrectly assumes axios.get() resolves to the raw response, but it actually resolves to a response wrapper, where the raw response is contained in a data subproperty of the wrapper, which coincidentally has the same name as the target property within the response.
You can either change your Axios response handler to get the inner data field:
axios.get('https://monstajams.co/streaming/rest/api/album/read')
.then(response => {
// this.faqs = response.data; // response.data is the raw response, but you need the array within the response (also named "data")
this.faqs = response.data.data;
})
demo
Or leave your frontend alone, and update your PHP backend to send only the array in the response:
// FROM THIS RESPONSE:
{
data: [/* PLAYLIST DATA */]
}
// TO THIS RESPONSE:
[/* PLAYLIST DATA */]
You are not updating your data accordingly to Vue docs.
For reactive changes see this document.
In the example below i update my list before Vue is mounted so rendering can occur accordingly.
let vm = new Vue({
el: "#app",
data: {
todos: []
},
methods: {
updateList() {
axios.get('https://monstajams.co/streaming/rest/api/album/read')
.then(res => {
res.data.data.forEach(item => {
Vue.set(vm.todos, vm.todos.length, {
text: item.title,
done: true
})
})
})
}
},
beforeMount() {
this.updateList()
},
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>
<div id="app">
<h2>Todos:</h2>
<ol>
<li v-for="todo in todos">
<label>
<span>
{{ todo.text }}
</span>
</label>
</li>
</ol>
</div>
I wanted to create a tree view from an XML file, and I did this. However, when I decided to make it more flexible I encountered some problems.
Here are my components:
Vue.component('elname', {
props: ['text'],
template: '<span>{{ text }}</span>'
})
Vue.component('recursive', {
props: ['d', 'liname', 'openclose'],
template: '#recursive',
data: function() {
return {
seen: true
}
}
}
)
and the Vue object looks like this:
var appp = new Vue({
el: '#here',
data: function(){
return {
friends: '',
}
},
beforeMount() {
parser = new DOMParser();
var response = "<scope><friend><name>Alex</name><hobbies><h>music</h><h>salsa</h></hobbies></friend><friend><name>Natasha</name><hobbies><h>hiking</h></hobbies></friend></scope>";
xml = parser.parseFromString(response, 'text/xml');
children = xml.getElementsByTagName('scope')[0];
this.friends = children;
}
})
I have this variable seen in recursive component
Vue.component('recursive', {
props: ['d', 'liname', 'openclose'],
template: '#recursive',
data: function() {
return {
seen: true // <-- here it is
}
}
}
)
It must change its value #click event to hide a nested list (please, see the JSfiddle), but when it changes it updates its value IN SEVERAL components.
How to make its value be updated only in a particular component?
Here is a template:
<div id="here">
<recursive :d="friends" openclose="[-]"></recursive>
</div>
<template id="recursive">
<div>
<ul v-if="d.children.length != 0">
<li v-for="n in d.childNodes" #click="seen = !seen">
<elname :text="n.tagName"></elname>
{{ openclose }}
{{seen}} <!-- it is just for testing purposes to illustrate how seen var changes -->
<recursive :d="n" openclose="[-]"></recursive>
</li>
</ul>
<ul v-else>
<elname :text="d.textContent"></elname>
</ul>
</div>
</template>
You have two issues:
You need to use click.stop so that the click event doesn't propagate to parents
You need a component inside your recursive to handle the toggling
Vue.component('elname', {
props: ['text'],
template: '<span>{{ text }}</span>'
});
Vue.component('recursive', {
props: ['d', 'openclose'],
template: '#recursive',
components: {
toggler: {
data() {
return {
seen: true
}
},
methods: {
toggle() {
this.seen = !this.seen;
}
}
}
}
});
var appp = new Vue({
el: '#here',
data: function() {
return {
friends: '',
}
},
beforeMount() {
parser = new DOMParser();
var response = "<scope><friend><name>Alex</name><hobbies><h>music</h><h>salsa</h></hobbies></friend><friend><name>Natasha</name><hobbies><h>hiking</h></hobbies></friend></scope>";
xml = parser.parseFromString(response, 'text/xml');
children = xml.getElementsByTagName('scope')[0];
this.friends = children;
}
})
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.min.js" integrity="sha256-Ab5a6BPGk8Sg3mpdlsHzH6khPkniIWsvEuz8Fv/s9X8=" crossorigin="anonymous"></script>
<div id="here">
<recursive :d="friends" openclose="[-]"></recursive>
</div>
<template id="recursive">
<div>
<ul v-if="d.children.length != 0">
<li is="toggler" v-for="n in d.childNodes" inline-template>
<div #click.stop="toggle">
<elname :text="n.tagName"></elname>
{{ openclose }}
<recursive v-if="seen" :d="n" openclose="[-]"></recursive>
</div>
</li>
</ul>
<ul v-else>
<elname :text="d.textContent"></elname>
</ul>
</div>
</template>
Currently you have 1 seen variable on an element, which controls the state for all child-elements. So a click on any child will change the seen value in the parent and show/hide all children of this parent.
Solution 1
Change the type of your seen variable to an array - with the same length as the children array. And change your handler to #click="seen[i] = !seen[i]"
Solution 2
Move the click listener to the children. So put #click="seen = !seen" on your outermost div in the template and render the whole list only on v-if="d.children.length && seen"
Vue.component( 'recursive-list', {
props: ["d"],
data: () => ({ expand: true }),
template: `<div style="margin: 5px">
<div v-if="Array.isArray(d)"
style="border: 1px solid black">
<button #click="expand = !expand">Show/Hide</button>
<template v-show="expand">
<recursive-list v-for="e in d" :d="e" />
</template>
<p v-show="!expand">...</p>
</div>
<p v-else>{{d}}</p>
</div>`
} )
new Vue({
el: '#main',
data: { d: ["Text", ["a","b","c"],[[1,2,3],[4,5,6],[7,8]]]
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.js"></script>
<div id='main'>
<h3>List:</h3>
<recursive-list :d="d"></recursive-list>
</div>
I've some modifications on your structure, maybe it's not exactly what you need but I think will became more clear.
<template id="tree">
<div>
<ul v-for="(tree, k, idx) in tree.childNodes">
<node :tree="tree" :idx="idx"></node>
</ul>
</div>
</template>
<template id="node">
<li>
<div v-if="tree.childNodes.length">
<span #click="seen = !seen">{{ tree.tagName }}</span>
<span>{{ seen }}</span>
<ul v-for="(node, k, id) in tree.childNodes">
<node :tree="node" :idx="id"></node>
</ul>
</div>
<div v-else>{{ tree.textContent }}</div>
</li>
</template>
https://jsfiddle.net/jonataswalker/Lw52t2dv/