Vue-apollo refetch/update data after prop change - vue.js

I'm having a problem with refeching data when my prop(id) is changing.
-- moveing between profiles (profile/1 to profile/2) --
I scour through the internet to find some information. Tried this.$forceUpdate, some methods too but nothing seems to resolve my problem. Adding :key to components only refresh first one "profile-header" but not working on other in "section".
Any idea to how to solve it? I will appreciate any help.
Im new to vue.
profile.vue
<template>
<div
v-if="Object.keys(user).length"
id="user-profile"
>
<profile-header
:header-data="user"
/>
<!-- profile info -->
<section id="profile-info">
<b-row>
<b-col
lg="3"
cols="12"
order="2"
order-lg="1"
>
<profile-about
:about-data="user"
/>
<profile-suggested-pages
:pages-data="user"
/>
</b-col>
<b-col
lg="6"
cols="12"
order="1"
order-lg="2"
>
<profile-post
:posts="user"
/>
</b-col>
</b-row>
</section>
</div>
</template>
<script>
import gql from 'graphql-tag'
import ProfileHeader from './ProfileHeader.vue'
import ProfileAbout from './ProfileAbout.vue'
import ProfileSuggestedPages from './ProfileSuggestedPages.vue'
import ProfilePost from './ProfilePost.vue'
export default {
components: {
ProfileHeader,
ProfileAbout,
ProfileSuggestedPages,
ProfilePost,
},
data() {
return {
routeParam: this.$route.params.id,
user: { },
}
},
watch: {
$route(from) {
console.log(this.$route.params.id)
console.log(this.user.id)
},
},
apollo: {
user: {
query: gql`
query User($id: ID!) {
user(id: $id) {
// query //
`,
variables() {
return { id: this.routeParam }
},
},
},
}
</script>
routes
{
path: '/profil/:id',
name: 'profile',
component: () => import('#/views/profile/Profile.vue'),
props: true,
}

Related

Vue.js 2 data not displayed in the template

I'm using vue.js 2 / vue-cli with axios. I'm facing a problem with the display of my posts (in the wall). I've made my axios request, have gotten my data (in the console), I've written my template and ... nothing was displayed ... I really appreciate your help
my template :
<template>
<v-layout column>
<v-flex xs4>
<panel title="The Wall">
<p v-for="post in posts" :key="post.index">{{ post.title }} - {{ post.content }}</p>
</panel>
</v-flex>
</v-layout>
</template>
my script :
<script>
import Panel from '../components/Panel'
import PostService from "../services/PostService.js";
export default {
components: {
Panel
},
data() {
return {
posts: null
}
},
async mounted() {
this.posts = (await PostService.getAllPosts()).data;
}
}
</script>
Add a data property called cidItem for example and bind it to your props as follows
<template>
<div id="app" class="container" :cid="cidItem">
<Images :cid="cidItem" />
<Video :cid="cidItem" />
<TextType :cid="cidItem" />
<Card :cid="cidItem" />
</div>
</template>
<script>
import axios from 'axios';
import Images from "./components/Images";
import Video from "./components/Video";
import TextType from "./components/TextType";
import Card from "./components/Card";
export default {
name: 'app',
props: ["cid"],
components: {
Images,
Video,
TextType,
Card
},
mounted() {
axios({method: "GET", "url": this.contentDeliveryUrl}).then(result => {
// eslint-disable-next-line
this.content = amp.inlineContent(result.data)[0];
console.log(this.content)
}, error => {
console.error(error);
});
},
data() {
return {
contentDeliveryUrl: 'https://c1.adis.ws/cms/content/query?fullBodyObject=true&query=%7B%22sys.iri%22%3A%22http%3A%2F%2Fcontent.cms.amplience.com%2F${this.cid}%22%7D&scope=tree&store=testing',
content: [],
cidItem:'7e4301de-9c6e-4fab-9e68-3031b94d662d'
}
}
}
</script>
Since your component have the same structure i recommend to use mixins, create a file named myMixins.js and add the following code inside it :
const myMixins = {
props:['cid'],
mounted() {
axios({
method: "GET",
"url": this.contentDeliveryUrl
}).then(result => {
// eslint-disable-next-line
this.content = amp.inlineContent(result.data)[0];
console.log(this.content)
}, error => {
console.error(error);
});
},
data() {
return {
contentDeliveryUrl: 'https://c1.adis.ws/cms/content/query?fullBodyObject=true&query=%7B%22sys.iri%22%3A%22http%3A%2F%2Fcontent.cms.amplience.com%2F${this.cid}%22%7D&scope=tree&store=testing',
content: []
}
}
}
export default mixins;
and inside each component add this :
import myMixins from './myMixins'
export default{
....
mixins: [myMixin]
}

