Vue variable getting undefined inside component - vue.js

My variable is getting undefined inside component, can anyone Helps me?
The variable is: "professor.nome"
Basically I load my "professor" variable inside the carregarProfessores() method.
Is that a way to load the Titulo component after everything?
This is the component thais is not loading the variable:
<Titulo
:texto="
professorId !== undefined
? 'Professor: ' + professor.nome
: 'Todos os alunos'
"
/>
If I try to access the var like this, works:
<h1>{{ professor.nome }}</h1>
This is my Vue code:
export default {
components: {
Titulo,
},
data() {
return {
professorId: this.$route.params.prof_id,
nome: "",
alunos: [],
professor: [],
};
},
created() {
if (this.professorId) {
this.carregarProfessores();
this.$http
.get("http://localhost:3000/alunos?professor.id=" + this.professorId)
.then((res) => res.json())
.then((alunos) => (this.alunos = alunos));
} else {
this.$http
.get("http://localhost:3000/alunos")
.then((res) => res.json())
.then((alunos) => (this.alunos = alunos));
}
},
props: {},
methods: {
carregarProfessores() {
this.$http
.get("http://localhost:3000/professores/" + this.professorId)
.then((res) => res.json())
.then((professor) => {
this.professor = professor;
});
},
},
};
Here is the Titulo component:
<template>
<div>
<h1>{{ titulo }}</h1>
</div>
</template>
<script>
export default {
props: {
texto: String,
},
data() {
return {
titulo: this.texto,
};
},
};
</script>

The problem is that your Titulo component is stateful. It takes a copy of the initial value of the prop texto but doesn't update it when it changes.
There's no need to take a copy in the first place, just use the prop itself in the template:
<template>
<div>
<h1>{{ texto }}</h1>
</div>
</template>
<script>
export default {
props: {
texto: String
}
};
</script>

Try
data() {
return {
professorId: this.$route.params.prof_id || null, // changed
nome: "",
alunos: [],
professor: null, // changed
};
},
Then
<Titulo
:texto="
professorId && professor
? 'Professor: ' + professor.nome
: 'Todos os alunos'
"
/>

As per your data.
professor is array so you can not access nome direct.
So you have iterator over professor array or
<h1>{{ professor[0].nome }}</h1>

Related

Vue computed value as data property value

Simplified example component code:
<template>
<section>
<div>{{ z }}</div>
<div>{{ compZ }}</div>
<div>{{ x }}</div>
</section>
</template>
<script>
export default {
name: "example",
data: () => ({
z: false,
x: [{ visible: null }]
}),
mounted() {
this.x[0].visible = this.compZ;
setTimeout(() => (this.z = true), 1e3);
},
computed: {
compZ() {
return this.z;
}
}
};
</script>
After a second results are:
true
true
[ { "visible": false } ]
I need x[n].visible to change when compZ changes. Any ideas on how to cleanly keep reactivity?
This is required, because i have 22 potential steps, that are visible depending on certain flags that can change after initialization.
You can add watcher for your z.
watch: {
z: function (newValue, oldValue) {
// here you can change x.y
}
},
Found a workaround, but i think it's ugly:
<template>
<section>
<div>{{ refFlag1 }}</div>
<div>{{ compRefFlag1 }}</div>
<div>{{ x }}</div>
</section>
</template>
<script>
export default {
name: "example",
data: () => ({
refFlag1: false,
refFlag2: false,
x: [{ visible: null, visibleFunc: "that.compRefFlag1" }]
}),
watch: {
allRelevatFlags: function () {
setTimeout(() => this.updateVisible());
}
},
mounted() {
this.x[0].visible = this.compRefFlag1;
setTimeout(() => (this.refFlag1 = true), 1e3);
},
methods: {
updateVisible() {
// eslint-disable-next-line no-unused-vars
let that = this; // eval doesn't see 'this' scope
this.x.forEach(step => (step.visible = eval(step.visibleFunc)));
}
},
computed: {
allRelevatFlags() {
return `${this.compRefFlag1}${this.compRefFlag2}`;
},
compRefFlag1() {
return this.refFlag1;
},
compRefFlag2() {
return this.refFlag2;
}
}
};
</script>
Watch for changes in any relevant flag and then using JS eval() set the visible flag anew.
There's got to be a better way...

