Vue: Update data from component to component - vue.js

i have a master layout named Main.vue and a view named Dashboard.vue, this is Main.vue
<template>
<div class="app">
<div class="loading" v-if="loading">
<square-loader :loading="true" color="#26A65B" size="50px"></square-loader>
</div>
<div class="container-fluid">
<router-view></router-view><!--Dashboard.vue will be rendered here-->
</div>
</template>
<script>
import SquareLoader from 'vue-spinner/src/SquareLoader.vue'
export default {
name: 'full',
components: {
SquareLoader
},
data () {
return {
loading: true,
}
}
}
</script>
Whenever the site start, SquareLoader will be shown.
At Dashboard.vue i have some requests to get data.
Is there any way to update loading = false ( loading is belong to Main.vue ) from Dashboard.vue?

Related

VUE3 use different v-for for the same component

I have a JobComponent.vue component where I fetch data from a VUEX Store. This component is used on two separate pages, first page Home.vue and second page AllJobs.vue.
In AllJobs.vue I used JobComponent.vue and everything is works fine, it's rendering all the jobs, but, here comes the problem...
In Home.vue I want to render only the last 5 jobs, so in store I make a getter that slice me only the latest 5 jobs.
How can I use this latestJobs from getters on the same component?
When I import the component in Home.vue page I can't use another v-for direct on the component...
here you can see my project structure and files
Home.vue
<template>
<div class="cards-container">
<JobComponent />
</div>
</template>
JobComponent.vue
<template>
<div v-for="job in allJobs" :key="job.id" class="card">
<div class="position">{{ job.position }}</div>
<div class="department">{{ job.department }}</div>
<div class="location">
<span class="material-symbols-outlined">location_on</span>
{{ job.location }}
</div>
<span class="material-symbols-outlined right-arrow">arrow_right_alt</span>
<span #click="deleteJob(job.id)" class="material-symbols-outlined right-arrow">delete</span>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
export default {
methods: {
...mapActions(['fetchJobs', 'deleteJob']),
},
computed: mapGetters(['allJobs']),
created() {
this.fetchJobs();
}
}
</script>
store.js (vuex)
const getters = {
allJobs: (state) => state.jobs,
latestJobs: (state) => {
const response = state.jobs.slice(0, 5);
return response;
}
};
Your component should be as independent as possible from the store. It's role is to display what ever is provided so it could be reused as you want, using props :
JobComponent.vue
<template>
<div class="card">
<div class="position">{{ position }}</div>
<div class="department">{{ department }}</div>
<div class="location">
<span class="material-symbols-outlined">location_on</span>
{{ location }}
</div>
<span class="material-symbols-outlined right-arrow">arrow_right_alt</span>
<span #click="$emit('deleteJob', id)" class="material-symbols-outlined right-arrow">delete</span>
</div>
</template>
<script>
export default {
props: {
id: string,
position: string,
department: string,
location: string
}
}
</script>
In this component you only display the provided data, and leave the responsibility of the parent component to choose how many components to display.
Home.vue
<template>
<div class="cards-container">
<JobComponent v-for="job in jobs" :key="job.id" :id="job.id" :position="job.position" :department="job.department" :location="job.location" #delete-job="deleteJob" />
</div>
</template>
<script>
export default {
created() {
this.$store.dispatch('fetchJobs')
},
computed: {
jobs() {
return this.$store.getters['latestJobs'] // Or allJobs, just make sure your getter returns an array even if no jobs are loaded yet.
}
},
methods: {
deleteJob() {
// Your logic for job delete
}
}
}
</script>

Trying to access the store in a main page and I'm getting the error

