Iterating through an array with a fakeAPI promise function Vue.js - arrays

<template>
<div class="hello">
<h1>Example 2</h1>
<input #click="send" type="button" value="Send" />
<div class="out" v-if="successIds.length">{{ successIds }}</div>
</div>
</template>
<script>
/*
#return
resolve: { id: 1, success: true }
or
reject: { success: false }
*/
const fakeApiRequest = (id) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
const success = id % 2;
success ? resolve({ id, success }) : reject({ success });
}, 2000);
});
};
export default {
data() {
return {
// Fetch ids
ids: [1, 2, 3, 4, 5, 6],
// Complete ids
successIds: [],
};
},
methods: {
// iterating through the ids array
newValues(id) {
this.ids.forEach((el) => el.this.fakeApiRequest(id));
},
// filling the successIds array
send(id) {
this.$set(this, "successIds", this.newValues(id));
},
},
};
</script>
How can I fill and display the successId array in the template with the iterated elements of the existing array using Promise function within a $set Vue method? I seem to fail trying to reach the fakeApiRequest in the newValues function.

I'm not sure why you think you need to use the $set method. There are clear documented uses for it, and setting/replacing an array is not one. Besides that though, to be frank, there are a lot of issues with your code, and it's not very clear what you're trying to do, but I did my best to correct all the issues and left comments describing my changes.
sandbox example
<template>
<div class="hello">
<h1>Example 2</h1>
<input type="button" value="Send" #click="send" />
<div v-if="successIds.length" class="out">{{ successIds }}</div>
</div>
</template>
<script>
export default {
data() {
return {
// Fetch ids
ids: [1, 2, 3, 4, 5, 6],
// Complete ids
successIds: []
};
},
methods: {
// note 1: method loops through all ids so not sure what the 'id' param is for
// note 2: calling other async methods requires we use 'async' and 'await' keywords
async newValues(id) {
// note 3: the code in this method before was non-sensical to me, e.g. "el.this.fakeApiRequest" isn't valid syntax
// and the forEach loop didn't do anything with the actual result returned by fakeApiRequest.
// note 4: forEach actually isn't designed for async code. Must use normal for loop
const results = [];
for (let i = 0; i < this.ids.length; i++) {
let res = null;
try {
res = await this.fakeApiRequest(this.ids[i]);
} catch (rejectVal) {
res = rejectVal;
}
console.log('res', res);
results.push(res);
}
return results;
},
// note 5: not sure why id is a param as this method isn't called with any specific id
async send(id) {
// note 6: the use of $set before was unnecessary
this.successIds = await this.newValues();
},
/*
#return
resolve: { id: 1, success: true }
or
reject: { success: false }
*/
// note 7: moved this here so it can be called by other methods. decreased timeout time for sanity's sake
fakeApiRequest(id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const success = id % 2;
success ? resolve({ id, success }) : reject({ success });
}, 100);
});
}
}
};
</script>

Related

Why my setState returns an array, but the state is a promise when the component rerender?