Props arguments are not reactive in setup

I'm trying to develop a component that compiles the given html; I succeeded with constant html texts, but I can't make this work with changing html texts.
main.js
app.component("dyno-html", {
props: ["htmlTxt"],
setup(props) {
watchEffect(() => {
console.log(`htmlText is: ` + props.htmlTxt);
return compile(props.htmlTxt);
});
return compile(props.htmlTxt);
},
});
Home.js
<template>
<div class="home">
<dyno-html
:htmlTxt="html2"
:bound="myBoundVar"
#buttonclick="onClick"
></dyno-html>
-------------
<dyno-html
:htmlTxt="html"
:bound="myBoundVar"
#buttonclick="onClick"
></dyno-html>
</div>
</template>
<script>
export default {
name: "Home",
components: {},
data: function() {
return {
html: "",
html2: `<div> Static! <button #click="$emit('buttonclick', $event)">CLICK ME</button></div>`
};
},
mounted() {
// get the html from somewhere...
setTimeout(() => {
this.html = `
<div>
Dynamic!
<button #click="$emit('buttonclick', $event)">CLICK ME</button>
</div>
`;
}, 1000);
},
methods: {
onClick(ev) {
console.log(ev);
console.log("You clicked me!");
this.html2 = "<b>Bye Bye!</b>";
},
},
};
</script>
Outcome:
Console:
It seems the changes of htmlText arrives to setup function, but it doesn't affect the compile function!
This is the expected behaviour because prop value is read once and results in static render function.
Prop value should be read inside render function. It can be wrapped with a computed to avoid unneeded compiler calls:
const textCompRef = computed(() => ({ render: compile(props.htmlTxt) }));
return () => h(textCompRef.value);

Get object from Vuex at specific index

