Splice doesn't delete any item from array - Angular12 - slice

I am trying create a function for deleting specific items from a shopping cart.
It doesn't give any errors, but when I click the button nothing happens. Any idea what might be the problem?
See the codes below about my issue.
service.ts
removeCartItem(product: Product){
this.cartItemList.map((a:any, index:any)=>{
if(product.id=== a.id){
this.cartItemList.splice(index,1);
}
})
cartitem.component.html
<div class="cartitem">
<div class="container">
<div class="row">
<div class="col">
{{ cartItem.name }}
</div>
<div class="col">
<img [src]="cartItem.imageUrl" class="card-img-top" alt="..." />
</div>
<div class="col-5">
{{ cartItem.description }}
</div>
<div class="col">{{ cartItem.price | currency: "EUR" }}</div>
<div class="col">{{ cartItem.qty }}</div>
<div>
<button (click)="removeItem(item)" class="btn btn-primary">
Remove from cart
</button>
</div>
</div>
</div>
</div>
cartitem.component.ts
constructor(public service: MessengerService) { }
ngOnInit(): void {
}
removeItem(item: Product){
this.service.removeCartItem(item);
}
cart.component.ts
cartItems: Product[] = [];
cartTotal = 0;
product: any;
constructor(private msg: MessengerService, public dialog: MatDialog) {}
ngOnInit() {
this.msg.getMsg().subscribe((product: Product) => {
this.addProductToCart(product);
});
}
addProductToCart(product: Product) {
let productExists = false;
for (let i in this.cartItems) {
if (this.cartItems[i].id === product.id) {
this.cartItems[i].qty++;
productExists = true;
break;
}
}
if (!productExists) {
this.cartItems.push({
id: product.id,
name: product.name,
description: product.description,
qty: 1,
price: product.price,
imageUrl: product.imageUrl,
purchased:product.purchased
});
}
this.cartItems.forEach((item) => {
this.cartTotal += item.qty * item.price;
});
}
cart.component.html
<ul *ngIf="cartItems.length > 0" class="list-group">
<li class="list-group-item">
<h3>My Cart</h3>
</li>
<li class="list-group-item" *ngFor="let item of cartItems">
<app-cartitem [cartItem]="item"></app-cartitem>
</li>
<li class="list-group-item">
<span>Total: {{ cartTotal | currency: "EUR" }} </span>
</li>
<li class="list-group-item">
<button
id="btnFinalize"
class="btn btn-primary"
(click)="purchaseDisabled(product)"
>
Finalize purchase
</button>
</li>
</ul>

The problem is that you are trying to splice inside map. Changing to the following will work:
removeCartItem(product: Product){
let indexToRemove: number = -1;
this.cartItemList.map((a:any, index:any)=>{
if(product.id === a.id){
indexToRemove = index;
}
return a;
});
if(indexToRemove !== -1){
this.cartItemList.splice(indexToRemove,1);
}
}
But notice there is no need to use map here. It's a waste of time and memory to copy the entire array again. Just a loop through the array to find the index to remove would be enough:
removeCartItem(product: Product){
let indexToRemove: number = -1;
let index: number = 0;
for(const cardItem of this.cartItemList){
if(product.id === cardItem.id){
indexToRemove = index;
}
index++;
}
if(indexToRemove !== -1){
this.cartItemList.splice(indexToRemove,1);
}
}

Related

Why does Vue evaluate my v-if to false despite it being true?

When I run this, vue returns the second template, even though groups.length is equal to 1.
Why? Does it have to do with the order in which the mounting occurs and v-if is evaluated? Again, I am certain that groups.length evaluates to 1. I have tried using beforeMount as opposed to mounted, but that did not work.
<template v-if = "groups.length">
<ul id = "groupList">
<li v-for = "group in groups">
<a>{{ group.name }}</a>
</li>
<div class = "addSidebar">
<label class = "btn" for = "modal-1">+</label>
</div>
</ul>
</template>
<template v-else>
<ul id = "groupList">
<li>
<a>You have not created/joined any groups.</a>
</li>
<div class = "addSidebar">
<label class = "btn" for = "modal-1">+</label>
</div>
</ul>
</template>
<script>
export default {
data() {
return {
groups: {}
}
},
methods: {
getGroups() {
axios.get('groupList').then((response) => {
this.groups = response.data
}).catch((errors) => {
console.log(errors)
});
},
newModal() {
$('#modal').modal('show');
}
},
mounted() {
this.getGroups()
},
name: "groupList"
}
</script>
you need to use javascript Async
https://www.w3schools.com/js/js_async.asp
<template >
<div>
<ul id="groupList" v-if="groups.length">
<li v-for="group in groups" :key="group.id">
<a>{{ group.name }}</a>
</li>
<div class="addSidebar">
<label class="btn" for="modal-1">+</label>
</div>
</ul>
<ul id="groupList" v-else>
<li>
<a>You have not created/joined any groups.</a>
</li>
<div class="addSidebar">
<label class="btn" for="modal-1">+</label>
</div>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
groups: {},
};
},
methods: {
async getGroups() {
await axios
.get("groupList")
.then((response) => {
this.groups = response.data;
})
.catch((errors) => {
console.log(errors);
});
},
newModal() {
$("#modal").modal("show");
},
},
async mounted() {
await this.getGroups();
},
name: "groupList",
};
</script>
in your code you created 2 <template > which is not valid syntax and vue should have root element
https://v2.vuejs.org/v2/guide/components.html#A-Single-Root-Element
Change
v-if = "groups.length"
to:
v-if="groups && groups.length > 0"
And you should have a single that contains one elements in it.

