Find a matching value in Vue component - laravel

I have passed this collection (postFavourite) to my vue component via props.
[{"id":1,"user_id":1,"post_id":2,"created_at":"2018-07-24 09:11:52","updated_at":"2018-07-24 09:11:52"}]
How do I then check if any instance of user_id in the collection is equal to userId which is the current logged in user (also sent via props).
Tried
let pf = _.find(this.postFavourite, { "user_id": this.userId})
Keep getting undefined as the value of the pf variable even though this.userID is equal to 1.
New to JS and Vue.js so any help would be great.
Here is the vue component code.
<template>
<div>
<i v-show="this.toggle" #click="onClick" style="color: red" class="fas fa-heart"></i>
<i v-show="!(this.toggle)" #click="onClick" style="color: white" class="fas fa-heart"></i>
</div>
</template>
<script>
export default {
data() {
return {
toggle: 0,
}
},
props: ['postData', 'postFavourite', 'userId'],
mounted() {
console.log("Post is :"+ this.postData)
console.log("User id is: "+ this.userId)
console.log("Favourite Object is :" +this.postFavourite);
console.log(this.postFavourite.find(pf => pf.user_id == this.userId));
},
methods: {
onClick() {
console.log(this.postData);
this.toggle = this.toggle ? 0 : 1;
}
}
}
</script>
This is how I passed the props to vue
<div id="app">
<favorite :post-data="'{{ $post->id }}'" :post-favourite="'{{Auth::user()->favourite }}'" :user-id="'{{ $post->user->id }}'"></favorite>
</div>

I gave up on lodash and find and just messed around with the data in the chrome console to work out how to check the value I wanted.
Then I built a loop to check for the value.
If it found it toggle the like heart on of not leave it off.
This will not be the best way to solve this problem but I'm just pleased I got my first real vue component working.
<template>
<div>
<i v-show="this.toggle" #click="onClick" style="color: red" class="fas fa-heart"></i>
<i v-show="!(this.toggle)" #click="onClick" style="color: white" class="fas fa-heart"></i>
</div>
</template>
<script>
export default {
props: ['postData', 'postFavourite', 'userId']
,
data() {
return {
toggle: 0,
favs: [],
id: 0
}
},
mounted () {
var x
for(x=0; x < this.postFavourite.length; x++){
this.favs = this.postFavourite[x];
if(this.favs['post_id'] == this.postData) {
this.toggle = 1
this.id = this.favs['id']
}
}
},
methods: {
onClick() {
console.log(this.postData)
if(this.toggle == 1){
axios.post('favourite/delete', {
postid: this.id
})
.then(response => {})
.catch(e => {
this.errors.push(e)
})
}
else if(this.toggle == 0){
axios.post('favourite', {
user: this.userId,
post: this.postData
})
.then(response => {
this.id = response.data
})
.catch(e => {
this.errors.push(e)
})
}
this.toggle = this.toggle ? 0 : 1;
}
}
}
</script>
Where I pass my props.
<favorite :post-data="'{{ $post->id }}'"
:post-favourite="{{ Auth::user()->favourite }}"
:user-id="'{{ Auth::user()->id }}'"></favorite>
Thanks to all that tried to help me.

From just the code you provided, I see no issue. However lodash is not required for this problem.
Using ES2015 arrow functions
let pf = this.postFavourite.find(item => item.user_id === this.userId);
Will find the correct item in your array
You can read more about this function in the mdn webdocs

You can use find() directly on this.postFavourite like this:
this.postFavourite.find(pf => pf.user_id == this.userId);
Here is another way to do it that might help you as well.
[EDIT]
In order to use find() the variable needs to be an array, this.postFavourite is sent as a string if you didn't use v-bind to pass the prop thats what caused the error.
To pass an array or an object to the component you have to use v-bind to tell Vue that it is a JavaScript expression rather than a string. More informations in the documentation
<custom-component v-bind:post-favourite="[...array]"></custom-component>

Related

Laravel vue.js and vuex link body text by id and show in a new component