The code below try to check if an url is reachable or not.
The urls to check are stored in a state called trackedUrls
I update this state with an async function checkAll.
The object just before being updated seems fine, but when the component rerender, it contains a promise !
Why ?
What I should change to my code ?
import React from "react"
export default function App() {
const [trackedUrls, setTrackedUrls] = React.useState([])
// 1st call, empty array, it's ok
// 2nd call, useEffect populate trackedUrls with the correct value
// 3rd call, when checkAll is called, it contains a Promise :/
console.log("trackedUrls :", trackedUrls)
const wrappedUrls = trackedUrls.map(urlObject => {
return (
<div key={urlObject.id}>
{urlObject.label}
</div>
)
})
// check if the url is reachable
// this works well if cors-anywhere is enable, click the button on the page
async function checkUrl(url) {
const corsUrl = "https://cors-anywhere.herokuapp.com/" + url
const result = await fetch(corsUrl)
.then(response => response.ok)
console.log(result)
return result
}
// Checks if every url in trackedUrls is reachable
// I check simultaneously the urls with Promise.all
async function checkAll() {
setTrackedUrls(async oldTrackedUrls => {
const newTrackedUrls = await Promise.all(oldTrackedUrls.map(async urlObject => {
let isReachable = await checkUrl(urlObject.url)
const newUrlObject = {
...urlObject,
isReachable: isReachable
}
return newUrlObject
}))
// checkAll works quite well ! the object returned seems fine
// (2) [{…}, {…}]
// { id: '1', label: 'google', url: 'https://www.google.Fr', isReachable: true }
// { id: '2', label: 'whatever', url: 'https://qmsjfqsmjfq.com', isReachable: false }
console.log(newTrackedUrls)
return newTrackedUrls
})
}
React.useEffect(() => {
setTrackedUrls([
{ id: "1", label: "google", url: "https://www.google.Fr" },
{ id: "2", label: "whatever", url: "https://qmsjfqsmjfq.com" }
])
}, [])
return (
<div>
<button onClick={checkAll}>Check all !</button>
<div>
{wrappedUrls}
</div>
</div>
);
}
Konrad helped me to grasp the problem.
This works and it's less cumbersome.
If anyone has a solution with passing a function to setTrackedUrls, I'm interested just for educational purpose.
async function checkAll() {
const newTrackedUrls = await Promise.all(trackedUrls.map(async urlObject => {
let isReachable = await checkUrl(urlObject.url)
const newUrlObject = {
...urlObject,
isReachable: isReachable
}
return newUrlObject
}))
setTrackedUrls(newTrackedUrls)
}
You can only put data into setState.

Render Array in Vuejs

I tried to render a list of items by Vuejs, the code below is a simplified version of it. Basically, I need it to display data, the state and data appears in VueDevTool but not on the page.
<template>
<div>
<h1>{{this.sendersList.length}}</h1> <!-- 0 -->
<h1>{{senders.length}}</h1> <!-- 0 -->
</div>
</template>
<script>
export default{
data () {
return {
sendersList: []
}
},
created () {
this.$store.dispatch('getAllSenders', {
app_id: this.$route.params.chat_id
}).then(response => {
this.sendersList = response
})
},
computed: {
senders(){
return this.$store.getters.getAllSenders
}
}
}
</script>
Store code returns data as normal, VueDevtool can see it but I cant find away to render it on the front-end
getAllMessages(context, data){
return new Promise((resolve, reject) => {
axios.post('messages/getAllMessages', {
sender_id: data.sender_id
}).then(response => {
let payload = []
for (let index = 0; index < response.data.length; index++) {
payload.push({
message_id: response.data[index].message_id,
message_content: response.data[index].message_content,
message_type: response.data[index].message_type,
message_sentiment: response.data[index].message_sentiment,
sender_id: response.data[index].sender_id,
user_id: response.data[index].user_id,
from_sender: response.data[index].from_sender,
created_date: response.data[index].created_date,
})
}
context.commit('getAllMessages', payload)
resolve(payload)
}).catch(error => {
reject(error)
})
})
},
Try change this
<h1>{{this.sendersList.length}}</h1>
To this
<h1>{{sendersList.length}}</h1>

React Question about promise in a for loop