Conditional Component variable value increment Vue/Laravel

[1]so i have a laravel project going on, and i want to increment the value of the variable deliver_later_num, depending on the "deliver_today" present in the same component in the items[] array, which i am outputting in the template file, i cannot figure how to do it, i do not know if i can increment the value on the template side or on the component side. here is the component code:
cartContent = new Vue({
el: '#cartList',
data: {
items: [], //array containing all the items
deliver_later_num: 0, //value to increment
},
methods: {
remove: function (product_id) {
removeProductIfFromCart(product_id);
},
incQuantity: function (product_id){
incCart(product_id)
},
decQuantity: function (product_id){
decCart(product_id)
},
}
})
here is the template file :
<div id="cartList">
<div v-for="item in items" class="items col-xs-12 col-sm-12 col-md-12 col-lg-12 clearfix">
<div class="info-block block-info clearfix" v-cloak>
<div class="square-box pull-left">
<img :src="item.attributes.image" class="productImage" width="100" height="105" alt="">
</div>
<h6 class="product-item_title">#{{ item.name }}</h6>
<p class="product-item_quantity">#{{ item.quantity }} x #{{ item.attributes.friendly_price }}</p>
<ul class="pagination">
<li class="page-item">
<button v-on:click="decQuantity(item.id)" :value="item.id" class="page-link" tabindex="-1">
<i class="fa fa-minus"></i>
</button>
</li>
<li class="page-item">
<button v-on:click="incQuantity(item.id)" :value="item.id" class="page-link" >
<i class="fa fa-plus"></i>
</button>
</li>
<li class="page-item">
<button v-on:click="remove(item.id)" :value="item.id" class="page-link" >
<i class="fa fa-trash"></i>
</button>
</li>
<input hidden class="delivers_today_state" type="text" :value=" item.attributes.delivers_today "> // if this equals 0 i want to increment the deliver_later_num value
</ul>
</div>
</div>
</div>
laravel controller code :
public function add(Request $request){
$item = Items::find($request->id);
$restID=$item->category->restorant->id;
//Check if added item is from the same restorant as previus items in cart
$canAdd = false;
if(Cart::getContent()->isEmpty()){
$canAdd = true;
}else{
$canAdd = true;
foreach (Cart::getContent() as $key => $cartItem) {
if($cartItem->attributes->restorant_id."" != $restID.""){
$canAdd = false;
break;
}
}
}
//TODO - check if cart contains, if so, check if restorant is same as pervious one
// Cart::clear();
if($item && $canAdd){
//are there any extras
$cartItemPrice=$item->price;
$cartItemName=$item->name;
$theElement="";
//Is there a varaint
//variantID
if($request->variantID){
//Get the variant
$variant=Variants::findOrFail($request->variantID);
$cartItemPrice=$variant->price;
$cartItemName=$item->name." ".$variant->optionsList;
//$theElement.=$value." -- ".$item->extras()->findOrFail($value)->name." --> ". $cartItemPrice." ->- ";
}
foreach ($request->extras as $key => $value) {
$cartItemName.="\n+ ".$item->extras()->findOrFail($value)->name;
$cartItemPrice+=$item->extras()->findOrFail($value)->price;
$theElement.=$value." -- ".$item->extras()->findOrFail($value)->name." --> ". $cartItemPrice." ->- ";
}
Cart::add((new \DateTime())->getTimestamp(), $cartItemName, $cartItemPrice, $request->quantity, array('id'=>$item->id,'variant'=>$request->variantID, 'extras'=>$request->extras,'restorant_id'=>$restID,'image'=>$item->icon,'friendly_price'=> Money($cartItemPrice, env('CASHIER_CURRENCY','usd'),true)->format(),'delivers_today' => $item->deliverstoday ));
return response()->json([
'status' => true,
'errMsg' => $theElement
]);
}else{
return response()->json([
'status' => false,
'errMsg' => __("You can't add items from other restaurant!")
]);
//], 401);
}
}
public function getContent(){
//Cart::clear();
return response()->json([
'data' => Cart::getContent(),
'total' => Cart::getSubTotal(),
'status' => true,
'errMsg' => ''
]);
}
link to the items array vue dev tools screenshot
[1]: https://i.stack.imgur.com/smLRV.png
thanks for your precious help and time.
A computed property can be used if the deliver_later_num is only dependent on the presence/absence of deliver_today on elements of items array
cartContent = new Vue({
el: '#cartList',
data: {
items: {}
},
computed: {
deliver_later_num() {
let num = 0;
Object.keys(this.items).forEach(key => {
let item = this.items[key];
Object.keys(item).forEach(k => {
if(k === 'deliver_today' && item[k]) {
num++;
}
});
});
return num;
},
}

Active user does not show in real time chat app

I am following up this guide https://www.codechief.org/article/real-time-chat-app-with-laravel-6-vue-js-and-pusher#gsc.tab=0 to create real-time chat app in Laravel and Vue.
But it does not show list of active user.
Also this span never shows
<span class="text-muted" v-if="activeUser" >{{ activeUser.first_name }}` is typing...</span>
Also, this method does not work properly because in console log it shows undefined is typing...
sendTypingEvent() {
Echo.join('chat')
.whisper('typing', this.user);
console.log(this.user.fist_name + ' is typing now')
}
And it is not actually real time, because I see new messages only if I reload page.
This is Vue component
<template>
<div class="row">
<div class="col-8">
<div class="card card-default">
<div class="card-header">Messages</div>
<div class="card-body p-0">
<ul class="list-unstyled" style="height:300px; overflow-y:scroll" v-chat-scroll>
<li class="p-2" v-for="(message, index) in messages" :key="index" >
<strong>{{ message.user.first_name }}</strong>
{{ message.message }}
</li>
</ul>
</div>
<input
#keydown="sendTypingEvent"
#keyup.enter="sendMessage"
v-model="newMessage"
type="text"
name="message"
placeholder="Enter your message..."
class="form-control">
</div>
<span class="text-muted" v-if="activeUser" >{{ activeUser.first_name }} is typing...</span>
</div>
<div class="col-4">
<div class="card card-default">
<div class="card-header">Active Users</div>
<div class="card-body">
<ul>
<li class="py-2" v-for="(user, index) in users" :key="index">
{{ user.first_name }}
</li>
</ul>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props:['user'],
data() {
return {
messages: [],
newMessage: '',
users:[],
activeUser: false,
typingTimer: false,
}
},
created() {
this.fetchMessages();
Echo.join('chat')
.here(user => {
this.users = user;
})
.joining(user => {
this.users.push(user);
})
.leaving(user => {
this.users = this.users.filter(u => u.id != user.id);
})
.listen('ChatEvent',(event) => {
this.messages.push(event.chat);
})
.listenForWhisper('typing', user => {
this.activeUser = user;
if(this.typingTimer) {
clearTimeout(this.typingTimer);
}
this.typingTimer = setTimeout(() => {
this.activeUser = false;
}, 1000);
})
},
methods: {
fetchMessages() {
axios.get('messages').then(response => {
this.messages = response.data;
})
},
sendMessage() {
this.messages.push({
user: this.user,
message: this.newMessage
});
axios.post('messages', {message: this.newMessage});
this.newMessage = '';
},
sendTypingEvent() {
Echo.join('chat')
.whisper('typing', this.user);
console.log(this.user.fist_name + ' is typing now')
}
}
}
</script>
"Also, this method does not work properly because in console log it shows undefined is typing..."
i assume you made a typo in your console.log, you probably meant:
this.user.first_name
Concerning your "realtime" problem, i suspect it might be because your broadcasted Event is being queued, so you might want to use the ShouldBroadcastNow instead of ShouldBroadcast

Why the images of the publications are altered at the moment that I make a new publication with images?

when I reload browser I can see the post's pics
I have a component referring to the posts, but when you make a post that contains images, they are not updated and the post remains with the images from the previous post, the only way to prevent this from happening is by updating the browser and it should not be like that
Could someone help me to know what is happening, if I should update the component when loading the images or what should I do?
Thanks!
fomPost.vue =>
<template>
<div class="card bg-dark border-left-primary border-right-primary shd mb-4">
<div class="card-body">
<div v-if="status_msg" :class="{ 'alert-green': status, 'alert-danger': !status }" class="alert"
role="alert">{{ status_msg }}
</div>
<div class="form-group">
<textarea id="content-body" class="form-control" v-model="body"></textarea>
<a href="javascript:void(0)" class="text-lg float-right" data-toggle="tooltip"
data-title="Agregar imágenes" data-placement="bottom" #click="toggle()">
<i class="fas fa-cloud-upload-alt"></i>
</a>
</div>
<div v-show="isOpen" :style="'margin-top: 2rem'">
<div class="uploader"
#dragenter="OnDragEnter"
#dragleave="OnDragLeave"
#dragover.prevent
#drop="onDrop"
:class="{ dragging: isDragging }">
<div class="upload-control" v-show="images.length">
<label for="file">Agregar más imágenes</label>
</div>
<div v-show="!images.length">
<p>Arrastra las imágenes ó</p>
<div class="file-input">
<label for="file">Selecciónalas</label>
<input type="file" id="file" #change="onInputChange"
accept="image/x-png,image/gif,image/jpeg" multiple>
</div>
</div>
</div>
<div class="images-preview" v-show="images.length">
<div class="img-wrapper" v-for="(image, index) in images" :key="index">
<div class="thumbnail" :style="`background-image: url(${image.replace(/(\r\n|\n|\r)/gm)})`">
<div class="options">
<div>
<a href="javascript:void(0)" class="text-light" uk-tooltip="title: Eliminar"
#click="removeimage(index)">
<i class="fas fa-trash-alt"></i>
</a>
</div>
<div>
<a href="javascript:void(0)" class="text-light" uk-tooltip="title: Previsualizar"
uk-toggle="target: #modal-media-image" #click="showImage(image)">
<i class="fas fa-search-plus"></i>
</a>
</div>
</div>
</div>
<div class="details">
<span class="size" v-text="getFileSize(files[index].size)"></span>
</div>
<div id="modal-media-image" class="uk-flex-top" uk-modal>
<div class="uk-modal-dialog uk-width-auto uk-margin-auto-vertical">
<button class="uk-modal-close-outside" type="button" uk-close></button>
<img width="1024px" :src="dialogImageUrl">
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card-footer bg-opacity-7-dark">
<button type="button" #click="createPost" class="btn btn-primary float-right">
<template v-if="isCreatingPost" class="align-items-center">
<div class="d-flex align-items-center">
<span class="mr-1">Publicando</span>
<div class="loadingio-spinner-dual-ring-botj7pu8xqc">
<div class="ldio-ifliw7yncz">
<div></div>
<div>
<div></div>
</div>
</div>
</div>
</div>
</template>
<template v-else>
Publicar
</template>
</button>
</div>
</div>
</template>
<script>
import {mapActions} from 'vuex'
export default {
name: "FormPostText",
props: ['profile'],
data() {
return {
dialogImageUrl: "",
dialogVisible: false,
isDragging: false,
dragCount: 0,
files: [],
images: [],
status_msg: "",
status: "",
isCreatingPost: false,
title: "",
body: "",
isOpen: false
}
},
mounted() {
$("#content-body").emojioneArea({
placeholder: "¿Qué estás pensando?",
searchPlaceholder: "Buscar",
buttonTitle: "Usa la tecla [TAB] para insertarlos más rápido",
pickerPosition: 'bottom',
filtersPosition: "bottom",
searchPosition: "top"
});
},
methods: {
...mapActions({
create: "post/makePost"
}),
showImage(file) {
this.dialogImageUrl = file;
this.dialogVisible = true;
},
OnDragEnter(e) {
e.preventDefault();
this.dragCount++;
this.isDragging = true;
return false;
},
OnDragLeave(e) {
e.preventDefault();
this.dragCount--;
if (this.dragCount <= 0)
this.isDragging = false;
},
onInputChange(e) {
const files = e.target.files;
Array.from(files).forEach(file => this.addImage(file));
},
onDrop(e) {
e.preventDefault();
e.stopPropagation();
this.isDragging = false;
const files = e.dataTransfer.files;
Array.from(files).forEach(file => this.addImage(file));
},
addImage(file) {
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
this.showNotification("La imágen no puede ser mayor a 2MB.");
return;
}
if (!file.type.match('image.*')) {
this.showNotification(`${file.name} no es una imágen`);
return;
}
this.files.push(file);
const img = new Image(), reader = new FileReader();
reader.onload = (e) => this.images.push(e.target.result);
reader.readAsDataURL(file);
return isLt2M;
},
removeimage: function (index) {
this.images.splice(index, 1);
},
getFileSize(size) {
const fSExt = ['Bytes', 'KB', 'MB', 'GB'];
let i = 0;
while (size > 900) {
size /= 1024;
i++;
}
return `${(Math.round(size * 100) / 100)} ${fSExt[i]}`;
},
toggle() {
this.isOpen = !this.isOpen;
},
createPost() {
var body = $("#content-body").val();
var text = emojione.toImage(body);
if (!this.validateForm()) {
return false;
}
this.isCreatingPost = true;
const formData = new FormData();
formData.append("user", this.profile.uid);
formData.append("body", text);
this.files.forEach(file => {
formData.append('images[]', file, file.name);
});
this.create(formData).then((res) => {
if (res.data.status === 0) {
this.status = code;
this.showNotification(res.data.msg);
}
document.querySelector(".emojionearea-editor").innerHTML = '';
this.isCreatingPost = false;
this.images = [];
this.files = [];
this.isOpen = false;
let post = res.data;
this.$emit("new", post);
}).catch(error => {
console.log(error)
this.isCreatingPost = false;
});
},
validateForm() {
if (!$("#content-body").val()) {
this.status = false;
this.showNotification("Debes escribir algo para publicar...");
return false;
}
return true;
},
showNotification(message) {
this.$swal.fire({
icon: 'error',
html: message
});
}
}
}
</script>
Post.vue =>
<template>
<div id="timeline">
<div v-if="authenticated.username === username || isFriend">
<FormPost :profile="profile" #new="addPostText"></FormPost>
</div>
<!--<pre>{{postsArr}}</pre>-->
<div v-if="postsCount > 0">
<div v-for="(post, index) in postsArr" :key="index">
<div class="cardbox shadow-lg bg-opacity-5-dark shd">
<div class="cardbox-heading">
<div class="dropdown float-right">
<button class="btn btn-sm btn-dark btn-circle" data-toggle="dropdown"
data-boundary="window">
<i class="fas fa-ellipsis-h"></i>
</button>
<div class="dropdown-menu dropdown-scale dropdown-menu-right" role="dropdown">
<template v-if="post.user.id === user.uid || post.friend.id === user.uid">
<b-dropdown-item href="javascript:void(0)" #click="deletePost(post.post.id, index)">
<span class="uk-margin-small-right" uk-icon="icon: trash"></span> Eliminar
</b-dropdown-item>
<b-dropdown-divider></b-dropdown-divider>
</template>
<b-dropdown-item href="javascript:void(0)">
<span class="uk-margin-small-right text-danger" uk-icon="icon: warning"></span>
Reportar
</b-dropdown-item>
</div>
</div>
<div class="media m-0">
<div class="d-flex mr-3">
<a v-bind:href="post.user.username">
<img class="img-fluid rounded-circle" v-bind:src="post.friend.photo" alt="User">
</a>
</div>
<div class="media-body">
<p class="m-0"><a v-bind:href="post.friend.username">{{ '#' + post.friend.username }}</a></p>
<small><span><i
class="far fa-clock"></i> {{ since(post.post.created_at) }}</span></small>
</div>
</div>
</div>
<div class="cardbox-item">
<p class="mx-4">{{ post.post.body | setEmoji }}</p>
<div v-if="post.images.length > 0">
<!--<photo-grid
:box-height="'600px'"
:box-width="'100%'"
:boxBorder="0"
:excess-text="'+ {{count}} imágenes'"
uk-lightbox="animation: slide"
>
<img v-for="(imahe, index) in post.images" v-bind:src="`http://127.0.0.1:8000/storage/images/${post.friend.id}/${imahe.url}`" :key="index"/>
</photo-grid>-->
<ImagesGrid :images="post.images" :idFriend="post.friend.id"
uk-lightbox="animation: slide"></ImagesGrid>
</div>
</div>
<div class="cardbox-base">
<ul class="float-right">
<li><a><i class="fa fa-comments"></i></a></li>
<li><a><em class="mr-5">{{ post.comments_count || comments_count }}</em></a></li>
<li><a><i class="fa fa-share-alt"></i></a></li>
<li><a><em class="mr-3">03</em></a></li>
</ul>
<ul>
<li>
<Likes :postid="post.post.id" :user="user"></Likes>
</li>
<li><a href="#"><img
src="http://www.themashabrand.com/templates/bootsnipp/post/assets/img/users/3.jpeg"
class="img-fluid rounded-circle" alt="User"></a></li>
<li><a href="#"><img
src="http://www.themashabrand.com/templates/bootsnipp/post/assets/img/users/1.jpg"
class="img-fluid rounded-circle" alt="User"></a></li>
<li><a href="#"><img
src="http://www.themashabrand.com/templates/bootsnipp/post/assets/img/users/5.jpg"
class="img-fluid rounded-circle" alt="User"></a></li>
<li><a href="#"><img
src="http://www.themashabrand.com/templates/bootsnipp/post/assets/img/users/2.jpg"
class="img-fluid rounded-circle" alt="User"></a></li>
<li><a><span>242 Likes</span></a></li>
</ul>
</div>
<CommentsPost
#new="commentsCount"
:postid="post.post.id"
:postuserid="user.uid"
:user="user"
></CommentsPost>
</div>
</div>
<nav class="pagination-outer">
<ul class="pagination">
<li class="page-item" :class="[pagination.current_page > 1 ? '' : 'disabled']">
<a href="#" class="page-link" aria-label="Previous" #click.prevent="changePage(pagination.current_page - 1)">
<span aria-hidden="true">«</span>
</a>
</li>
<li class="page-item" v-for="page in pagesNumber" :class="[page == isActived ? 'active' : '']">
<a class="page-link" href="#" v-bind:data-hover="page" #click.prevent="changePage(page)">{{page}}</a>
</li>
<li class="page-item" :class="[pagination.current_page < pagination.last_page ? '' : 'disabled']">
<a href="#" class="page-link" aria-label="Next" #click.prevent="changePage(pagination.current_page + 1)">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
</div>
<div v-else class="card bg-opacity-5-dark mb-4">
<div class="card-body">
<span class="text-light">No tiene posts aún.</span>
</div>
</div>
</div>
</template>
<script>
import Vue from 'vue'
var moment = require("moment");
moment.locale("es");
import {mapActions, mapGetters} from 'vuex'
import FormPost from "../Posts/FormPost";
import Likes from "../Posts/Likes";
import CommentsPost from "../Posts/CommentsPost";
import ImagesGrid from "../Posts/ImagesGrid";
export default {
name: "Posts",
props: {
username: {
type: String,
required: true
},
profile: {
type: Object,
required: true
},
user: {
type: Object,
required: true
},
isFriend: {
type: Boolean,
required: true
}
},
data() {
return {
page: 0,
offset: 4,
comments_count: ''
}
},
mounted() {
this.getPosts();
},
beforeRouteEnter(to, from, next) {
this.getPosts()
next()
},
beforeRouteUpdate(to, from, next) {
this.getPosts()
next()
},
computed: {
...mapGetters({
authenticated: "auth/authenticated",
postsArr: "post/postsLists",
pagination: "post/postsPagination",
postsCount: "post/postsCount"
}),
isActived: function() {
return this.pagination.current_page;
},
pagesNumber: function () {
if(!this.pagination.to) {
return [];
}
var from = this.pagination.current_page - this.offset;
if(from < 1) {
from = 1;
}
var to = from + (this.offset * 2);
if(to >= this.pagination.last_page) {
to = this.pagination.last_page;
}
var pagesArray = [];
while(from <= to) {
pagesArray.push(from);
from++;
}
return pagesArray;
}
},
methods: {
...mapActions({
getPostsByUser: "post/fetchPosts",
removePost: "post/deletePost",
}),
async getPosts(page) {
await this.getPostsByUser(page);
await this.$forceUpdate();
},
async addPostText(post) {
await this.postsArr.unshift(post);
await this.$forceUpdate();
await this.changePage(1);
},
since(d) {
return moment(d).fromNow();
},
commentsCount(count) {
this.comments_count = count;
},
deletePost(post, index) {
this.postsArr.splice(index, 1)
this.removePost(post)
},
changePage(page) {
this.pagination.current_page = page;
this.getPosts(page);
}
},
filters: {
setEmoji: (value) => {
var rx = /<img\s+(?=(?:[^>]*?\s)?class="[^">]*emojione)(?:[^>]*?\s)?alt="([^"]*)"[^>]*>(?:[^<]*<\/img>)?/gi;
return value.replace(rx, "$1")
}
},
components: {
FormPost,
Likes,
CommentsPost,
ImagesGrid
},
watch: {
'$route': 'getPosts'
}
ImagesGrid.vue =>
<template>
<photo-grid
:box-height="'600px'"
:box-width="'100%'"
:boxBorder="0"
:excess-text="'+ {{count}} imágenes'"
uk-lightbox="animation: slide"
>
<a :href="`http://127.0.0.1:8000/storage/images/${idFriend}/${imahe.url}`"
v-for="(imahe, index) in images"
v-bind:data-image="`http://127.0.0.1:8000/storage/images/${idFriend}/${imahe.url}`"
:key="index">
</a>
</photo-grid>
</template>
<script>
export default {
props: ['idFriend', 'images'],
watch: {
$route(to, from, next) {
this.$forceUpdate();
next()
}
}
};
</script>
Post.js =>
import axios from 'axios'
import store from './index'
export default {
namespaced: true,
state: {
posts: [],
posts_count : 0,
pagination: {
'total': 0,
'current_page': 0,
'per_page': 0,
'last_page': 0,
'from': 0,
'to': 0,
}
},
getters: {
postsLists(state) {
return state.posts;
},
postsCount(state) {
return state.posts_count
},
postsPagination(state) {
return state.pagination
}
},
mutations: {
SET_POST_COLLECTION (state, data) {
state.posts = data.posts.data ;
state.pagination = data.paginate;
state.posts_count = data.paginate.total;
}
},
actions: {
makePost({commit,dispatch}, data) {
return new Promise((resolve, reject) => {
axios.post('user/post/create', data)
.then((response) => {
dispatch("fetchPosts", 1)
resolve(response);
})
.catch((error) => {
reject(error);
});
})
},
fetchPosts({commit}, page) {
return new Promise((resolve, reject) => {
axios.get(`user/${store.state.user.profile.username}/posts?page=${page}`)
.then((response) => {
//console.log(response.data.posts.data);
commit('SET_POST_COLLECTION', response.data);
resolve(response);
})
.catch((error) => {
reject(error);
});
})
},
deletePost({commit}, id) {
return new Promise((resolve, reject) => {
let params = {'id': id};
axios.post('user/post/delete', params)
.then((response) => {
console.log(response.data)
resolve(response);
})
.catch((error) => {
reject(error);
});
})
},
createComment({commit}, comment) {
return new Promise((resolve, reject) => {
axios.post('user/post/comment/create', comment)
.then((response) => {
console.log(response.data)
resolve(response);
})
.catch((error) => {
reject(error);
});
})
}
}
}

