How to unshift to append under spicific div in Vue - vue.js

I have comments and this comment containe likes, My propblem is when I try to push replay to the comment it is append to the wrong comment, So I used this.$el; to get the target comment bu the propblem is unshift/push just use with list and it shows error push is not a function
My qustion is how I can use unshift/push to append under spicific div not list
async addReplay(comment, e){
const element = this.$el; //this to get the target div
}
here the post replay function
async getaddReplay(id, setReplayPlace){
await axios.get('/account/api/auth/user/')
.then(response => {
this.currentUserImage = response.data.profile_image
})
const replayData = {
content: this.commentReplayContent,
video: this.$route.params.video_id,
parent: id
}
await axios.post(`/video/api/video/comment/${id}/replay/create/`, replayData)
.then(response => {
console.log(this.setReplayPlace)
// here I want append the item to spicific div
this.setReplayPlace.unshift({ content: this.commentReplayContent, author: this.$store.state.user.username, id:response.data.id, author_image:this.currentUserImage, video:this.$route.params.video_id ,likes:0, total_parents:0, check_like: false, publish:'now'})
this.commentReplayContent = ''
})
.catch(error => {
console.log(error)
})
}
Edit
Here there are comments with replies component and I pass the data to repliy component using props
<ul v-for="(comment, index) in comments" :key="index">
<li class="comment-object">
<div class="image-container">
<img class="profile-pic" :src="comment.author_image" v-on:change="currentUserImage" alt="profile picture" id="user_video_comment_profile_image" refs="user_video_comment_profile_image" />
</div>
<div class="comment-text">
<h2 class="username" style="color: #C2C3C4">{{comment.author}} <span class="muted">· {{comment.publish}}</span>
<DeleteComment :comment="comment" v-if="comments || index" :comments="comments" :index="index" />
</h2>
<p class="comment">{{comment.content}} </p>
<RepliesJustAdded #justAddedReplies="addRepliesToParent" :replaiesJustAdded="replaiesJustAdded" :setReplayPlace="setReplayPlace" :allReplies="allReplies" :replaiesData="replaiesData" v-if="replaiesJustAdded || setReplayPlace || allReplies || replaiesData" />
</div>
</li>
</ul>
The Replay component
<ul v-for="replay in replies" :key="replay.id" id="video_comments_replies" >
<li class="comment-object">
<div class="image-container">
<img class="profile-pic" :src="replay.author_image" alt="profile picture" id="user_video_comment_profile_image" refs="user_video_comment_profile_image" />
</div>
<div class="comment-text">
<h2 class="username" style="color: #C2C3C4">{{replay.author}} <span class="muted">· {{replay.publish}}</span></h2>
<p class="comment">{{replay.content}} </p>
<RepliesActionButtons :replay="replay" />
</div>
</li>
</ul>

I didn't find the line where you are trying to call push but I thing I know what's wrong: your are trying to call push but array-like collections don't have such method. You should convert your collection to array before calling push. Try something like that:
// create a `NodeList` object
const divs = document.querySelectorAll('div');
// convert `NodeList` to an array
const divsArr = Array.from(divs);
After that all methods of Array.prototype will be available.

Related

Vue Computed Value Filter (script setup)

