I'm building a chrome extension using vue.js. In one of my vue components I get tab informations of the current tab and wanna display this information in my template. This is my code:
<template>
<div>
<p>{{ tab.url }}</p>
</div>
</template>
<script>
export default {
data() {
return {
tab: {},
};
},
created: function() {
chrome.tabs.query({ active: true, windowId: chrome.windows.WINDOW_ID_CURRENT }, function(tabs) {
this.tab = tabs[0];
});
},
};
</script>
The Problem is, that the template gets the data before it's filled through the function. What is the best solution for this problem, when the tab data doesn't change after it is set once.
Do I have to use the watched property, although the data is only changed once?
// EDITED:
I've implemented the solution, but it still doesn't work. Here is my code:
<template>
<div>
<div v-if="tabInfo">
<p>set time limit for:</p>
<p>{{ tabInfo.url }}</p>
</div>
<div v-else> loading... </div>
</div>
</template>
<script>
export default {
data() {
return {
tabInfo: null,
};
},
mounted() {
this.getData();
},
methods: {
getData() {
chrome.tabs.query({ active: true, windowId: chrome.windows.WINDOW_ID_CURRENT }, function(tabs) {
console.log(tabs[0]);
this.tabInfo = tabs[0];
});
},
},
};
</script>
The console.log statement in my getData function writes the correct object in the console. But the template only shows the else case (loading...).
// EDIT EDIT
Found the error: I used 'this' in the callback function to reference my data but the context of this inside the callback function is an other one.
So the solution is to use
let self = this;
before the callback function and reference the data with
self.tab
You could initialize tab to null (instead of {}) and use v-if="tabs" in your template, similar to this:
// template
<template>
<div v-if="tab">
{{ tab.label }}
<p>{{ tab.body }}</p>
</div>
</template>
// script
data() {
return {
tab: null,
}
}
new Vue({
el: '#app',
data() {
return {
tab: null,
}
},
mounted() {
this.getData();
},
methods: {
getData() {
fetch('https://reqres.in/api/users/2?delay=1')
.then(resp => resp.json())
.then(user => this.tab = user.data)
.catch(err => console.error(err));
}
}
})
<script src="https://unpkg.com/vue#2.5.17"></script>
<div id="app">
<div v-if="tab">
<img :src="tab.avatar" width="200">
<p>{{tab.first_name}} {{tab.last_name}}</p>
</div>
<div v-else>Loading...</div>
</div>
Related
In my parent component I have a button and child component:
<div> #click="editMe(edit)" />
<edit-team :openModal="openModal"/>
</div>
export default {
data() {
return {
openModal: false,
};
},
method: {
editMe(edit) {
this.openModal = true;
},
}
}
So after I click the editMe button, I am expecting openModal becomes true and goes true to child component.
<template>
<el-dialog
:visible.sync="modal"
/>
</template>
<script>
export default {
props: {
openModal: {
type: Boolean,
required: true,
},
},
data() {
return {
modal: this.openModal,
};
},
</script>
But unfortunately, the modal is not opening because props comes as false always. I assigned openModal to new variable because it was giving me a warning about mutating props. So how do you think I can send the props in right value?
In child just try this
<template>
<el-dialog
:visible.sync="openModal"
/>
</template>
<script>
export default {
props: {
openModal: {
type: Boolean,
required: true,
},
},
},
</script>
If you have sibilngs components and you need to retrieve data, you can use the emit keyword and emit events
Then it will work like this :
The sibling emit the event to show the modal
The parent update the showModalData to true
This child is re-rendered
Vue.component('modal', {
template: '#modal',
props: ['show'],
});
Vue.component('buttonModal', {
template: '#buttonModal',
methods: {
showModal(){
this.$emit('show-modal-button', true)
}
}
});
new Vue({
el: '#app',
data: () => ({
showModalData: false
}),
methods: {
editMe() {
this.showModalData = true
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="app">
<p>this is the parent</p>
<hr>
<button-modal #show-modal-button="editMe">Show modal</button-modal>
<hr>
<modal :show="showModalData" />
</div>
<template id="modal">
<div>
props show : {{ show }}
<h2 v-if="show">This is the modal</h2>
</div>
</template>
<template id="buttonModal">
<div>
<p>This is the sibilng</p>
<button #click="showModal"> Show the modal through sibiling components </button>
</div>
</template>
Hi I'm having problems with a v-if that's not updating after receiving an eventbus event. This is my code :
<template>
<div class="main">
<button v-if="inCreationMode()">
Make a new snippet
</button>
<button v-if="mode ==='edit'">Push edits</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
mode: "creation",
};
},
created() {
this.emitter.on("editSnippet", snippet => function(snippet){
this.mode = "edit";
});
},
};
</script>
I tried replacing the v-if conditions by functions :
<template>
<div class="main">
<button v-if="inCreationMode()">
Make a new snippet
</button>
<button v-if="inEditMode()">Push edits</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
mode: "creation",
};
},
created() {
this.emitter.on("editSnippet", snippet => function(snippet){
this.mode = "edit";
});
},
methods:{
inCreationMode() {
return this.mode === "creation";
},
inEditMode(){
return this.mode ==="edit";
}
}
};
</script>
I tried using this.$forceUpdate() as well.
Does anyone know a solution to this problem.
Thanks in advance.
I see many problems here, that could all be part of the reason why it's not working. Given that you have defined your emitter to be new Vue(), and added that to the Vue prototype correctly and that the component that emits the event does it by this.emitter.emit('editSnippet'). You have to:
Replace
this.emitter.on("editSnippet", snippet => function(snippet){ this.mode = "edit"; });
with
this.emitter.on("editSnippet", () => { this.mode = "edit";});
Reasons:
You have to use an arrow function, otherwise this will not reference your component.
Your callback should be a function that "does something", not one that returns a function (that won't be called)
Further improvements (optional):
Use computed props instead of methods as described in the other answer
Cleanup the listener in beforeUnmount (or beforeDestroy) with this.emitter.off... . Actually not really optional, it is at least really bad code style to not do that
You should use computed properties instead of methods in this case :
<template>
<div class="main">
<button #click=" inCreationMode() " v-if="inCreationMode">
Make a new snippet
</button>
<button v-if="inEditMode">Push edits</button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
mode: "creation",
};
},
created() {
this.emitter.on("editSnippet", snippet => function(snippet){
this.mode = "edit";
});
},
computed:{
inCreationMode() {
return this.mode === "creation";
},
inEditMode(){
return this.mode ==="edit";
}
}
};
</script>
Note : remove the () from the v-if value
I want to show loader initially but after the response I want to hide it. I have following code:
<template>
<div id="app">
<Document :loading="loading">
</div>
</template>
<script>
import Document from "./components/Document";
export default {
name: "App",
data() {
return {
loading: true
};
},
components: {
Document
},
methods:{
function(){
let response = await Axios.get(`this-is-url`, {});
if (response.data == null) {
return;
} else {
this.loading = false
}
}
}
};
</script>
The loader will be placed in the document component like below:
<template>
<div>
<div>
<b-spinner type="grow" label="Spinning"></b-spinner>
</div>
</div>
</template>
<script>
export default {
name: "Document",
props: {
loading: null
},
};
</script>
I am writing function in App component because this will come from emit.
i think by changing the value of loading to false at the response it will hide the Document component.
if (response.data == null) {
this.loading= false;
}
I want to use the Id of the element in which I use the function as a parameter. Example:
<div
class="col-2 column-center"
id="myId"
#mouseover="aFunction(id)"
>
methods: {
aFunction(id){
alert(id);
}
}
But it doesn't work. How can I do it?
In another way, you can make your id attribute bind with the data property, like this :
<template>
<div class="col-2 column-center" v-bind:id="id" #mouseover="aFunction(id)">
test
</div>
</template>
<script>
export default {
name: 'Test',
data() {
return {
id: 'myId',
};
},
methods: {
aFunction(id) {
alert(id);
},
},
};
</script>
You can pass the event to the method and then get the id from event.target.id.
https://jsfiddle.net/tr0f2jpw/
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!'
},
methods: {
aFunction(event){
alert(event.target.id);
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<div
class="col-2 column-center"
id="myId"
#mouseover="aFunction($event)"
>
some stuff
</div>
</div>
In my application, I use a 2 way binding with vuex:
messageContent: {
get () {
return store.getters.messageContent
},
set (value) {
store.commit('updateContent', value)
}
},
<b-form-textarea id="content" v-model="messageContent" :rows="3"></b-form-textarea>
<b-button size="sm" variant="primary" value="Cancel" aria-label="Cancel" #click.prevent='onCancel'>
<i class="fa fa-times" aria-hidden="true"></i> Cancel
</b-button>
Since mutation of the prop occurred already, if I click cancel edit button:
methods: {
onCancel () {
this.isEdit = !this.isEdit
}
}
how do I rollback?
I figured one way is the solution, but I'm not sure how to implementation it with vuex.
I think you need something like in this example, you need to clearly distinguish what is old content - old state and what new content - new state. Something, as this is simplified, yet working example.
var store = new Vuex.Store({
state: {
savedContent: 'Previous content'
},
mutations: {
saveContent (state, value) {
state.savedContent = value
}
}
})
new Vue ({
el: '#app',
store,
data: {
newContent: store.state.savedContent
},
methods: {
save () {
this.$store.commit('saveContent', this.newContent)
},
cancel () {
this.newContent = this.$store.state.savedContent
}
}
})
<div id="app">
<textarea v-model="newContent"></textarea><br>
<button #click="save">save</button>
<button #click="cancel">cancel</button>
</div>
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vuex"></script>