I am getting an array from Vuex and I want an object at 2 positions.
HTML
<p class="">
{{ MainImg[2].para}}
</p>
Vue
export default {
name: "App",
components: { },
data() {
return {
imageQuery: this.$route.params.image,
};
},
computed: {
...mapGetters("design", {
MainImg: ["singleDesigns"]
})
},
created() {
this.fetchDesigns();
},
mounted() {
console.log(this.MainImg);
},
methods: {
fetchDesigns() {
this.$store.dispatch("design/getSingleDesign", this.imageQuery);
}
}
};
But it shows an undefined error.
And When I add MainImg array in Vue data like this.
data() {
return {
imageQuery: this.$route.params.image,
MainImg:[{para:"1"},{para:"2"},{para:"3"},{para:"4"}]
};
It Works.
P.S.-
Store Code-
export const state = () => ({
designs: [],
})
export const getters = {
singleDesigns(state) {
return state.designs;
}
}
I am not adding Action and Mutation because it works fine with other code.
It looks like the array is empty at the first rendering, so you should add a condition to render it :
<p class="" v-if="MainImg && MainImg.length >= 2">
{{ MainImg[2].para}}
</p>

Vuejs - How to get the current id in a url of an api with axios

When the client clicks on the button, I want him to block another client, so I want my id to be dynamic: http://example/user/:id.
My template:
<template>
<div class>
<div v-for="post in posts" :key="post.id">
<div>
<div>{{ post.name }}</div>
<div>{{ post.id }}</div>
<button #click='BlockUser'>Block</button>
</div>
</div>
</div>
</template>
And my script:
<script>
const axios = require('axios');
export default {
name: 'User',
data() {
return {
posts: [],
errors: [],
id: {
id: ""
},
}
},
methods: {
getData() {
axios.get(`http://example/user`)
.then(result => {
this.posts = result.data
console.log(result)
})
},
BlockUser() {
axios.get('http://example/user/blacklist/:id' + encodeURIComponent(this.id.id))
.then(response => {
console.log(response)
})
},
},
}
</script>
Initially, I set a value for the id in data id a number and it worked. But now that I put an empty string.
it returns an undefined
If your posts have user_id field inside, you can pass it to BlockUser method, and use it in the request url.
<button #click='BlockUser(post.user_id)'>Block</button>
BlockUser(user_id) {
axios.get(`http://example/user/blacklist/${user_id}`)
.then(response => {
console.log(response)
})
},

Vue v-model data is from ajax undefined value

I used the vue 2. I had a data from ajax, this is my code example:
<template>
<div>
<input type="input" class="form-control" v-model="siteInfo.siteId">
<input type="input" class="form-control" v-model="siteInfo.info.name">
<input type="input" class="form-control" v-model="siteInfo.accountData.name">
</div>
</template>
<script>
export default {
name: 'Site',
data() {
return {
siteInfo: {},
/* siteInfoName: '', */
}
},
/*computed: {
siteInfoName: function() {
return siteInfo.info.name || '';
},
...
},*/
methods: {
getData() {
// do ajax get data
this.$http.post('URL', {POSTDATA}).then(response => {
/*
response example
{ body:
data: {
sitdeId: 1,
info: { name: 'test'},
accountData: { name: 'accountTest'},
}
}
*/
this.siteInfo = response.body.data;
})
}
},
mounted() {
this.getData();
}
}
</script>
I got a warring message
[Vue warn]: Error in render: "TypeError: Cannot read property 'name'
of undefined"
I can use computed to fix it, but if I had a lot model, I should
write a lot computed.
I should create a lot data for those model?
I should not use an object to bind a lot model?
Does it have another solution for this situation? Thanks your help.
Before the data loads siteInfo.info will be undefined, so you can't access name in the v-model:
v-model="siteInfo.info.name"
Likewise for siteInfo.accountData.name.
My suggestion would be to set the initial value of siteInfo to null and then put a v-if="siteInfo" on the main div. Alternatively you could put a v-if on the individual input elements that checks for siteInfo.info and siteInfo.accountData.
You may also want to consider showing alternative content, such as a load mask, while the data is loading.
Don't be worried about too many v-models - you can do an iteration on the Object - like with Object.entries().
Vue.component('list-input-element', {
props: ['siteLabel', 'siteInfo'],
template: '<div><label>{{siteLabel}}<input type="input" class="form-control" v-model="siteInfo"></label></div>'
})
new Vue({
name: 'Site',
el: '#app',
data() {
return {
siteInfo: {},
}
},
methods: {
getData() {
// using mockup data for this example
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(response => response.json())
.then(json => {
console.log(json)
this.siteInfo = json
})
// do ajax get data
/*this.$http.post('URL', {
POSTDATA
}).then(response => {
this.siteInfo = response.body.data;
})*/
}
},
mounted() {
this.getData();
}
})
div {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<list-input-element v-for="siteInfo in Object.entries(siteInfo)" :site-label="siteInfo[0]" :site-info="siteInfo[1]" />
</div>
Rounding up
So, when you do the single file template, use a computed value, and return an Object from that.
Base your v-for on that computed, and you'll have no problems.
Something like this:
<template>
<div>
<input type="input" class="form-control" v-for="infoEl in siteInfoComputed" v-model="infoEl">
</div>
</template>
<script>
export default {
name: 'Site',
data() {
return {
siteInfo: {},
}
},
computed: {
siteInfoComputed: function() {
// you could check for all the keys-values you want here, and handle
// 'undefined' problem here
// so, actually you "create" the Object here that you're going to use
let ret = {}
// checking if this.siteInfo exists
if (Object.keys(this.siteInfo).length) ret = this.siteInfo
return ret
},
},
methods: {
getData() {
// do ajax get data
this.$http.post('URL', {POSTDATA}).then(response => {
/*
response example
{ body:
data: {
sitdeId: 1,
info: { name: 'test'},
accountData: { name: 'accountTest'},
}
}
*/
this.siteInfo = response.body.data;
})
}
},
mounted() {
this.getData();
}
}
</script>