I can't find anything relevant online. The relevant answers online dont use the tag.
Does it even work with script setup?
<template>
<div class="bg-white md:container md:mx-auto w-10">
<ul class="flex flex-wrap p-2 justify-center bg-black text-white">
<li class="p-2">
Filter
</li>
<li class="p-2">
Search
</li>
</ul>
<div class="flex flex-wrap justify-center h-52 content-center">
<div class="flex justify-center">
<div class="mb-3 xl:w-96">
<label
for="exampleSearch2"
class="form-label inline-block mb-2 text-gray-700"
>Search</label
>
<input
v-model="searchValue"
type="search"
class="form-control block w-full px-3 py-1.5 text-base font-normal text-gray-700 bg-white bg-clip-padding border border-solid border-gray-300 rounded transition ease-in-out m-0 focus:text-gray-700 focus:bg-white focus:border-blue-600 focus:outline-none"
id="exampleSearch2"
placeholder="Type query"
/>
<div class="flex flex-wrap">
<div>Test: {{ searchValue }}</div>
</div>
</div>
</div>
</div>
<div class="grid lg:grid-cols-4 md:grid-cols-3 gap-8">
<dogCard v-for="breed in breedsArray" :key="breed.id" :breed="breed" />
</div>
</div>
</template>
<script setup>
import dogCard from "../components/dogCard.vue";
import { ref, onMounted, computed } from "vue";
import searchBox from "./searchBox.vue";
let URL = "https://api.thedogapi.com/v1/breeds";
const searchValue = ref("");
let breedsData = ref([]);
// Fetching API Data
function getBreedsArray() {
fetch(URL)
.then((response) => response.json())
.then((data) => {
breedsData.value = data;
console.log("Data Fetched", breedsData.value);
})
.catch((error) => {
console.error(error);
});
}
onMounted(() => {
getBreedsArray();
});
const breedsArray = computed({
get() {
return breedsData.value;
},
set(val) {
breedsData.value = breedsData.value.filter((breed) => breed.name.includes() == searchValue);
},
});
let isVisible = ref(false);
</script>
Im trying to filter the dogCard through a Searchbox. I just don't know how to do it.
The Data of 'breedsData' should change on when something is entered in the Searchbox.
Thanks in advance, I'm stuck for so long now, an i dont how to fix this.
In the filter() in the computed setter, you do:
(breed) => breed.name.includes() == searchValue
But you probably want to use the value of the ref instead of the ref itself, and put it into includes():
(breed) => breed.name.includes(searchValue.value)
Note that this would permanently remove filtered breeds, as you change the breedsData.value. A better approach might be to just use the computed getter (I don't think the setter works the way you want it to anyway):
const breedsArray = computed(() => searchValue.value ?
breedsData.value.filter( breed => breed.name.includes(searchValue.value)) :
breedsData.value
)
Since searchValue is reactive, a change to it will trigger an update of the breedsArray.

Vue 3 : Event, emit and props

I have a problem with event, emit and props (and probably some logic too)
I have a component A in which I have a loop with a component B.
In this loop, I have a method to open the modal (which is a component C) but this method is not part of component B.
Like this :
<a>
<!-- MODAL-->
<div v-if="showModal">
<modal-cat #cat="catId = getCatId($event)" #addTx="addTx($event)"></modal-cat>
</div>
<div v-if="transactions.length != 0" class="mx-auto">
<div v-for="tx in transactions" :key="tx">
<div class="mb-2 border border-gray-600 rounded-lg bg-white pt-2 pb-4">
<div class="flex justify-end">
<span
class="inline-flex items-center justify-center h-6 w-6 rounded-full text-lg bg-blue-800 text-white"
#click="showModal = true, txToAdd = tx">
<i class='bx bx-plus'></i>
</span>
</div>
<transaction-data :transaction="tx" :address="walletAddress"></transaction-data>
</div>
</div>
</div>
</a>
In this modal, I fetch some data (in fact, a array of categories) that I also display in a loop.
Like this :
<div class="modal-container">
<div v-for="(categorie) in categories" :key="categorie">
<p #click="$emit('cat', categorie.id)">{{ categorie.name}}</p>
</div>
<div class="modal-footer">
<slot name="footer">
<button class="modal-default-button" #click="$emit('addTx', 'ok')">
OK
</button>
</slot>
</div>
</div>
I need some data from my modal in my component A but I also need some data from my component B in my component A (to add a transactions to a category)
I managed to get the data I wanted like this (And I can get it):
const showModal = ref(false);
const txToAdd = ref({});
const catId = ref(0);
function getCatId(event) {
return event
}
const addTx = (value) => {
if (value === "ok") {
//console.log(txToAdd.value); <= the value are well displayed in the console.
let data = {
tx: txToAdd.value,
catId: catId.value
}
store.dispatch("categories/addTxToCategories", data);
}
}
But in my store, when I try to get the payload, I can't access to the data and I only get the payload object.
Is there something wrong with my logic ? What am I doing wrong ?
EDIT
I just need to wrap the result in spread operator, like this :
const addTx = (value) => {
if (value === "ok") {
//console.log(txToAdd.value);
let data = {
tx: {...txToAdd },
catId: catId.value
}
store.dispatch("categories/addTxToCategories", {...data });
}
}
And in my store, the payload MUST be the second argument :
async addTxToCategories({ commit }, payload) {}

How can I make my section hide only after the submit button is pressed. right now the section disappears after I press one letter

vue.js, how can I make my section hide only after the submit button is pressed. right now the section disappears after I press one letter. I want the V-if and V-else to activate only after the user has submitted their request. or if routing the results on to a different page would easier id like to go that route also.
<template>
<div class="home">
<section id="whiteClawVideo" class="videoWrapper d-block w-100">
<div class="video-container fluid">
<iframe width="100%" height="600" src="https://www.youtube.com/embed/JORN2hkXLyM?
autoplay=1&loop=1" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope;
picture-in-picture" allowfullscreen></iframe>
</div>
</section>
<form #submit.prevent="SearchMovies()" class="search-box">
<input type="text" placeholder="What are you looking for? " v-model="search" />
<input type="submit" value="Search">
</form>
<div class="movies-list" v-if="search !== ''" >
<div class="container">
<div class="row">
<div class="col-3" v-for="movie in movies" :key="movie.imdbID">
<router-link :to="'/movie/'+movie.imdbID" class="movie-link">
<img class="movieImg" height="100%" :src="movie.Poster" alt="Movie Poster" />
<div class="type">{{ movie.Type }}</div>
<div class="detail">
<p class="year">{{movie.Year}}</p>
<h3>{{ movie.Title }}</h3>
<p>{{movie.imdbID}}</p>
</div>
</router-link>
</div>
</div>
</div>
</div>
<div class="container" v-else>
<MovieSection />
<SecondMovieSection />
</div>
</div>
</template>
import { ref } from 'vue';
import env from '#/env.js';
import MovieSection from '#/components/MovieSection.vue';
import SecondMovieSection from '#/components/SecondMovieSection.vue'
export default {
components: {
MovieSection,
SecondMovieSection
},
setup () {
const search = ref("");
const movies = ref([]);
const SearchMovies = () => {
if (search.value !== "") {
fetch(`API_HERE`)
.then(response => response.json())
.then(data => {
console.log(data)
movies.value = data.Search;
})
}
}
return {
search,
movies,
SearchMovies
}
}
}
Well, it closes once you type a single character because search is a model - it updates on every keypress you do within input it's bound to. What you wanna do instead is hide form based on whether you have entries in your movies array or not, so try changing v-if="search !== ''" to v-if="!movies.length"

How to mark notification as Read using Vue js?

I'm trying to mark a notification as read when a user clicks on it. Right now, when a user clicks on one of the notifications, it marks all of the user's notifications as read, instead of just the one.. I created a "click" function on the <a>.
AppHeader.vue:
<li class="dropdown-item preview-item dropdown-item-notifications" v-for="(unreadNotification, index) in unreadNotifications" :key="index">
<a class="dropdown-item" type="button" #click="markAsRead()">
<div class="preview-item-content flex-grow py-2">
<div v-if="unreadNotification ? unreadNotification.type === 'App\\Notifications\\InterviewRequestCandidateReply' : ''">
<p class="font-weight-light small-text"> <InterviewRequestCandidateReplyMessage /> </p>
</div>
<div v-else-if="unreadNotification ? unreadNotification.type === 'App\\Notifications\\InterviewRequestReplyConfirmed' : ''">
<p class="font-weight-light small-text"> <InterviewRequestReplyConfirmedMessage /> </p>
</div>
</div>
</a>
</li>
methods: {
markAsRead: async function() {
try {
const response = await employerService.markNotificationAsRead();
this.loadNotificationsData();
console.log(this.notifications);
} catch (error) {
this.$toast.error("Some error occurred, please refresh!");
}
},
},
employerService.js from code above:
export function markNotificationAsRead(id) {
return http().post(`employer/notifications/${id}`);
}
In my #click="markAsRead() function I think I need to get the id so maybe something like this #click="markAsRead(unreadNotification.id). Now the tricky part and where I'm stuck is, how can I pass this id into the markNotificationAsRead() function below?
const response = await employerService.markNotificationAsRead();
I'm not sure how to do this. I'm using Laravel for my backend.
--------------------- UPDATE: ---------------------
Something strange is happening. I know that the answers provided should work, but for some reason it's still marking all records as read.
EmployerNotificationsController.php:
public function markAsRead($id)
{
$notifications = Auth::user()->notifications->where('id', $id)->first()->markAsRead();
return response()->json($notifications, 200);
}
api.php:
Route::post('/employer/notifications/{id}', 'EmployerNotificationsController#markAsRead')
->name('employer.notifications.mark-as-read');
Any ideas why?
You're absolutely right about passing the ID to the click handler: #click="markAsRead(unreadNotification.id)
Your markAsRead method will receive the ID as an argument that you can then pass to your service method:
markAsRead: async function(id) {
try {
const response = await employerService.markNotificationAsRead(id);
//...
},
<li class="dropdown-item preview-item dropdown-item-notifications" v-for="(unreadNotification, index) in unreadNotifications" :key="index">
<a class="dropdown-item" type="button" #click="markAsRead(unreadNotification.id)">
<div class="preview-item-content flex-grow py-2">
<div v-if="unreadNotification ? unreadNotification.type === 'App\\Notifications\\InterviewRequestCandidateReply' : ''">
<p class="font-weight-light small-text"> <InterviewRequestCandidateReplyMessage /> </p>
</div>
<div v-else-if="unreadNotification ? unreadNotification.type === 'App\\Notifications\\InterviewRequestReplyConfirmed' : ''">
<p class="font-weight-light small-text"> <InterviewRequestReplyConfirmedMessage /> </p>
</div>
</div>
</a>
</li>
methods: {
markAsRead: async function(id) {
try {
const response = await employerService.markNotificationAsRead(id);
this.loadNotificationsData();
console.log(this.notifications);
} catch (error) {
this.$toast.error("Some error occurred, please refresh!");
}
},
},
just like this ...

Vue-test-utils cannot call text on empty wrapper

I have the following html template
<div>
<li class="posted-comment" v-for="commentDetail in commentDetail" :key="commentDetail.id"
#mouseover="hoverIn()" #mouseout="hoverOut()">
<div class="comment-user-avatar"><img :src="commentDetail.img_url"></div>
<div class="comment-user-details">
<span class="distinct-user-name">{{ commentDetail.name}}</span>
<span class="distinct-user-title">{{ commentDetail.user }}</span>
<span class="full-stop">.</span>
<span class="distinct-time-stamp">{{ commentDetail.time}}</span>
</div>
<div class="comment-info-block-wrapper">
<div id="distinct-user-comment">
{{ commentDetail.text}}
</div>
</div>
</li>
Calling wrapper.find('li.posted-comment .comment-info-block-wrapper #distinct-user-comment') says cannot call text on empty wrapper, looks like the "distinct-user-comment" id cant be found
describe('Comment.vue', () => {
it('renders distinct user comment class', () => {
const string = 'So what the German automaker is likely to focus on today is the bigger picture. This will be the first time we see the Taycan free from any prototype bodywork.';
const wrapper = shallowMount(Comment)
expect(wrapper.find("li.posted-comment .comment-info-block-wrapper #distinct-user-comment").text()).equal(string)
})
})