React.js show detailed info of a list - javascript

How can I achieve to show detailed page from a search function??
for example I have the data from the API call to be rendered in a list, but then I would like to hit the "more" link and show a detailed page of the selected list
example link: https://master.d1y6j5rvhgf8yj.amplifyapp.com/
with the initial data it works, but after I search it still direct to initial data
const findArticlesQuery = (query) => {
axios.get(url)
.then(res => {
[res.data].map((val) =>(
setArticleData(val.articles)
))
}).catch(error => {
console.log(error.response)
});
}
useEffect(
() =>{
findArticlesQuery();
} ,[]
)
and then I have the article List but not sure how to create a detailed page,
<ArticleListItem articlesData={articleData}/>
const articleDataItem = articlesData.map((value, idx)=> (
<li key={idx} className="collection-item">
<span>{value.title}</span>
<a href={`/article/${idx}`}>More</a> <--- I want to use this button to show detailed info
</li>
))
I can achieve this with router and matching params but only with the initial data,
If I use the search function and the articlesData is updated, the link would still get the initial data and not the searched
I also must say that I have the useEffect on the App component which reset the data when I go to another page

In detail page, you shouldn't use index of list for show detail.
You should use a unique attribute of the data to display details.
example: data: [{code: 'abc', title: '...'}, {code: '', title: ''}...]
const articleDataItem = articlesData.map((value, idx)=> (
<li key={value.code} className="collection-item">
<span>{value.title}</span>
<a href={`/article/${value.code}`}>More</a> <--- I want to use this button to show detailed info
</li>
))
In detail page, Search by received code value

Related

Vuejs How to have many <a> tags and hashtags within <router-link> or #click function

enter image description here
i want to have a post card like twitter post card, if clicked on any part of the post card goes to post page, but when clicked on hashtags or links goes to the hashtag or link
example below
1.
<div #click="gotoPostPage" class="text-weight-light text-justify">
{{ feed.content }}
check google if you need more information then follow #benny(a tag) or follow these hashtags
#google #finda, #checks, now when i click within this post take me to post page
</div>
now this hits the gotoPostPage function even if link or a tag is clicked
using this also
2.
<router-link :to="`/post/${ feed.id }`">
{{ feed.content }}
check google if you need more information then follow #benny(a tag) or follow
these hashtags
#google #finda, #checks, now when i click within this post take me to post page
</router-link>
goes to even when check google is clicked
Please how can i resolve this, your help is highly appreciated,
Thanks
Add #click.stop to your links. E.g:
<a href="https://google.com" #click.stop>check google</a>
This will stop propagation of click events to the parent DOM elements.
Note: #click.stop is shorthand for #click="e => e.stopPropagation()".
Docs here.
Update, based on data shown in comment:
I would avoid storing HTML id database. That's a really bad pattern. You're supposed to detach the data from the UI layer. You have to allow the UI layer to change, based on device and medium. I'd try to store that data as strings holding a hashtag name and, where the name is different than the hastag, as on object, containing both.
Something like this:
const { createApp, reactive, computed, onMounted, toRefs } = Vue;
createApp({
setup() {
const state = reactive({
items: [],
links: computed(() => state.items.map(
item => ({
name: item.name || `#${item}`,
url: item.url || `/hashtag/${item}`
})
))
});
onMounted(async() => {
/* replace this promise with an axios call to server which returns
an array, similar to the one i'm passing here:
(see axios call example below)
*/
const data = await new Promise(resolve => {
setTimeout(() => {
resolve([
'one',
'two',
'three',
'печаль',
'грусть',
{
name: '#mens',
url: '/hashtag/fıstıklıbaklava'
},
'чайная',
'джаз'
]);
});
})
// example axios call:
/*
const data = await axios
.get('http://endpoint-returning-data')
.then(({data}) => data);
*/
state.items = data;
console.log('See difference betwen items (received from server) \r\n\and links (same data, mapped for template):\r\n', {...state}, '\r\nMakes sense?');
})
return {
...toRefs(state),
log: () => console.log('You clicked on app!')
}
}
}).mount('#app')
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<div id="app">
<div v-for="link in links" :key="link.url" #click="log">
<a :href="link.url"
v-text="link.name"
#click.stop></a>
</div>
</div>
As you can see, it produces the HTML from the data. The template also applies the stopPropagation(), in the v-for.