passing data in vuejs using props

I'm doing image upload in vuejs. And I separate the upload file, use props to listen
In user.vue
<template>
<a-form
:form="form"
#submit.prevent="uploadFile"
>
<a-tabs v-model="activeKey" tabPosition="right">=
<image-upload
:option="{
title: 'Upload',
placeholder: 'File upload'
}"
/>
</a-tabs>
</a-form>
</template>
<script>
import { Tabs, Form } from "ant-design-vue";
Vue.use(Tabs);
Vue.use(Form);
export default {
components: {
image-upload: () =>
import(
/* webpackChunkName: "js/chunks/photo-platform.chunk" */ "./upload/index.vue"
),
},
methods: {
uploadFile() {
this.form.validateFields((err, values) => {
console.log(values);
});
}
}
}
</script>
In upload/index.vue
<template>
<b-row>
<b-col md="3">
<b-form-group>
<label>{{option.title}}</label>
<b-form-file
accept="image/*"
placeholder= {{ option.placeholder }}
drop-placeholder="Drop file here..."
name="titleImage"
v-model="titleImage"
#change="changeTitle"
multiple
/>
</b-form-group>
</b-col>
</b-row>
</template>
<script>
import {
BCard,
BRow,
BCol,
BForm,
} from "bootstrap-vue";
export default {
props: {
option: Object
},
components: {
BFormGroup,
BRow,
BCol,
BCard,
},
data() {
return {
titleImage: [],
}
},
methods: {
changeTitle() {
////
}
}
}
</script>
I am using props in vuejs. Now I want when I select multiple images in upload/index.vue, and press submit form uploadFile in user.vue, I will get the data of the image I just selected in. Please give me your opinion. I'm new to vuejs so it's really hard for me. Thanks

VueJS props issue something missing

I have a tabbed view and I need to pass a value from a select field to the tabs but I don't get props right. This is the parent:
<template>
<div>
<b-form-group id="memberListing" label-for="memberListing" class="mr-sm-2">
<b-form-select
v-model="memberSelection"
:title="memberSelection"
#change="getUserID"
aria-describedby="memberListing"
>
<option disabled value="" selected>Mitglied auswählen</option>
<option
v-for="member in memberList"
v-bind:key="member"
:value="member.cb_userid"
lazy
>
{{member.user_name}}
</option>
</b-form-select>
</b-form-group>
<Biometrics :title="memberSelection"></Biometrics>
<b-card title="Card Title" no-body>
<b-card-header header-tag="nav">
<b-nav card-header tabs>
<b-nav-item to="/users/profile" exact exact-active-class="active"
>Profil</b-nav-item
>
<b-nav-item
to="/users/trainingsplans"
exact
exact-active-class="active"
>Trainingspläne</b-nav-item
>
<b-nav-item to="/users/biometrics" exact exact-active-class="active"
>Biometrie</b-nav-item
>
</b-nav>
</b-card-header>
<b-card-body>
<router-view></router-view>
</b-card-body>
</b-card>
</div>
</template>
<script>
import axios from 'axios'
import Biometrics from "#/components/users/biometrics.vue";
export default {
data: () => {
return {
profileList: 'http://localhost:8000/userProfiles/profileList',
memberList: [],
memberSelection: null,
}
},
props: {
},
components: {
Biometrics
},
async mounted() {
try {
let memberListData = await axios.get(this.profileList)
this.memberList = memberListData.data
console.log(this.memberList)
} catch (e) {
this.errors.push(e)
}
},
methods: {
getUserID: function () {
// this.childMemberSelect = this.memberSelection
// console.log(this.childMemberSelect)
},
},
}
</script>
What am I missing? I'm trying for hours now but I cant get the value to show up in the specified tab when I select the value.
this is the child :
<pre>
<template>
<div>
{{title}}
</div>
</template>
<script>
export default {
created () {
console.log(this.title)
},
methods: {},
props: {
title: Number,
},
data() {
return {
}
},
components:{},
}
</script>
</pre>