Main.vue:
<template>
<div>
<div class="home_page_header">
<h1>My Recipe</h1>
<button #click="toggleOpen">Add New Recipe</button>
</div>
<div v-for="recipe in $store.state.recipes" :key="recipe.Recipe_Name">
<h1 >{{recipe.Recipe_Name}}</h1>
<p>{{recipe.Ingredients}}</p>
<router-link :to="`/recipe/${recipe.Recipe_Name}`">
<button>View Recipe</button>
</router-link>
<router-view/>
</div>
<div>
<h1></h1>
<p></p>
</div>
<div class="popUp" v-show="openUp">
<div>
<label for="receipe_name">Recipe Name</label>
<input type="text" id="receipe_name" v-model="values.receipeName"/>
</div>
<div>
<label for="ingredients_name">Ingredients</label>
<input type="text" id="ingredients_name" v-model="values.ingredientsName" v-for="i in values.ingredientsRows" :key="i"/>
<button #click="addrows" >Add Ingredients</button>
</div>
<div><button #click="onSubmit">Submit</button></div>
<div><button #click="toggleClose">Close</button></div>
</div>
</div>
</template>
<script>
import store from '#/store/index.js'
export default {
data() {
return {
values:{
receipeName:'',
ingredientsName:'',
ingredientsRows:1
},
values_final:{
receipeName:'',
ingredientsName:'',
ingredientsRows:1
},
openUp : false
}
},
methods: {
toggleOpen(){
this.openUp = true
},
toggleClose(){
this.openUp = false
},
onSubmit(){
if(this.values.receipeName || this.values.ingredientsName == ''){
alert('enter the full details')
}
},
addrows(){
this.values.ingredientsRows++;
},
},
computed: this.$store.commit('Add_Recipes',{...values_final})
}
</script>
store:
import { createStore } from 'vuex'
export default createStore({
state: {
recipes:[
{
Recipe_Name: 'curd',
Ingredients:'xxxxxx'
}
]
},
mutations: {
Add_Recipes (state,recipe) {
state.recipes.push(recipe)
}
},
Error : app.js:340 Uncaught TypeError: Cannot read properties of undefined (reading '$store')...
I'm trying to create a recipe app by using option API, I have one main page. In that main page that contains one title and add recipe button to add the details. And another one is a popup to enter the recipe details. so here after entering all the details that should show in a main page. I'm trying to access the store in a main page but iam getting the above error.
I guess you need to move the import statement of your store to your main.js file (not your Main.vue). And add app.use(store) after you created the app there with const app = createApp(…).

Click Event to parent in Vue with multiple components

Can't find a solution to my problem when using the same child component multiple times..
Maybe someone can help me? I feel like this is very easy.
I have a parent, which loads the same child component multiple times. The child component has an option to edit another component that is loaded by the parent.
It is working, but when I click edit, it shows all the components. I want it to show the component right beneath it.
Here is my code.
Parent:
<template>
<div>
<Card title="E-mail address" content="test#test.com" #clicked="onClickChild"></Card>
<Address v-if="this.editCard" />
<hr>
<Card title="Shipping method" content="Post 5 dollar"></Card>
<Shipment v-if="this.editCard" />
<hr>
<Card title="Payment method" content="Bank by wire"></Card>
<Payment v-if="this.editCard" />
<hr>
<Terms></Terms>
</div>
</template>
<script>
import Address from './forms/Address';
import Shipment from './forms/Shipment';
import Payment from './forms/Payment';
import Terms from './Terms';
import Card from './Card';
export default {
name: 'OPC',
components: {
Address,
Shipment,
Payment,
Terms,
Card
},
data() {
return {
editCard: false
}
},
methods: {
onClickChild (value) {
this.editCard = true
}
}
};
</script>
Child (Card.vue)
<template>
<div class="card mb-1 p-1">
<div class="d-flex justify-content-space-between">
<h3>{{ title }}</h3>
<span #click="editCard()"><i class="material-icons">mode_edit</i></span>
</div>
<p class="m-0">{{ content }}</p>
</div>
</template>
<script>
export default {
name: 'Card',
props: ['title', 'content'],
methods: {
editCard () {
this.$emit('clicked', 'someValue')
}
}
}
</script>

VueJS - using v-bind to pass an attribute supplied by a webservice

I started to work recently on a VueJS project (first time with that framework) and I face a problem.
I have an object (called "propObject") defined in a mother component. That propObject gets its value via a webservice, called in a beforeRouteEnter method in that mother component.
I have to pass this propObject to a child component so I can display what's inside (a "libelle" attribute, among other things). I tried to do it using v-bind and props but I didn't manage to make it work.
Here is my code :
Mother.vue
<template>
<div class="row justify-content-center">
<b-container>
<b-row>
{{propObject.libelle}}
<b-col> <cpm-child :prop-object="propObject"/></b-col>
[...]
</b-row>
</b-container>
</template>
<script lang="ts" src="./mother.component.ts"></script>
Mother.component
#Component({
components: {
'cpm-child': Child,
},
})
export default class Mother extends Vue {
#Inject('propObjectService') private propObjectService: () => propObjectService;
public propObject: IPropObjectClass = new PropObjectClass();
beforeRouteEnter(to, from, next) {
next(vm => {
if (to.params.propObjectId) {
vm.load(to.params.propObjectId);
}
});
}
public load(propObjectId: string): void {
this.propObjectService()
.find(propObjectId)
.then(res => {
this.propObject = res;
});
}
}
Child.vue
<template>
<div>
<span>
{{propObject.libelle}}
[...]
</span>
</div>
</template>
<script lang="ts" src="./child.component.ts"></script>
Child.component
export default class Child extends Vue {
props: {
propObject: IPropObjectClass,
}
}
propObject.model.ts
export interface IPropObjectClass {
code?: string;
libelle?: string;
[...]
}
export class PropObjectClass implements IPropObjectClass {
constructor(
public code?: string,
public libelle?: string,
[...]
) {}
}
My goal is to display the {{propObject.libelle}} in the child vue. In the Google Chrome's console, propObject is considered "undefined".
Last information : {{propObject.libelle}} is displayed correctly in the mother vue after a few seconds, so the propObjectService works as intended.
So far, nothing I tried worked, so any help would be greatly appreciated. If you need further clarification, don't hesitate to ask.
I created a sample with Vue 2 / Vue CLI showing a standard way of initializing a prop with data before rendering the child. You should be able to port it to your app.
The main takeaways are that you can call your data service in the parent (Mother) created() lifecycle hook. And by using the v-if directive, you child will not be rendered until the prop has been updated with data from the service call.
Parent.vue
<template>
<div class="parent">
<h4>Parent</h4>
<hr>
<child v-if="user" :userProp="user"/>
</div>
</template>
<script>
import axios from 'axios'
import Child from './Child.vue'
export default {
components: {
Child
},
data() {
return {
user: null
}
},
methods: {
getUser() {
axios.get('https://jsonplaceholder.typicode.com/users/1')
.then(response => this.user = response.data)
.catch(error => console.log(error));
}
},
created() {
this.getUser();
}
}
</script>
Child.vue
<template>
<div class="child">
<h5>Child</h5>
<div class="row">
<div class="col-md-6">
<div class="row">
<div class="col-md-3 font-weight-bold">ID</div>
<div class="col-md-5">{{ user.id }}</div>
</div>
<div class="row">
<div class="col-md-3 font-weight-bold">NAME</div>
<div class="col-md-5">{{ user.name }}</div>
</div>
<div class="row">
<div class="col-md-3 font-weight-bold">USER NAME</div>
<div class="col-md-5">{{ user.username }}</div>
</div>
<div class="row">
<div class="col-md-3 font-weight-bold">EMAIL</div>
<div class="col-md-5">{{ user.email }}</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
userProp: {
type: Object,
required: true
}
},
data() {
return {
user: this.userProp
}
}
}
</script>