Commit initial array to store when leaving page, but if pressing back button loads last commited array

I have an array of articles that can be filtered based on their category. If you click the filter for "ideas" it will filter the array into a new array, let's say "ideasFiltered" and then i commit it to my store "filteredArticles". If i leave the page and press back it is saving that info. Yay! Great! BUT, let's say i go to a new page in my website and then i click my news page again, it has still saved that last commit "ideasFiltered". Is there a way to only save that data if you are pressing "back" in the browser or pressing "back to articles" out of one of my articles (i have a button that let's you go back to the list of articles on each article page), but if you click to a different link in my site it will reset to my full article list?
here is my store:
const store = new Vuex.Store({
state: {
filteredArticles: this.articles
},
mutations: {
setFilteredList (state, value) {
state.filteredArticles = value
}
},
plugins: [
createPersistedState()
],
})
my computed:
computed: {
filteredArticles () {
return store.state.filteredArticles
}
}
so if you click one of my filtered is runs a script, here is an example of the ideas script
ideas: function ideas() {
this.$store.commit('setFilteredList', articles) **// resetting to the full list before filtering**
var ideasFiltered = this.filteredArticles.filter(function(post) {
return post.category === 'Ideas';
});
this.filteredCategory = 'ideas'; **// this is used to add the category to my url**
this.$store.commit('setFilteredList', ideasFiltered) **// committing to the store**
}
here is my html, not sure if it is really necessary though
<div class="news-article-list">
<ul>
<li v-for="(article, index) in filteredArticles" :key="index">
<a :href="article.url">
<img :src="article.featureImg" v-if="article.featureImg" alt="Article Feature Image" />
<h2 class="news-title">{{ article.title }}</h2>
<p class="news-date">{{ article.date }}</p>
<p class="short-desc">{{ article.shortDesc }}...</p>
</a>
<router-link class="read-more" :to="article.url">Read More</router-link>
</li>
</ul>
</div>
Let me know if i need to explain any further. I need it to commit ALL articles if going to a different page on my site and clicking news to get back to the news page, but saving the FILTERED article list if you go to an article and click back.
thanks in advance!
I wouldn't worry about detecting the back button. What you want to know where from did the user come and where to the user wants to go. to and from are your routes. ideasFiltered is changed permanently until you clear it manually. To handle clearing it yourself, create an action in vue-router that resets the data in ideasFiltered and then dispatch that action depending on what route the user is on. You can add a watch in your Vue component to watch the global vue-router $router. In Vue.js 3 its like this:
import {useRoute} from 'vue-router';
import {useStore} from 'vuex';
....
setup() {
const $route = useRoute();
const $store = useStore();
watch(() => $route.name, async () => {
// If the user is not on News or Articles route, then tell store to reset filtered list
if ($route.name !=== "News" || $route.name !=== "Articles") {
$store.dispatch('clearFilteredList', null) // You need to create this action in your store
},
{
deep: true,
immediate: true
}
)
}
The mutation and action in $store to be committed would be something like:
mutations: {
setFilteredList (state, value) { // value passed should be null
state.filteredArticles = value
}
actions: {
clearFilteredList({commit}, payload) { // This is null from Vue component remember!
commit('setFilteredList', payload) // sets it to null
}
I can't see your full store code so the above answer is a best guess but should give you an idea how to go about it. You don't have to use null, you could use make it an empty array or object e.g. []

Filtering items using a condition (vue.js)

This is now solved
The problem
I am trying to make a filter that does this.
Gets 'name' from session storage and matches it with 'name' on product.
I'm storing all 'logged in' user data on 'sessionStorage' for practice.
I'm storing all the 'products' in 'localStorage'
I'm just doing this as a small project to understand and practice vue & js.
Basically if the product has been created by user x, upon login, user x should only be able to see the products he listed and not the rest of the products.
This is what I can do
I can get the user info from sessionStorage and when creating a product, the user 'name' is passed on along with the product details.
I can also retrieve all the products from localStorage.
I don't know how to come up with a filter using vue.js that does this.
Some code sample would really help me understand whats going on.
Additionally, I want to display the results in html.
Thanks in advance.
},
computed: {
filteredProducts() {
this.products.filter(x => x.name === this.loggedUser[0].user);
}
},
});```
[1]: https://i.stack.imgur.com/W7PMf.png
computed: {
filteredProducts() {
return this.products.filter(x => x.name === this.loggedUser[0].text); // text or user, whichever field is for username
},
},
After that show the list in html use v-for.
<p v-if="filteredProducts.length === 0">No products found</p>
<li v-for="(product, key) in filteredProducts" :key="key">Product: {{ product.description }}, Price {{ product.price }} <button #click="deleteProduct(key)">Delete</button></li>
add delete method
methods: {
// ...
deleteProduct(index) {
this.products.splice(index, 1);
},
}
I think this may work from you (given what you have provided above)
computed: {
filteredProducts () {
return this.products.filter(function (item) {
return (item.name === this.loggedUser[0].name)
}
}
}
Basically, we are filtering the products based on the name that is tagged on that list with the loggedUser name.
Hope this helps.

Vue with VCalendar component: Keep data in sync with another Vue instance

I'm programming a page that displays a list of meetings in a table. It's also possible to edit and delete meetings. Now I'd like to offer an alternative view using VCalendar.
Data is received from the server on page load and stored in a JS variable. Both the Vue instance containing the table and the VCalendar component share this data. If I edit a table cell, the changes are reflected in the component. But when I delete a date in the table view, it remains in the calendar.
This is the relevant HTML (edit: Added some attributes to the td):
<calendar-component></calendar-component>
<table id='meetings-table'>
<tr v-for='meeting in meetings' :key='date.id'>
<td contenteditable #blur='handleInput($event,meeting,"name")>
#{{ meeting.name }}
</td>
<td>
<input type='checkbox' v-model='selected'
:value='meeting.id'>
</td>
</tr>
</table>
<div>
<button v-if='selected.length' #click='deleteMeetings'>
Delete selected rows
</button>
</div>
My JS (edit: Added handleInput method):
let table = new Vue({
el:'#meetings-table',
data: {
selected: [],
meetings: window.meetings,
},
methods: {
/**
* Deletes selected meetings.
*/
deleteMeetings: function () {
let requests = [];
// Make a single request and store it
for (let id of this.selected) {
requests.push(axios.delete('/termine/' + id)
.then(response => {
// Remove meetings
this.meetings = this.meetings.filter(t => t.id != id);
// Remove id from list of selected meetings
this.selected = this.selected.filter(elem => elem != id);
}));
}
const axiosArray = axios.all(requests);
},
/**
* Handles edits in table cells.
*/
handleInput: function($event, meeting, field) {
const newValue = $event.target.textContent;
// Update value in $data
meeting[field] = newValue;
// AJAX request follows, but is not necessary for this example to work
}
}
});
The relevant parts of the component:
<template>
<v-calendar :attributes='attributes'>
<div
slot='meeting-row'
slot-scope='{ customData }'>
<!-- Popover content omitted -->
</div>
</v-calendar>
</template>
<script>
let meetings = window.meetings;
export default {
data() {
return {
incId: meetings.length,
editId: 0,
meetings,
};
},
computed: {
attributes() {
return [
// Today attribute
{
// ...
},
// Meeting attributes
...this.meetings.map(meeting => ({
key: meeting.id,
dates: new Date('2018,11,31'),// moment(meeting.slot.date, 'DD.MM.YY').format('YYYY, MM, DD'), //meeting.dates,
customData: meeting,
order: meeting.id,
dot: {
backgroundColor: '#ff8080',
},
popover: {
// Matches slot from above
slot: 'meeting-row',
}
}))
];
}
}
};
</script>
This is what happens:
I load the page containing only a single meeting. The meeting is
shown both in the table and the calendar component. Vue devtools show
it in both meetings arrays (in the component as well as in the other
Vue instance). Using the console, I can also see it in
window.meetings.
After clicking the delete button (triggering the deleteMeetings method in my JS), the meeting is gone from the table, but remains in
the calendar, in the component's meetings array and in
window.meetings.
What do I have to change to keep the meetings arrays in sync even when deleting a meeting in the table? Note that I haven't yet implemented any methods for the calendar component.
Calendar, and table components should share a single state: currently selected meetings. From what I understand, right now you have that state in 2 separate places: table Vue instance, and a calendar-component, which is a child of some other Vue instance.
It may look like you're sharing the state already (with window.meetings), but it's not the case: you only initialize the same set of meetings when the components are created. And then changes in one component are not reflected in another component.
What you can try to do is to have meetings stored in the 'main' Vue app on your page, pass them as props to table and calendar components, and then trigger events from table and calendar components, when meetings array is modified. You should also define the event hanlders in the 'main' Vue app, and listen on components. A rough sketch of the solution:
<div id="app">
<table-component
:meetings="meetings"
#meetingUpdated="handleMeetingUpdate"
#meetingDeleted="handleMeetingDeletion"
></table-component>
<calendar-component
:meetings="meetings"
#meetingUpdate="handleMeetingUpdate"
#meetingDeleted="handleMeetingDeletion"
></calendar-component>
</div>
let app = new Vue({
el:'#app',
data: {
meetings: []
},
methods: {
handleMeetingUpdate(event) {
//
},
handleMeetingDeletion(event) {
//
},
}
//
});
I hope the above is enough to point you in the right direction. If not, please let me know, and I'll do my best to help you with this further.

How should I take a multidimensional array in vuejs?

This problem has made me sleep well in two days.
This is my code with vuejs & axios & nuxt:
<template>
<ul>
<li v-for="item in data.apilist.interfaces" :key="item" v-if="data.apilist.interfaces">
{{ item.name }}
</li>
</ul>
</template>
<script>
import axios from 'axios'
const util = require('util')
export default {
data: () => ({
title: ''
}),
async asyncData ({ params, error }) {
let res = await axios.get(
`https://bird.ioliu.cn/v1?url=https://api.steampowered.com/ISteamWebAPIUtil/GetSupportedAPIList/v1/`
)
return { data: util.inspect(res, { showHidden: true, depth: null }) }
},
head () {
return {
title: this.title
}
}
}
</script>
Json data : https://api.steampowered.com/ISteamWebAPIUtil/GetSupportedAPIList/v1/
No matter how I do it, I can't get the value of apilist->interfaces->name.
The above example code system prompts me Cannot read property'interfaces' of undefined , which part is the problem?
===================update
I have installed chrome vue dev, but it seems to work on nuxt applications. Now I try to print the entire data data.
The data generated when I typed the corresponding connection directly in the browser is as follows:
enter image description here
But strange things happen, and when I jump to this link from somewhere else in the application, the data data is like this:
enter image description here
I tried v-for="item in data.apilist" {{item.interfaces.name}} or v-if="data.apilist" or v-if="data" and he no longer complains but no data is generated.
This is the result of the powershell output:
enter image description here && enter image description here
Cannot read property'interfaces' of undefined
Simply means that you are trying to access to the property "interfaces" on a undefined reference : somethingThatDoesntExist.interfaces
Here is the mess :
<li v-for="item in data.apilist.interfaces" :key="item" v-if="data.apilist.interfaces">
{{ data.interfaces }}
</li>
You are iterating on data.apilist.interfaces and binding just beneath :
data.interfaces
But you need to bind
item.interfaces
Since you are using v-for="item in ..." and not v-for="data in ...".
Code review is important before asking.

Categories

Resources