So I've got this page which puts out all of the profiles on my database and what I want to do is when someone clicks on an individual profile they go in to a profile page which displays the rest of the data for the corresponding id they have just clicked on but I don't seem to be able to get it to work.
Below is my js file to get individual profiles.
import { projectFirestore } from "../Firebase/Config";
import { ref } from "vue"
const getPBasic = (id) => {
const PBasic = ref(null)
const error = ref(null)
const load = async () => {
try{
let res = await projectFirestore.collection('Basic').doc(id).get()
PBasic.value = {...res.data(), id: res.id}
console.log(PBasic.value)
}
catch (err){
error.value = err.message
console.log(error.value)
}
}
return { PBasic, error, load}
}
export default getPBasic
And this is what the vue page which I want the data to appear on after they have clicked on a profile from the previous page.
<script>
import getPBasic from "../Composables/getPBasic";
const {PBasic, error, load} = getPBasic(route.params.id);
load();
export default {
name: "Slider",
data() {
return {
images: [
"/src/assets/sample-1.jpg",
"/src/assets/sample-2.jpg",
"/src/assets/sample-3.jpg",
"/src/assets/sample-4.jpg"
],
currentIndex: 0
};
},
methods: {
next: function() {
this.currentIndex += 1;
},
prev: function() {
this.currentIndex -= 1;
}
},
computed: {
currentImg: function() {
return this.images[Math.abs(this.currentIndex) % this.images.length];
}
}
};
</script>
<template>
<div v-if="error">{{ error }}</div>
<div v-if="PBasic" class="PBasic">
<br><br>
<p>{{ PBasic.Name }} </p>
<p>{{ PBaic.Age }} </p>
</div>
<div v-else>
<spinner/>
</div>
Thats what I've got so far I just shortened it for here so it didn't go on and on for too long, if anyone has any ideas I would greatly appreciate it, Thanks.
You can run your getPBasic function in page load to fetch the data you need.
One possible solution is, Run your getPBasic function in a lifecycle hook that fire on page render like mounted() hook or onMounted() in vue3 script setup.
And as we can see your getPBasic function is a synchronous function so use await to get the return value properly.
One possible code might look like this,
<script>
import getPBasic from "../Composables/getPBasic";
const {PBasic, error, load} = getPBasic(route.params.id);
export default {
name: "Slider",
mounted(){
let {PBasic,error,load} = await getPBasic();
this.PBasic = PBasic;
this.error = error;
this.load = load;
},
data() {
return {
PBasic:{},
error: null,
load: false,
images: [
"/src/assets/sample-1.jpg",
"/src/assets/sample-2.jpg",
"/src/assets/sample-3.jpg",
"/src/assets/sample-4.jpg"
],
currentIndex: 0
};
},
methods: {
next: function() {
this.currentIndex += 1;
},
prev: function() {
this.currentIndex -= 1;
}
},
computed: {
currentImg: function() {
return this.images[Math.abs(this.currentIndex) % this.images.length];
}
}
};
</script>
<template>
<div v-if="error">{{ error }}</div>
<div v-if="PBasic" class="PBasic">
<br><br>
<p>{{ PBasic.Name }} </p>
<p>{{ PBaic.Age }} </p>
</div>
<div v-else>
<spinner/>
</div>
Related
I'm new to Vue, javascript & Web development. Using Vue, I tried to recreate the moviedb app(from Brad's 50 JS Projects in 50 Days course).
I'm getting stuck and can't get the data out of a scope.
I've successfully retrieved data & destructured it.
But how can I get those values out of that scope (out of setMovies function) and use it in the Vue file (html template)?
Here's my code:
I've made the api_key private
<h1>MovieDesk</h1>
<div class="hero">
<!-- Search -->
<div class="search">
<form #submit.prevent="handleSearch">
<input type="text" placeholder="Search here..." />
<button #click="handleSearch">Search</button>
</form>
</div>
</div>
<!-- Movies -->
<div v-if="searchOn">
<SearchedMovies />
</div>
<div v-else>
<MovieList/>
</div>
</template>
<script>
// imports-------------------
import { ref } from "#vue/reactivity";
import MovieList from "../components/MovieList.vue";
import SearchedMovies from "../components/SearchedMovies.vue";
import { onMounted } from "#vue/runtime-core";
export default {
components: { MovieList, SearchedMovies },
setup() {
const searchOn = ref(false);
const api_url = ref(
"https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=api_key&page=1"
);
const movies = ref([])
// getting the data ------------------------------
onMounted(() => {
fetch(api_url.value)
.then((res) => res.json())
.then((data) => {
console.log(data);
setMovies(data.results);
});
});
function setMovies(movies) {
movies.forEach((movie) => {
const { title, poster_path, vote_average, overview } = movie;
});
}
return { searchOn, setMovies };
},
};
</script> ```
In your setMovies function, You can set the response in the movies variable and then return that variable from your setup.
function setMovies(apiResponse) {
movies.value = apiResponse
}
return { movies };
Live Demo :
const { ref, onMounted } = Vue;
const App = {
setup() {
const movies = ref([])
onMounted(() => {
const apiResponse = [{
id: 1,
name: 'Movie 1'
}, {
id: 2,
name: 'Movie 2'
}, {
id: 3,
name: 'Movie 3'
}];
setMovies(apiResponse);
})
function setMovies(res) {
movies.value = res;
}
return {
movies
};
}
};
Vue.createApp(App).mount("#app");
<script src="https://unpkg.com/vue#next"></script>
<div id="app">
<pre>{{ movies }}</pre>
</div>
Add 'movies' to the return statement at the bottom of your code, then you should be able to render it.
I have this code here,
it basically fetch from firestore in the setup(), then display the information with the Categoria component. It also should update the Categoria components when the <span> is pressed. However, something don't work. My snippet successfully update the database but does not reload the component... any ideas?
<template>
<div class="header">
<span class="mdi mdi-home icona" />
<h1>Modifica menĂ¹</h1>
</div>
<Categoria
v-model="categorie"
v-for="categoria in categorie"
v-bind:key="categoria"
:Nome="categoria.nome"
/>
<div class="contenitore_aggiungi">
<span #click="crea()" class="mdi mdi-plus aggiungi" />
</div>
</template>
<script>
import Categoria from "#/components/edit/Categoria-edit.vue";
import { useRoute } from "vue-router";
import { creaCategoria, ottieniCategorie } from "#/firebase";
export default {
name: "Modifica",
components: { Categoria },
async setup() {
const route = useRoute();
let idRistorante = route.params.id;
let categorie = await ottieniCategorie(idRistorante);
console.log(categorie);
return { idRistorante, categorie };
},
methods: {
crea() {
let nuovaCategoria = "Nuova categoria";
creaCategoria(this.idRistorante, nuovaCategoria);
this.categorie.push({ nome: nuovaCategoria });
console.log(this.categorie);
},
},
};
</script>
Thanks for your answers!
You need to declare categorie as a reactive property. Also you can write methods in setup() itself instead of methods:
import { ref } from 'vue'
export default {
setup() {
const route = useRoute();
let idRistorante = route.params.id;
const categorie = ref({}) // <-- add default value of properties
const getData = async () => {
const data = await ottieniCategorie(idRistorante);
categorie.value = data
}
getData() // or void getData()
const crea = () => {
let nuovaCategoria = "Nuova categoria";
categorie.value.push({ nome: nuovaCategoria });
console.log(categorie.value);
},
return { idRistorante, categorie, crea };
}
}
Make sure the default value of categorie is set in ref(). If it's an array set it to ref([]).
I have this app to make I am dealing with the CRUD and only the "U(pdate)" is posing problems
I have :
<template>
<EditQuestion
v-show="showEditQ"
:questToEdit="questToEdit"
/>
</template>
<script>
import EditQuestion from '../components/EditQuestion'
export default {
name: 'Home',
components: {
EditQuestion
},
data() {
return {
questToEdit:{},
}
},
methods:{
async getQuestion(_id){
const questToEdit = await this.fetchQuestion(_id)
return questToEdit
},
async fetchQuestion(_id) {
const res = await fetch(`http://localhost:3000/api/questions/${_id}`)
const data = await res.json()
return data
},
}
</script>
and in the component side I have
<template>
<div>
<p>test</p>
<p>{{questToEdit.question}}</p>
<li
v-for="(proposition,index) in questToEdit.propositions"
:key="index"
>{{proposition.props}}
</li>
</div>
</template>
<script>
export default {
name: 'EditQuestion',
props: {
questToEdit:Object
},
data(){
return {
}
},
}
</script>
I feel like I can't access questToEdit in the component (it's doing nothing ) or its never called on home ? thx for your time in advence.
I have 10 records in the database that i want to load dynamically.
This app loads data from database using react redux. The Load more button also works.
Here is my problem,
Each time I click on Load More button, it will load more 2 records from the
database which will replace already displayed records.
I think that my problem lies is the Loadmore() functions
1.)how do I append the new records to already displayed records each time the loadmore button is click.
2.)Am also checking to display a message No more records once data is finished but cannot get it to work properly as the message
got displayed each time loadmore button is clicked
import React from "react";
import { Link } from "react-router-dom";
import { connect } from "react-redux";
import { userActions } from "../_actions";
class HomePage extends React.Component {
constructor(props) {
super(props);
this.state = {
row_pass: 0
};
this.row = 0;
this.rowperpage = 2;
this.buttonText = "Load More";
this.loadMore = this.loadMore.bind(this);
}
componentDidMount() {
this.props.dispatch(userActions.getAll(this.row));
}
loadMore() {
this.row += this.rowperpage;
alert("loading" + this.row);
this.props.dispatch(userActions.getAll(this.row));
this.buttonText = "Load More";
}
get finished() {
if (this.row >= this.rowperpage) {
return <li key={"done"}>No More Message to Load.</li>;
}
return null;
}
render() {
const { user, users } = this.props;
return (
<div
style={{ background: "red" }}
className="well col-md-6 col-md-offset-3"
>
<h1>
Hi{user.message}! {user.token}
</h1>
<p>You're logged in with React!!</p>
<h3>All registered users:</h3>
{users.loading && <em>Loading users...</em>}
{users.error && (
<span className="text-danger">ERROR: {users.error}</span>
)}
{users.items && (
<ul>
{users.items.map((user, index) => (
<li key={user.id}>
{user.firstName + " " + user.lastName}:
<span>
{" "}
- <a>home</a>
</span>
</li>
))}
{this.finished}
</ul>
)}
<p>
<a className="pic" onClick={this.loadMore}>
{this.buttonText}
</a>
<input
type="text"
className="form-control"
name="this.row"
id="this.row"
value={this.row}
onChange={this.handleChange}
/>
</p>
</div>
);
}
}
function mapStateToProps(state) {
const { users, authentication } = state;
const { user } = authentication;
return {
user,
users
};
}
const connectedHomePage = connect(mapStateToProps)(HomePage);
export { connectedHomePage as HomePage };
here is user.action.js
function getAll(row) {
return dispatch => {
dispatch(request(row));
userService.getAll(row)
.then(
users => dispatch(success(users)),
error => dispatch(failure(error.toString()))
);
};
user.reducer.js code
import { userConstants } from '../_constants';
export function users(state = {}, action) {
switch (action.type) {
case userConstants.GETALL_REQUEST:
return {
loading: true
};
case userConstants.GETALL_SUCCESS:
return {
items: action.users
};
case userConstants.GETALL_FAILURE:
return {
error: action.error
};
/*
case userConstants.DELETE_FAILURE:
// remove 'deleting:true' property and add 'deleteError:[error]' property to user
return {
...state,
items: state.items.map(user => {
if (user.id === action.id) {
// make copy of user without 'deleting:true' property
const { deleting, ...userCopy } = user;
// return copy of user with 'deleteError:[error]' property
return { ...userCopy, deleteError: action.error };
}
return user;
})
};
*/
default:
return state
}
}
If I understand you right, this is what you need to do. Firstly, don't replace the whole items with action.users. Concat it with the old items state instead:
case userConstants.GETALL_REQUEST:
return {
...state,
loading: true
};
case userConstants.GETALL_SUCCESS:
return {
loading: false,
error: null,
items: [ ...(state.items || []), ...action.users ]
};
From here, to properly show "No More Message to Load", you need to fix this.finished condition:
get finished() {
if (this.row >= 10) {
return (<li key={'done'}>No More Message to Load.</li>);
}
return null;
}
Where 10 is the total count of users, not this.rowperpage. Ideally, this value should come from API response.
Hope this helps.
UPDATE
To display proper buttonText I would suggest to replace your current implementation with something like:
get buttonText() {
if (this.props.users.loading) return 'Loading...';
if (this.props.users.error) return 'Error has occurred :(';
return 'Load more'
}
I am very new to State Management. I'm currently building a product list with infinite loading features. These are my codes:
This is my component:
<template>
<div class="">
<section class="space--sm">
<div class="container">
<div class="row">
<div class="col-sm-12">
<div v-for="(data, index) in products" v-bind:key="data.id" class="item col-sm-4 col-xs-12" :id="'data-id-'+data.id" >
<a href="#" v-on:click.prevent="selectProduct(data)" >
<h4>{{data.name}}</h4>
</a>
</div>
<infinite-loading force-use-infinite-wrapper="true" #infinite="infiniteHandler" ref="infiniteLoading">
<span slot="no-results">
No results
</span>
<span slot="no-more">
There are no more results
</span>
</infinite-loading>
</div>
</div>
</div>
</section>
</div>
</template>
<script>
import InfiniteLoading from 'vue-infinite-loading';
import { mapState, mapActions } from 'vuex';
export default {
name: 'List',
computed: mapState({
products: state => state.list.products
}),
methods: {
...mapActions('list', [
'selectProduct'
]),
infiniteHandler($state) {
setTimeout(() => {
this.$store.dispatch('products/fetch')
console.log(this.products.length);
//returns 0 but vue debugger says products state got datas
if (this.products.length) {
$state.loaded();
if(this.products.length < 15){
$state.complete();
}
} else {
$state.complete();
}
}, 1000);
},
},
components: {
InfiniteLoading
}
}
</script>
This is my store:
import axios from 'axios';
// initial state
const state = {
products: [],
selected_product: [],
page: 1
}
// getters
const getters = {}
// mutations
const mutations = {
FETCH(state, products) {
state.products = state.products.concat(products);
},
selectProduct (state, { product }) {
state.selected_product = product;
},
updatePage (state) {
state.page += 1;
},
}
// actions
const actions = {
fetch({ commit }) {
return axios.get('/api/get/products', {
params: {
page: state.page
}
})
.then((response) => {
commit('updatePage')
commit('FETCH', response.data.data)
})
.catch();
},
selectProduct ({ state, commit }, product) {
commit('selectProduct', { product })
}
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
In the infiniteHandler method, I did this :
this.$store.dispatch('products/fetch')
Afer mutation products state should have loaded data inside (checked using vue debugger) but when I do this after dispatching :-
console.log(this.products.length);
I get 0 which also means no datas present inside the state. Am I doing anything wrong? Also is this the best practice of using Vuex? Thank you
If an asynchronous action returns a promise (as yours does), it is returned by the call to dispatch so your consumer can wait for the action to complete.
For example
async infiniteHandler($state) { // note, this is an "async" function
setTimeout(() => {
await this.$store.dispatch('products/fetch') // note the "await"
console.log(this.products.length);
if (this.products.length) {
$state.loaded();
if(this.products.length < 15){
$state.complete();
}
} else {
$state.complete();
}
}, 1000);
},
One more thing, your catch() at the end of the fetch action won't do you any favours. I'd advise removing it so your consumers can be made aware of any errors. That, or handle the error in your action but still pass the promise rejection on to your component, eg
.catch(res => {
// handle error, maybe with your state
throw res // make sure to keep rejecting the promise
})
and in your component
try {
await this.$store.dispatch('products/fetch')
// etc
} catch (e) {
// do something else
}
Just noticed another thing... you should use the passed in context state in your actions, not some global state object, ie
fetch({ state, commit }) {
this.$store.dispatch('products/fetch') is asynchronous. console.log(this.products.length); is called before action is finished.
You should use watch in this case.
watch: {
products: function (newVal) {
if (newVal.length) {
$state.loaded();
if(newVal.length < 15){
$state.complete();
}
} else {
$state.complete();
}
}
}
async infiniteHandler($state) {
setTimeout(() => {
await this.$store.dispatch('products/fetch')
console.log(this.products.length);
if (this.products.length) {
setTimeout(() => {
$state.loaded();
}, 1000);
if(this.products.length < 15){
$state.complete();
}
} else {
$state.complete();
}
}, 1000);
},