Display content from AJAX request using setState

I'm doing a webapp using github search API. I want the info for each repo to be displayed under the specific repo.
I want the content from the AJAX request to be displayed when the specific button is being pressed. I am using React. Since I'm using item.x to access the information I need to get that item and I assume I need to use map but when doing that it will display all the results and not just the specific repository's. Anyway that I can get the Item since it currently says it's undefined?
let searchTerm;
class SearchBox extends React.Component {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
this.state = { repositories: [],
showInfo: false };
}
render() {
let moreDetail;
if(this.state.showInfo){
moreDetail= <div className="info"> <li>
<p>Open issue count </p>:{item.open_issues_count}
</li>
<li>
<p>Number of forks </p>:{item.forks}
</li>
<li>
<p>Language </p>:{item.language}
</li></div>;
}
return(
<div>
<form>
<input type="text" className="searchbox" ref={(input) => { this.searchBox = input; }}/>
<button onClick={this.onClick}>Search</button>
</form>
<h2>Repositories</h2>
<ul>
{ this.state.repositories.map( ( item, index ) => (
<div key={ index }>
<a href={item.html_url}> <li >
{ item.name }
</li>
</a>
{moreDetail}
<button onClick={this._handleClick.bind(this)}>Detailed view</button>
</div>
)) }
</ul>
</div>
);
}
_handleClick(){
this.setState({
showInfo: !this.state.showInfo
});
}
onClick(event) {
searchTerm = this.searchBox.value;
let endpoint = 'https://api.github.com/search/repositories?sort=stars&order=desc&q=' + searchTerm;
console.log(searchTerm);
fetch(endpoint)
.then(blob => blob.json())
.then(response => {
this.setState({ repositories: response.items });
});
event.preventDefault();
}
}
The problem is with context. When you define the moreDetail variable, you don't have item in your context (only during the map function you have that.)
One option is to use the variable moreDetail as a function that receives the item you want to show.
Your render method should look something like:
render() {
const moreDetail = (item) => ( !this.state.showInfo ? <span /> :
<div className="info">
<li>
<p>Open issue count </p>:{item.open_issues_count}
</li>
<li>
<p>Number of forks </p>:{item.forks}
</li>
<li>
<p>Language </p>:{item.language}
</li>
</div>
);
return (
<div>
<form>
<input type="text" className="searchbox" ref={(input) => { this.searchBox = input; }}/>
<button onClick={this.onClick}>Search</button>
</form>
<h2>Repositories</h2>
<ul>
{ this.state.repositories.map( ( item, index ) => (
<div key={ index }>
<a href={item.html_url}> <li >
{ item.name }
</li>
</a>
{moreDetail(item)}
<button onClick={this._handleClick.bind(this)}>Detailed view</button>
</div>
)) }
</ul>
</div>
);
}

Resources