I am very new to Laravel and Vuex, I have a simple array of post on my page.
test 1
test 2
test 3
I am trying to link the text on the AppPost.vue component and show the post that has been clicked on a new component (AppShowpost.vue) on the same page. I believe I have to get the post by id and change the state? any help would be good. Thank you.
when you click test 1 it will show "test 1" on a new component (AppShowpost.vue)
In side my store timeline.js, I belive I need to get the post by id and change the state ?
import axios from 'axios'
export default {
namespaced: true,
state: {
posts: []
},
getters: {
posts (state) {
return state.posts
}
},
mutations: {
PUSH_POSTS (state, data) {
state.posts.push(...data)
}
},
actions: {
async getPosts ({ commit }) {
let response = await axios.get('timeline')
commit('PUSH_POSTS', response.data.data)
}
}
}
My AppTimeline.vue component
<template>
<div>
<app-post
v-for="post in posts"
:key="post.id"
:post="post"
/>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex'
export default {
computed: {
...mapGetters({
posts: 'timeline/posts'
})
},
methods: {
...mapActions({
getPosts: 'timeline/getPosts'
})
},
mounted () {
this.getPosts()
}
}
</script>
My AppPost.vue component. I need to link the post.body to display the post in my AppShowpost.vue component.
<template>
<div class="w-full inline-block p-4">
<div class="flex w-full">
<p>
{{ post.body }}
</p>
</div>
</div>
</template>
<script>
export default {
props: {
post: {
required: true,
type: Object
}
}
}
</script>
My AppSowpost.vue component that needs to display the post that is clicked.
<template>
<div>
// Displaypost ?
</div>
</template>
<script>
export default {
// Get post from id ?
}
</script>
Okay you can create a new state in your vuex "current_state", asyou said, you can dispatch a mutation by passing the id to the vuex.
state: {
posts: [],
current_state_id : null
},
In your mutations
set_state_id (state, data) {
state.current_state_id = data;
}
On your app post.vue, you can set a computed property that watches the current state
computed: {
currentState() {
return this.$store.getters["timeline/current_state_id"];
}}
And create a watcher for the computed property to display the current id/post
watch: {
currentState: function(val) {
console.log(val);
},
Maybe this will help you. First I will recommend to use router-link. Read about router link here if your interested. It is very helpful and easy to use. But you will have to define the url and pass parameter on our vue-route(see bellow).
1.You can wrap your post.body in router-link as follow.
//With this approach, you don't need any function in methods
<router-link :to="'/posts/show/' + post.id">
{{ post.body }}
</router-link>
2. In your AppSowpost.vue component, you can find the post in vuex state based on url params as follow.
<template>
<div> {{ thisPost.body }} </div>
</template>
// ****************
computed: {
...mapState({ posts: state=>state.posts }),
// Let's get our single post with the help of url parameter passed on our url
thisPost() { return this.posts.find(p => p.id == this.$route.params.id) || {}; }
},
mounted() { this.$store.dispatch("getPosts");}
3. Let's define our vue route.
path: "posts/show/:id",
name: "showpost",
params: true, // Make sure the params is set to true
component: () => import("#/Components/AppShowPost.vue"),
Your Mutations should look as simple as this.
mutations: {
PUSH_POSTS (state, data) {
state.posts = data;
}
},
Please let me know how it goes.

How to use v-if from global function

This is my vue component code
<div v-if="$can('employee-create')" class="card-tools">
<router-link to="/admin/addphonebook" class="btn btn-success">
Add New
<i class="fa fa-phone"></i>
</router-link>
</div>
This is resources/assets/js/mixins/Permissions.vue file
export default {
methods: {
$can(permissionName) {
let route = window.routes.permission;
axios.get(route+`/${permissionName}`)
.then((resounse)=> {
return true;
})
.catch((error)=> {
return false;
});
},
},
};
This is resources/assets/js/app.js to import the mixin
import Permissions from './mixins/Permissions';
Vue.mixin(Permissions);
The $can function returning true but the 'Add New' button is not showing
v-if don't get the return true value
Anyone can help me?
Thanks in advance
#Csaba Gergely solved your problem. When you get data from server $can method return true once for a second or less, but after call $can steel returning false. You can create a variable called it success and store the axios call result.
It must be something like this
<div v-if="success" class="card-tools">
<router-link to="/admin/addphonebook" class="btn btn-success">
Add New
<i class="fa fa-phone"></i>
</router-link>
</div>
export default {
data(){
return {
success:false
}
},
methods: {
$can(permissionName) {
let route = window.routes.permission;
axios.get(route+`/${permissionName}`)
.then((resounse)=> {
this.success = true;
//return true;
})
.catch((error)=> {
this.success = false;
//return false;
});
},
},
P.S. Bocsi #Csaba Gergely, ha elhappoltam eloled a kerdest :(

vuejs refresh template after ajax return

I'm trying to make my VueJS template refresh after an AJAX request.
At the page first loading my datas are correctly updated en the template is OK.
in my template i have this section that i want to be updated each time one of the task has changed :
<li v-for="(task,index) in tasks" #click="setCurrentTask(index)" :class="{'active' : currentTask.id == task.id}">
<span><i class="far fa-dot-circle"></i></span>{{ task.attributes.name }}
<a class="ui mini label" :class="task.attributes.state.attributes.name">{{ task.attributes.state.attributes.name }}</a><br>
<div class="details">
{{ currentTask.attributes.estimatedStart }}<br>
<i class="fas fa-play" #click="startTask(index)"></i>
</div>
</li>
Then i have a request that says to change the state of a specific task to start it then it returns the task with the status updated :
export default {
name: "fdr",
data(){
return {
fdr:'',
tasks:'',
currentTask:null,
currentIndex:null,
}
},
methods: {
setCurrentTask: function (index) {
this.currentTask = this.tasks[index]
this.currentIndex = index;
if(this.currentTask.time != null){
$('.time').html( this.currentTask.timer.getTimeValues().toString());
$('#startDate').calendar();
}
},
startTask(index){
axios.get(this.$env_uri+'/api/task/'+this.tasks[index].id+"/start").then(function (resource) {
this.tasks[index] = resource.body
}.bind(this))
},
},
created() {
this.$http.get(this.$env_uri+'/api/fdr/' + this.$route.params.id)
.catch(response => {
return this.$router.push('/404')
})
.then(function (resource) {
this.fdr = resource.body
this.tasks = this.fdr.relationship.tasks
this.setCurrentTask(0);
}
);
},
When this.task is updated VueJs is not refreshing my template to display the new status of the task. I can't find a way to do it... do you have any ideas ?
Totally misunderstod the issue first.
The problem is updating updating an index in an array. This is something that has to be done with Array.$set(index, value) since Vue don't have a way to tell if an index in an array is changed.
You can read more about it here

Laravel router-link works only the first time

I am trying to fetch results from database in News.vue, and display them in Topnews.vue. I have two links fetched. When I click link1, it shows up the Topnews.vue template with everything working as intended, however, if i click link2, nothing happens, except for that the URL changes, but the template does not show up the result. If i refresh the page and click link2 or click on the navbar, then link2, it shows up, and same, clicking then link1, changes the URL, but doesnt show up. I'm really stuck on that and I'd be really glad if you help me out on that issue. Hope you understand.
News.vue
<template id="news">
<div class="col-sm-5">
<div class="cars" v-for="row in filteredNews" >
<div class="name" >
<p class="desc_top_time">{{row.created_at}}</p>
<span class="last_p"> {{row.category}}</span>
<h3 style="margin-bottom:-4px; font-size: 16px;">
<router-link class="btn btn-primary" v-bind:to="{name: 'Topnews', params: {id: row.id} }">{{row.title}}</router-link></h3>
</div></div></div>
</template>
<script>
export default {
data: function() {
return {
news: [],
}
},
created: function() {
let uri = '/news';
Axios.get(uri).then((response) => {
this.news = response.data;
});
},
computed: {
filteredNews: function() {
if (this.news.length) {
return this.news;
}
}
}
}
</script>
Topnews.vue
<template id="topnews1">
<div class="col-sm-7">
<div class="cars">
<img :src="topnews.thumb" class="img-responsive" width=100%/>
<div class="name" ><h3>{{ topnews.title }}</h3>
<p>
<br>{{ topnews.info }}<br/>
</p>
</div></div></div>
</template>
<script>
export default {
data:function(){
return {topnews: {title: '', thumb: '', info: ''}}
},
created:function() {
let uri = '/news/'+this.$route.params.id;
Axios.get(uri).then((response) => {
this.topnews = response.data;
});
}
}
</script>
Like GoogleMac said Vue will reuse the same component whenever possible. Since the route for both IDs use the same component Vue will not recreate it, so the created() method is only being called on the first page. You'll need to use the routers beforeRouteUpdate to capture the route change and update the data.
in TopNews.vue:
export default {
data:function(){
return {topnews: {title: '', thumb: '', info: ''}}
},
beforeRouteEnter:function(to, from, next) {
let uri = '/news/'+ to.params.id;
Axios.get(uri).then((response) => {
next(vm => {
vm.setData(response.data)
})
});
},
beforeRouteUpdate: function(to, from, next) {
let uri = '/news/'+ to.params.id;
Axios.get(uri).then((response) => {
this.setData(response.data);
next();
});
},
methods: {
setData(data) {
this.topnews = data
}
}
}
If you click a link referring to the page you are on, nothing will change. Vue Router is smart enough to not make any changes.
My guess is that the IDs are messed up. If you are using Vue devtools you will be able to easily see what data is in each link. Are they as you expect.

Accessing Vue components data

I'm having trouble accessing data in Vue component I use prop to pass my data from view to component like this. I'm using Laravel.
<fav-btn v-bind:store="{{ $store }}"></fav-btn>
And my component looks like this:
<template>
<a href="#" class="btn-link text-danger" v-on:click="favorite">
<i v-bind:class="{ 'fa fa-heart fa-2x': isFavorited == true, 'fa fa-heart-o fa-2x': isFavorited == false }" class="" aria-hidden="true"></i>
</a>
</template>
<script>
export default {
props: ['store'],
data(){
return{
isFavorited: this.store.favoritable.isFavorited,
}
},
methods: {
favorite: function () {
this.AjaxRequest();
this.ToggleFav();
},
ToggleFav: function () {
this.isFavorited = !(this.isFavorited);
},
AjaxRequest: function () {
if (this.isFavorited)
{
axios.delete('stores/' + this.store.favoritable_id);
}
else {
axios.post('stores/' + this.store.favoritable_id);
}
}
}
}
</script>
In Vue devtools I can see all the objects in props but I can't access them the isFavorited always stays false. Am I accessing the objects attributes incorrectly?
You are doing it wrong. You shouldn't diractly mutate a value which is in store. You should write a mutator in the store file and change value by that. Here is the docs.
https://vuex.vuejs.org/en/mutations.html

Resources