Is possible to generate vue component using for loop inside function?

Is possible to generate vue component using for loop. I am trying to generate and able to get but it's override by new component dynamically it's override component one schema also with component second with is at last generated.
https://jsfiddle.net/3ordn7sj/5/
https://jsfiddle.net/bt5dhqtf/973/
for (var key in app.html) {
Vue.component(key, {
template: `<div><vue-form-generator :model="model"
:schema="schema"
:options="formOptions"
ref="key"></vue-form-generator>{{ key }}</div>`,
mounted() {
this.schema = app.html[key]
},
data: function () {
return {
schema: app.html[key],
key: '',
formOptions: this.formOptions,
model: this.model,
}
},
}
)
}
Is possible to generate vue component using for loop. I am trying to generate and able to get but it's override by new component dynamically it's override component one schema also with component second with is at last generated. In above jsfiddel link my data is there inside created.
I am trying to generate vue component base on this data and I am using vue form generator.In above code what exactly I am trying to do is while my loop running form generated but I don't know how it's first component also getting second component schema and it;s showing on first step also overrides schema data.
I am very confused why this is happening I tried a lot but I am not getting any solution if you have please suggest what I can do for generate component using for loop inside function.
Please try to solve this issue or tell me id it;s not possible.
I did like this
<form-wizard #on-complete="onComplete"
#on-change="handleChange"
validate-on-back
ref="wizard"
:start-index.sync="activeTabIndex"
shape="circle" color="#20a0ff" error-color="#ff4949" v-if="html != 0">
<tab-content v-for="tab in tabs"
v-if="!tab.hide"
:key="tab.title"
:title="tab.title"
:icon="tab.icon">
<component :is="tab.component"></component>
</tab-content>
</form-wizard>
Inside Data I have added for now this tabs option
tabs: [{title: 'Personal details', icon: 'ti-user', component: 'firstTabSchema'},
{title: 'Is Logged In?', icon: 'ti-settings', component: 'secondTabSchema', hide: false},
],
generateNewForm.vue
<template>
<div class="app animated fadeIn">
<loading :active.sync="this.$store.state.isLoading"
:can-cancel="true"
:is-full-page="this.$store.state.fullPage"></loading>
<b-row>
<b-col cols="12" xl="12">
<transition name="slide">
<b-card>
<div slot="header">
<b-button variant="primary" #click="goBack"><i class="icon-arrow-left icons font-1xl"></i>Back</b-button>
</div>
<formcomponent :tree="this.$store.state.formData" />
</b-card>
</transition>
</b-col>
</b-row>
</div>
</template>
<script>
import {store} from '#/components/store'
import formcomponent from '#/components/formcomponent';
import Vue from 'vue'
import Loading from 'vue-loading-overlay';
import 'vue-loading-overlay/dist/vue-loading.css';
import {FormWizard, TabContent} from 'vue-form-wizard'
import 'vue-form-wizard/dist/vue-form-wizard.min.css'
import VueFormGenerator from "vue-form-generator";
/*import VeeValidate from 'vee-validate';*/
Vue.use(VueFormGenerator);
Vue.use(Loading);
export default {
name: 'tables',
store: store,
data: () => {
return {
finalModel: {},
activeTabIndex: 0,
model: {},
count: 0,
}
},
components: {
'loading': Loading,
FormWizard,
TabContent,
formcomponent: formcomponent
},
created() {
},
beforeMount() {
this.$store.dispatch('loadFormData', this.$route.params.id);
},
methods: {
onComplete: function(){
alert('Yay. Done!');
},
goBack() {
this.$router.go(-1)
}
}
}
</script>
formcomponent.vue
<template>
<div>
<form-wizard #on-complete="onComplete"
#on-change="handleChange"
validate-on-back
ref="wizard"
:start-index.sync="activeTabIndex"
shape="circle" color="#20a0ff" error-color="#ff4949" v-if="html != 0">
<tab-content v-for="tab in tabs"
v-if="!tab.hide"
:key="tab.title"
:title="tab.title"
:icon="tab.icon">
<component :is="tab.component"></component>
</tab-content>
</form-wizard>
</div>
</template>
<script>
import Vue from 'vue'
import {FormWizard, TabContent} from 'vue-form-wizard'
import 'vue-form-wizard/dist/vue-form-wizard.min.css'
import VueFormGenerator from "vue-form-generator";
//console.log(Vue.options);
Vue.use(VueFormGenerator);
export default {
components: {
FormWizard,
TabContent,
},
data() {
return {
loadingWizard: false,
error: null,
count: 0,
dash: '-',
firstTime: 0,
model: {},
html: '',
index: '',
activeTabIndex: 0,
tabs: [{title: 'Personal details', icon: 'ti-user', component: 'firstTabSchema'},
{title: 'Is Logged In?', icon: 'ti-settings', component: 'secondTabSchema', hide: false},
],
formOptions: {
validationErrorClass: "has-error",
validationSuccessClass: "has-success",
validateAfterLoad: true,
validateAfterChanged: true,
},
}
},
created() {
this.html = this.tree;
this.index = this.ind;
},
props: ['tree', 'ind'],
methods: {
onComplete: function () {
alert('Yay. Done!');
},
setLoading(value) {
this.loadingWizard = value
},
handleChange(prevIndex, nextIndex) {
console.log(`Changing from ${prevIndex} to ${nextIndex}`)
},
setError(error) {
this.error = error
},
validateFunction: function () {
return new Promise((resolve, reject) => {
console.log(this.$refs.firstTabSchema);
setTimeout(() => {
if (this.count % 2 === 0) {
reject('Some custom error')
} else {
resolve(true)
}
this.count++
}, 100)
})
},
validate() {
return true
},
buildTree(tree, rep = 1) {
var html = '';
var app = this;
var dash = "--";
app.html = tree;
var test = this.formOptions;
for (var key in app.html) {
var isComponentExists = key in Vue.options.components
if(!isComponentExists) {
Vue.component(key, {
template: `<div :class="key"><vue-form-generator :model="model"
:schema="schema"
:options="formOptions"
ref="key"></vue-form-generator>{{ key }}</div>`,
mounted() {
this.schema = app.html[key]
this.key = key
},
data: function () {
return {
schema: app.html[key],
key: '',
formOptions: this.formOptions,
model: this.model,
}
},
}
)
//console.log(Vue.$options);
this.$emit('init')
}
}
}
},
watch: {
tree: {
handler() {
this.html = '';
this.buildTree(this.tree)
},
deep: true
}
}
}
</script>
So if I understand you correctly you are trying to use a list of some kind app.html to dynamically register a set of identical components under different names (key). I think it is possible, but i cannot tell from the code you provided what is going wrong.
I can tell you that this approach to code reuse/abstraction is probably not the right way to go. The whole point of components is that you can reuse functionality with the use of binding props. What you are trying to do is probably better achieved like this:
Vue.component('my-custom-form', {
props: ['key', 'schema', 'formOptions', 'model'],
template: `
<div>
<vue-form-generator
:model="model"
:schema="schema"
:options="formOptions"
:ref="key"
></vue-form-generator>{{ key }}
</div>`,
})
Then in your vue template:
<my-custom-form
v-for="(key, value) in app.html"
:key="key"
:schema="value"
:formOptions="formOptions"
:model="model"
/>
Let me know if that helps. Otherwise, if you are sure you want to stick with your original approach give me some more context for the code and I will see what i can do. Best of luck!
I think i understand a little bit better where you are getting stuck. I see this piece of code in your jsfiddle:
<div id="app">
<div>
<form-wizard #on-complete="onComplete">
<tab-content v-for="tab in tabs"
v-if="!tab.hide"
:key="tab.title"
:title="tab.title"
:icon="tab.icon">
<component :is="tab.component"></component>
</tab-content>
</form-wizard>
</div>
</div>
You don't need to use the component :is syntax to solve this problem. You could also write is as follows:
<div id="app">
<div>
<form-wizard #on-complete="onComplete">
<tab-content v-for="(tab, tabindex) in tabs"
v-if="!tab.hide"
:key="tab.title"
:title="tab.title"
:icon="tab.icon">
<my-custom-form v-if="tabindex == 1" :key="'the first key'" :schema="app.html['the first key']"/>
<my-custom-form v-else-if="tabindex == 2" :key="'the second key'" :schema="app.html['the second key']"/>
</tab-content>
</form-wizard>
</div>
</div>
etc. Let me know if that example is clear.
best