Is it possible to call a function in one vue component from another vue component?

LogoutModal.vue
<template>
<div class="modal LogoutModal" v-bind:class="{'is-active':confirmLogout}">
<div class="modal-background"></div>
<div class="modal-content">
</div>
<button class="modal-close is-large" aria-label="close" #click="closeModal"></button>
</div>
</template>
<script>
export default {
name: "LogoutModal",
data() {
return {
confirmLogout: false
};
},
methods: {
closeModal: function() {
this.confirmLogout = false;
},
showModal: function() {
this.confirmLogout = true;
}
}
};
</script>
Navigation.vue
<template>
<aside class="menu">
<ul class="menu-list">
<li>
<a #click="showModal">Logout</a>
</li>
</ul>
<LogoutModal />
</aside>
</template>
<script>
import LogoutModal from "#/components/LogoutModal.vue";
export default {
name: "Navigation",
components: {
LogoutModal
}
};
</script>
I want to call the showModal function when i click on the Logout link. How can i achive that?
Or is that possible to change the variable in LogoutModal.vue from Navigation.vue. I have a variable called confirmLogout in LogoutModal.vue it has to be updated from Navigation.vue. How can I do that?
You should have confirmLogout in the navigation component, then pass that into your modal, you will also need a way to close the modal so in your modal you should this.$emit('close') when your user has signaled they want to close it.
<LogoutModal :open="confirmLogout" #close="confirmLogout=false" />
In your logout modal add a prop
<script>
export default {
name: "LogoutModal",
props: ['open'],
...
then in the template
<div class="modal LogoutModal" v-bind:class="{'is-active':open}">
You could define a dynamic property in LogoutModal, that sets it's state.
Here's more about that: https://v2.vuejs.org/v2/guide/components-props.html