I am in the process of learning React and making HTTP requests.
Recently Im trying to implement a dropdown for the webpage that Im working on. In my code I had to loop through an array of id, and make a post request for each of the id to extract the metadata. So Im encountering a problem with the dropdown options. The dropdown options are suppose to be the names for the corresponding id.
The array of id is an array of objects that looks like this
[{key: "someidnumber1", count: 5}, {key: "someidnumber2", count: 5}, {key: "someidnumber3", count: 10},....]
So what I did first is to loop through the id array, and make a post request on each of the id as parameter. This is inside my render method.
render() {
return(
<SomeOtherComponent>
{ //Do something to fetch the ids
let promises = [];
let names = [];
let options = [];
ids.map(id => {
promises.push(
axios
.post(TARGET_META_URL, {
filters: [
{
field: "id",
values: [id.key]
}
]
})
.then(response => {
// adding the name from the data into the names array
names.push(response.data[0].name);
})
});
Promise.all(promises).then(() => {
// Wait for the promises to collection all the names
// and pass into a new array
options = [...names];
}
return (
<Dropdown
options={options}
/>
);
}
</SomeOtherComponent>
);
}
My dropdown options after opening it is empty. So I did a couple console log and figured out that the options is declared outside the Promise.all so when the render() method is called, the dropdown takes in an empty array. I need help on how to setup the options for the dropdown so it waits for all the code before it finish running. I tried putting the second return inside the Promise.all() but I get an error method saying that render() doesn't have a return.
Make another component which fetches the data and renders them once the responses have come back. Use Promise.all to wait for all of the Promises to resolve together.
const getName = id => axios
.post(TARGET_META_URL, {
filters: [
{
field: "id",
values: [id.key]
}
]
})
.then(response => response.data[0].name);
const AsyncDropdown = ({ ids }) => {
const [options, setOptions] = useState();
useEffect(() => {
Promise.all(ids.map(getName))
.then(setOptions)
.catch((err) => {
// handle errors
});
}, [ids]);
return options ? <Dropdown options={options} /> : null;
}
And replace your original render method with:
render() {
return(
<SomeOtherComponent>
<AsyncDropdown ids={ids} />
</SomeOtherComponent>
);
}
Maybe this will help -
componentDidMount() {
let promises = [];
let options = [];
ids.map(id => {
promises.push(
axios
.post(TARGET_META_URL, {
filters: [
{
field: "id",
values: [id.key]
}
]
})
});
Promise.all(promises).then((response) => {
// Wait for the promises to collection all the names
// and pass into a new array
options = response.map(res => res.data[0].name);
this.setState({ options })
}
}
render() {
return(
<SomeOtherComponent>
{ this.state.options?.length ? <Dropdown options={this.state.options} /> : null }
</SomeOtherComponent>
);
}

Shuffle.js implementation with React.js

I'm trying to activate shuffle.js component functionality (search, filter and sort) with react.js. However, the documentation on the website is very limited. I know that I need to add a search input and some buttons to do what I want, yet I'm not sure how to connect my search box input and other button events to manipulate the photogrid (or other elements within a container) that is being rendered by react.
I have imported shuffle.js as node module and initialised it on the react page. The basic code that they provide seems to be working and displays the photo grid, however, that's pretty much it. I also want to implement the search, filtering and sorting functionality but there isn't documentation on how to do that in react.js. The code below shows the photogrid implementation but nothing else.
import React, {Component} from "react";
import Shuffle from 'shufflejs';
class PhotoGrid extends React.Component {
constructor(props) {
super(props);
const grayPixel = 'data:image/gif;base64,R0lGODlhAQABAIAAAMLCwgAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==';
const blackPixel = 'data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=';
const greenPixel = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mO02Vz4HwAE9AJhcLBN6AAAAABJRU5ErkJggg==';
this.state = {
photos: [{
id: 4,
src: grayPixel
},
{
id: 5,
src: blackPixel
},
{
id: 6,
src: greenPixel
},
],
searchTerm: '',
sortByTitle: '',
sortByDate: '',
sortByPopularity: '',
filterCategory: ''
};
this.filters = {
cat1: [],
cat2: [],
};
this.wb = this.props.dataWB;
this.element = React.createRef();
this.sizer = React.createRef();
this._handleSearchKeyup = this._handleSearchKeyup.bind(this);
this._handleSortChange = this._handleSortChange.bind(this);
this._handleCategory1Change = this._handleCategory1Change.bind(this);
this._handleCategory2Change = this._handleCategory2Change.bind(this);
this._getCurrentCat1Filters = this._getCurrentCat1Filters.bind(this);
this._getCurrentCat2Filters = this._getCurrentCat2Filters.bind(this);
}
/**
* Fake and API request for a set of images.
* #return {Promise<Object[]>} A promise which resolves with an array of objects.
*/
_fetchPhotos() {
return new Promise((resolve) => {
setTimeout(() => {
resolve([{
id: 4,
username: '#stickermule',
title:'puss',
date_created: '2003-09-01',
popularity: '233',
category1:'animal',
category2:'mammals',
name: 'Sticker Mule',
src: 'https://images.unsplash.com/photo-1484244233201-29892afe6a2c?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=800&h=600&fit=crop&s=14d236624576109b51e85bd5d7ebfbfc'
},
{
id: 5,
username: '#prostoroman',
date_created: '2003-09-02',
popularity: '232',
category1:'industry',
category2:'mammals',
title:'city',
name: 'Roman Logov',
src: 'https://images.unsplash.com/photo-1465414829459-d228b58caf6e?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=800&h=600&fit=crop&s=7a7080fc0699869b1921cb1e7047c5b3'
},
{
id: 6,
username: '#richienolan',
date_created: '2003-09-03',
popularity: '231',
title:'nature',
category1:'art',
category2:'insect',
name: 'Richard Nolan',
src: 'https://images.unsplash.com/photo-1478033394151-c931d5a4bdd6?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=800&h=600&fit=crop&s=3c74d594a86e26c5a319f4e17b36146e'
}
]);
}, 300);
});
}
_whenPhotosLoaded(photos) {
return Promise.all(photos.map(photo => new Promise((resolve) => {
const image = document.createElement('img');
image.src = photo.src;
if (image.naturalWidth > 0 || image.complete) {
resolve(photo);
} else {
image.onload = () => {
resolve(photo);
};
}
})));
}
_handleSortChange(evt) {
var value = evt.target.value.toLowerCase();
function sortByDate(element) {
return element.getAttribute('data-created');
}
function sortByPopularity(element) {
return element.getAttribute('data-popularity');
}
function sortByTitle(element) {
return element.getAttribute('data-title').toLowerCase();
}
let options;
if (value == 'date-created') {
options = {
reverse: true,
by: sortByDate,
};
} else if (value == 'title') {
options = {
by: sortByTitle,
};
} else if (value == 'popularity') {
options = {
reverse: true,
by: sortByPopularity,
};
} else if (value == 'default') {
this.shuffle.filter('all');
} else {
options = {};
}
this.shuffle.sort(options);
};
_getCurrentCat1Filters = function () {
return this.filters.cat1.filter(function (button) {
return button.classList.contains('active');
}).map(function (button) {
console.log('button value: '+button.getAttribute('data-value'))
return button.getAttribute('data-value');
});
};
_getCurrentCat2Filters = function () {
return this.filters.cat2.filter(function (button) {
return button.classList.contains('active');
}).map(function (button) {
console.log('button value: '+button.getAttribute('data-value'))
// console.log('button value: '+button.getAttribute('data-value'))
return button.getAttribute('data-value');
});
};
_handleCategory1Change = function (evt) {
var button = evt.currentTarget;
console.log(button)
// Treat these buttons like radio buttons where only 1 can be selected.
if (button.classList.contains('active')) {
button.classList.remove('active');
} else {
this.filters.cat1.forEach(function (btn) {
btn.classList.remove('active');
});
button.classList.add('active');
}
this.filters.cat1 = this._getCurrentCat1Filters();
console.log('current cat contains : '+this.filters.cat1);
this.filter();
};
/**
* A color button was clicked. Update filters and display.
* #param {Event} evt Click event object.
*/
_handleCategory2Change = function (evt) {
var button = evt.currentTarget;
// Treat these buttons like radio buttons where only 1 can be selected.
if (button.classList.contains('active')) {
button.classList.remove('active');
} else {
this.filters.cat2.forEach(function (btn) {
btn.classList.remove('active');
});
button.classList.add('active');
}
this.filters.cat2 = this._getCurrentCat2Filters();
console.log('current cat contains : '+this.filters.cat2);
this.filter();
};
filter = function () {
if (this.hasActiveFilters()) {
this.shuffle.filter(this.itemPassesFilters.bind(this));
} else {
this.shuffle.filter(Shuffle.ALL_ITEMS);
}
};
itemPassesFilters = function (element) {
var cat1 = this.filters.cat1;
var cat2 = this.filters.cat2;
var cat1 = element.getAttribute('data-category1');
var cat2 = element.getAttribute('data-category2');
// If there are active shape filters and this shape is not in that array.
if (cat1.length > 0 && !cat1.includes(cat1)) {
return false;
}
// If there are active color filters and this color is not in that array.
if (cat2.length > 0 && !cat2.includes(cat2)) {
return false;
}
return true;
};
/**
* If any of the arrays in the `filters` property have a length of more than zero,
* that means there is an active filter.
* #return {boolean}
*/
hasActiveFilters = function () {
return Object.keys(this.filters).some(function (key) {
return this.filters[key].length > 0;
}, this);
};
_handleSearchKeyup(event) {
this.setState({
searchTerm: event.target.value.toLowerCase()
}, () => {
this.shuffle.filter((element) => {
return element.dataset.name.toLowerCase().includes(this.state.searchTerm) || element.dataset.username.toLowerCase().includes(this.state.searchTerm);
})
})
}
componentDidMount() {
// The elements are in the DOM, initialize a shuffle instance.
this.shuffle = new Shuffle(this.element.current, {
itemSelector: '.js-item',
sizer: this.sizer.current,
});
// Kick off the network request and update the state once it returns.
this._fetchPhotos()
.then(this._whenPhotosLoaded.bind(this))
.then((photos) => {
this.setState({
photos
});
});
}
componentDidUpdate() {
// Notify shuffle to dump the elements it's currently holding and consider
// all elements matching the `itemSelector` as new.
this.shuffle.resetItems();
}
componentWillUnmount() {
// Dispose of shuffle when it will be removed from the DOM.
this.shuffle.destroy();
this.shuffle = null;
}
render() {
return (
<div>
<div id='searchBar'>
<input type="text" className='js-shuffle-search' onChange={ this._handleSearchKeyup } value={ this.state.searchTerm } />
</div>
<div id='gridActions'>
<h2>Filter By cat 1</h2>
<button onClick={ this._handleCategory1Change } value='all'>All</button>
<button onClick={ this._handleCategory1Change } value='art'>Art</button>
<button onClick={ this._handleCategory1Change } value='industry'>Industry</button>
<button onClick={ this._handleCategory1Change } value='animal'>Animal</button>
<h2>Filter By cat 2</h2>
<button onClick={ this._handleCategory2Change } value='all'>All</button>
<button onClick={ this._getCurrentCat1Filters } value='mammals'>Mammals</button>
<button onClick={ this._getCurrentCat2Filters } value='insects'>Insects</button>
<h2>Sort By</h2>
<button onClick={ this._handleSortChange } value='default'>Default</button>
<button onClick={ this._handleSortChange } value='date-created'>By Date</button>
<button onClick={ this._handleSortChange } value='title'>By Title</button>
<button onClick={ this._handleSortChange } value='popularity'>By Popularity</button>
</div>
<div ref={ this.element } id='grid' className="row my-shuffle-container shuffle"> {
this.state.photos.map(image =>
<PhotoItem { ...image } />)}
<div ref={ this.sizer } className="col-1#xs col-1#sm photo-grid__sizer"></div>
</div>
</div>
);
}
}
function PhotoItem({id, src, category1, category2, date_created, popularity, title, name, username }) {
return (
<div key={id}
className="col-lg-3 js-item"
data-name={name}
data-title={title}
data-date-created={date_created}
data-popularity={popularity}
data-category1={category1}
data-cetagory2={category2}
data-username={username}>
<img src={src} style={{width : "100%",height :"100%"}}/>
</div>
)
}
export default PhotoGrid;
The photogrid right now does nothing, just displays photos which I'm unable to search, filter and sort.
Only judging by the documentation, I haven't tried it yet, but should work.
The instance of Shuffle has a filter method, which takes a string, or an array of strings, to filter the elements by "groups", or a callback function to perform more complicated search. You should call this.shuffle.filter after updating the state of your component, i.e.:
_handleSearchKeyup(event){
this.setState({searchTerm : event.target.value}, () => {
this.shuffle.filter((element) => { /* use this.state.searchTerm to return matching elements */ } );
})
}
Edited after building a fiddle.
The callback function looks at data-name and data-username attributes to check if they contain the search string
_handleSearchKeyup(event){
this.setState({searchTerm : event.target.value.toLowerCase()}, () => {
this.shuffle.filter((element) => {
return (
element.dataset.name.toLowerCase().includes(this.state.searchTerm) ||
element.dataset.username.toLowerCase().includes(this.state.searchTerm)
);
})
})
}
For the above to work you also need to add these attributes to the DOM nodes, so update the PhotoItem component:
function PhotoItem({ id, src, name, username }) {
return (
<div key={id}
className="col-md-3 photo-item"
data-name={name}
data-username={username}>
<img src={src} style={{width : "100%",height :"100%"}}/>
</div>
)
}
In opposition to pawel's answer I think that this library operates on DOM. It makes this not react friendly.
Classic input handlers saves values within state using setState method. As an effect to state change react refreshes/updates the view (using render() method) in virtual DOM. After that react updates real DOM to be in sync with virtual one.
In this case lib manipulates on real DOM elements - calling render() (forced by setState()) will overwritte earlier changes made by Shuffle. To avoid that we should avoid using setState.
Simply save filter and sorting parameters directly within component instance (using this):
_handleSearchKeyup(event){
this.searchTerm = event.target.value;
this.shuffle.filter((element) => { /* use this.searchTerm to return matching elements */ } );
}
Initialize all the params (f.e. filterCategories, searchTerm, sortBy and sortOrder) in constructor and use them in one this.shuffle.filter() call (second parameter for sort object) on every parameter change. Prepare some helper to create combined filtering function (mix of filtering and searching), sorting is far easier.
setState can be used for clear all filters button - forced rerendering - remember to clear all parameters within handler.
UPDATE
For sorting order declare
this.reverse = true; // in constructor
this.orderBy = null;
handlers
_handleSortOrderChange = () => {
this.reverse = !this.reverse
// call common sorting function
// extracted from _handleSortChange
// this._commonSortingFunction()
}
_handleSortByChange = (evt) => {
this.orderBy = evt.target.value.toLowerCase();
// call common sorting function
// extracted from _handleSortChange
// this._commonSortingFunction()
}
_commonSortingFunction = () => {
// you can declare sorting functions in main/component scope
let options = { reverse: this.reverse }
const value = this.orderBy;
if (value == 'date-created') {
options.by = sortByDate // or this.sortByDate
} else if (value == 'title') {
options.by = sortByTitle
//...
//this.shuffle.sort(options);
You can also store ready options sorting object in component instance (this.options) updated by handlers. This value can be used by _commonSortingFunction() to call this.shuffle.sort but also by filtering functions (second parameter).
reversing button (no need to bind)
<button onClick={this._handleSortOrder}>Reverse order</button>
UPDATE 2
If you want to work with 'normal' react, setState you can move (encapsulate) all the filtering (searchBar, gridActions) into separate component.
State update will force rerendering only for 'tools', not affecting elements managed in real DOM by shuffle (parent not rerendered). This way you can avoid manual css manipulations ('active') by using conditional rendering (plus many more possibilities - list active filters separately, show order asc/desc, show reset only when sth changed etc.).
By passing this.shuffle as prop you can simply invoke search/filter/sort in parent.

Some problems with vue router reload and components updating

I'm actually working on a VueJs project where I meet some problems.
The 1st is that when I'm reloading page using parameters, I'm throwed back to the root page and I don"t understand why. I'm actually using the vue webpack template and lazy loading for my routes templates.
The 2nd problem is that I'm making a page of comments for my project who's are generated from a array of object. When the user has done posting his comment a new array is loaded from the database and put in the data of the parent as following :
parentComponent -> data -> array of objects -> each object = a component
So I used this.$set to put datas but it has not worked,I used a foreach loop and now I'm just replacing the array like this :
oldArray = new Array
But it doesn't work and my child components don't want to update even with forceUpdate().Maybe it's something very silly but I didn't found the answer.
If someone could have the answer that would be very nice to share it.
Here is my parent code:
<template>
<div class='comments'>
<div v-if="comments.length > 0" class="comments__block">
<comment-block v-for="comment in comments" :key="comment.Id" :comment.sync="comment" :updateHandler="loadComments"></comment-block>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'Comments',
data () {
return {
page: 1,
comments: [],
numberPages: 0,
resultEmpty: false
}
},
components: {
'comment-block': commBlock,
},
computed: {
methods: {
loadComments (nextPage = 1) {
if (isLogged) {
axios
.get(`API query if logged)
.then(response => {
if (Success) {
if (Comments) {
this.comments = Comments
this.resultEmpty = false
} else {
this.resultEmpty = true
}
}
})
.catch(e => {
console.log(e)
})
} else {
axios
.get(`url if not logged`)
.then(response => {
if (Success) {
if (Comments) {
this.comments = []
Comments.forEach(c => {
this.comments.push(c)
})
this.resultEmpty = false
} else {
this.resultEmpty = true
}
}
})
.catch(e => {
console.log(e)
})
}
}
},
mounted () {
this.loadComments()
},
watch: {
'$route' (to, from) {
this.loadComments()
}
},
}
</script>
And for the children
<template>
<div class='comment-block'>
<div class="comment-block__header">
<div class="comment-block__username">
{{userName}}
</div>
<div v-if="isLogged" class="comment-block__likes">
<input v-model="like" class="comment-block__radio" type="radio" name="likes" id="like" value="Y"/><label class="comment-block__radio-label comment-block__radio-label--like" for="like" v-bind:class="{'comment-block__radio-label--liked': like == 'Y'}" #click="toggle('Y')">{{nbLikes}}<v-icon large color="white">keyboard_arrow_up</v-icon></label>
<input v-model="like" class="comment-block__radio" type="radio" name="likes" id="dislike" value="N"/><label class="comment-block__radio-label comment-block__radio-label--dislike" for="dislike" v-bind:class="{'comment-block__radio-label--liked': like == 'N'}" #click="toggle('N')">{{nbDislikes}}<v-icon large color="white">keyboard_arrow_down</v-icon></label>
<input v-model="like" class="comment-block__radio" type="radio" name="likes" id="disapproved" value="D"/><label class="comment-block__radio-label comment-block__radio-label--disapproved" for="disapproved" v-bind:class="{'comment-block__radio-label--liked': like == 'D'}" #click="toggle('D')">{{nbDisapproved}}<v-icon large color="white">report</v-icon></label>
</div>
<div v-else class="comment-block__likes">
<button class="comment-block__button" #click="displayMessage = ! displayMessage" id="like"><p class="comment-block__radio-p comment-block__radio-p--like" for="like">{{comment.NbLikes}}<v-icon large color="white">keyboard_arrow_up</v-icon></p></button>
<button class="comment-block__button" #click="displayMessage = ! displayMessage" id="dislike"><p class="comment-block__radio-p comment-block__radio-p--dislike" for="dislike">{{comment.NbDislikes}}<v-icon large color="white">keyboard_arrow_down</v-icon></p></button>
<button class="comment-block__button" #click="displayMessage = ! displayMessage" id="disapproved"><p class="comment-block__radio-p comment-block__radio-p--disapproved" for="disapproved">{{comment.NbDisapproved}}<v-icon large color="white">report</v-icon></p></button>
</div>
</div>
<div class="comment-block__body">
<div cols="12">
<div class="comment-block__commentary">
{{commentary }}
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'Comments',
data () {
return {
id: this.comment.idComment,
userName: this.comment.UserName,
commentary: this.comment.Commentary,
nbLikes: this.comment.NbLikes,
nbDislikes: this.comment.NbDislikes,
nbDisapproved: this.comment.NbDisapproved,
displayedLikes: this.nbLikes,
displayedDislikes: this.nbDislikes,
displayedDisapproved: this.nbDisapproved,
lastLike: this.comment.LastLike,
like: this.comment.LastLike,
displayMessage: false
}
},
props: {
comment: {type: Object, required: true},
updateHandler: {type: Function, required: true}
},
computed: {
isLogged () {
if (this.$store.state.jwt.token) {
return true
}
return false
}
},
methods: {
submitLike () {
var payload = {
IdComment: this.id,
Like: this.like
}
var header = {
headers: {
Authorization: this.$store.state.jwt.token
}
}
axios
.post('someUrl', payload, header)
.then(resp => {
if (Success) {
this.updateHandler()
}
})
.catch(e => {
console.log(e)
})
},
toggle (value) {
if (value === this.like) {
this.like = ''
}
}
},
watch: {
like (newVal, oldVal) {
if (newVal !== oldVal) {
this.submitLike()
switch (oldVal) {
case 'Y' : this.nbLikes--
break
case 'N' : this.nbDislikes--
break
case 'D' : this.nbDisapproved--
break
}
switch (newVal) {
case 'Y' : this.nbLikes++
break
case 'N' : this.nbDislikes++
break
case 'D' : this.nbDisapproved++
break
}
}
if (oldVal === newVal) {
this.like = ''
}
},
comment (newVal) {
this.$forceUpdate()
}
}
}
</script>
PS: Sorry for my bad english if it is.

Resources