How to share ajax results between two components with the first one using the second?

First of all, I'm a beginner with VueJS, so I may presenting you a bunch of non-sens. :-) I read all the beginner doc, but I'm still stuck for this case.
I have 2 template component managed by a functionnal component:
<template>
<h2>PageSpeed performance score: {{ results.score }}.</h2>
</template>
The second one, using the first one (the first one is needed to be used elsewhere to display score only:
<template>
<div>
<template v-if="results">
<hosting-performance-score :results="results"/>
<div
v-for="(result, rule) in results.rules"
v-if="result.ruleImpact > 0"
:key="rule"
class="panel panel-default"
>
<div class="panel-heading">{{ result.localizedRuleName }}</div>
<div class="panel-body">
<p>
{{ result.summary.format }}
<b>{{ result.ruleImpact }}</b>
</p>
</div>
</div>
</template>
<i
v-else
class="fa fa-spin fa-spinner"
/>
</div>
</template>
<script>
import HostingPerformanceScore from './HostingPerformanceScore';
export default {
components: {
HostingPerformanceScore,
},
};
</script>
And then, the functional one with the AJAX logic:
<script>
import axios from 'axios';
import axiosRetry from 'axios-retry';
import HostingPerformanceScore from './HostingPerformanceScore';
import HostingPerformancePage from './HostingPerformancePage';
axiosRetry(axios);
export default {
functional: true,
props: {
scoreOnly: {
default: false,
type: Boolean,
},
slug: {
required: true,
type: String,
},
},
data: () => ({
results: null,
}),
created() {
axios.get(Routing.generate('hosting_performance_pagespeed', {
slug: this.slug,
})).then((response) => {
this.results = {
rules: Object.entries(response.data.formattedResults.ruleResults).map((entry) => {
const result = entry[1];
result.ruleName = entry[0];
return result;
}).sort((result1, result2) => result1.ruleImpact < result2.ruleImpact),
score: response.data.ruleGroups.SPEED.score,
};
});
},
render: (createElement, context) => {
return createElement(
context.props.scoreOnly ? HostingPerformanceScore : HostingPerformancePage,
context.data,
context.children
);
},
};
</script>
The issue is: I can't access the result and I don't know how to pass it properly: Property or method "results" is not defined on the instance but referenced during render.
Or maybe functional components are not designed for this, but I don't know how to achieve it otherway. How would you do it?
Thanks! :-)
You appear to have this a little backwards in terms of which components can be functional and which not.
Since your HostingPerformanceScore and HostingPerformancePage components are really only rendering data, they can be functional components by just rendering props they accept.
Your other component has to maintain state, and so it cannot be a functional component.
I put together an example of how this might work.
HostingPerformanceScore
<template functional>
<h2 v-if="props.results">
PageSpeed performance score: {{ props.results.score }}.
</h2>
</template>
HostingPerformancePage
<template functional>
<div>
<h2>Hosting Performance Page</h2>
<HostingPerformanceScore :results="props.results"/>
</div>
</template>
<script>
import HostingPerformanceScore from "./HostingPerformanceScore.vue";
export default {
components: {
HostingPerformanceScore
}
};
</script>
PerformanceResults.vue
<template>
<HostingPerformanceScore :results="results" v-if="scoreOnly" />
<HostingPerformancePage :results="results" v-else />
</template>
<script>
import HostingPerformancePage from "./HostingPerformancePage.vue";
import HostingPerformanceScore from "./HostingPerformanceScore.vue";
export default {
props: {
scoreOnly: Boolean
},
data() {
return {
results: null
};
},
created() {
setTimeout(() => {
this.results = {
score: Math.random()
};
}, 1000);
},
components: {
HostingPerformancePage,
HostingPerformanceScore
}
};
</script>
And here is a working example.