How to access a Vue component's data from a script - vue.js

Here are the simplified html and javascript files of the page. It has a button and component which is a text displays the data of the component. I want the component's data to be changed when I click the button. But how to access the component's data from a script?
index.html
<body>
<div id="app">
<my-component></my-component>
<button id="btn"> change data </button>
</div>
<script src="https://unpkg.com/vue#next"></script>
<script src="./main.js"></script>
</body>
main.js
let app = Vue.createApp({});
app.component('my-component', {
data: function() {
return {
component_data : "foo"
}
},
template: '<p> data = {{ component_data }} </p>'
}
);
app.mount("#app");
document.querySelector("btn").onclick = function() {
// HOW TO CHANGE component_data TO "bar"
}

One possibility is to incorporate the button into the HTML within the component's template. If that's feasible for your app then you can add a function to the component and bind the function to the button's click event.
E.g. (Note this is untested so may have typos)
app.component('my-component', {
data: function() {
return {
component_data : "foo"
}
},
methods: {
changeData() {
this.component_data = "The data changed";
}
},
template: `<p> data = {{ component_data }} </p>
<button #click="changeData">Change data</button>`
}
);
If the button can't be incorporated into my-component then I'd recommend using the Vuex datastore. Vuex is a reactive datastore that can be accessed across the entire application.

You can use component props change data between components.
index.html
<body>
<div id="app">
<my-component :component-data="text"></my-component>
<button #click="handleBtnClick"> change data </button>
</div>
<script src="https://unpkg.com/vue#next"></script>
<script src="./main.js"></script>
</body>
main.js file
let app = Vue.createApp({
data() {
return { text: 'foo' }
},
methods: {
handleBtnClick() {
this.text = 'bar';
}
}
});
app.component('my-component', {
props: {
componentData: {
type: String,
default: 'foo'
}
}
template: '<p> data = {{ componentData }} </p>'
}
);
app.mount("#app");
I think you new in Vuejs. You have to first read Vue documentation

To get the reference of a component outside of it, you can use the template refs
Here is the refactor of the code provided in the above question to access the components data from the script.
<div id="app">
<my-component ref="my_component"></my-component>
<button #click="onBtnClick()"> change data </button>
</div>
let app = Vue.createApp({
methods: {
onBtnClick() {
this.$refs.my_component.component_data = "bar";
}
}
});

Related

Update properties of component in Vue.js

I've been searching for the answer 2nd day. But still couldn't find solution.
I have the modal window template. And the main page template from where I need to update modal window size by clicking on the button (span). Shortly it's like this for HTML:
<template id="modal">
<div>
<div :class="'modal-' + size">
...
</div>
</div>
</template>
<template id="list">
<div>
<span #click="onDetails">
Show Details
</span>
</div>
<modal size="md" #showdetails="showdetails();" ref="modal">
...
</modal>
</template>
And for JS:
Vue.component("modal", {
template: "#modal",
props: {
size: {
type: String,
default: ""
}
},
methods: {
onDetails() {
this.$emit("showdetails")
}
}
})
var List = Vue.extend({
template: "#list",
methods: {
showDetails() {
if(this.$refs.modal.size == "md") {
this.$refs.modal.size = "lg"
}
<additional code here>
}
}
})
When I'm accessing this.$refs.modal.size for read - it's OK. When I'm just changing it from showDetails - OK if only this action in the function. When I'm put something else instead of - size is not updating.
For example:
this.$refs.modal.size = "lg" - will work
this.$refs.modal.theme = "danger"; this.$refs.modal.size = "lg" - neither of them are updating
What am I doing wrong?
You need to assign the attribute value of Size by the javascript method setAttribute . Example : this.$refs.modal.setAttribute('size', 'lg')
There is a working demo below:
new Vue({
el: '#app',
methods: {
showdetails() {
console.log(this.$refs.modal.getAttribute('size'));
this.$refs.modal.setAttribute('size', 'lg')
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.15/vue.js"></script>
<div id='app'>
<button size="md" #click="showdetails" ref="modal">Click</button>
</div>

How do I render data from Firebase in vue at page load

I am trying to load data from firebase at page load.
The console logs correctly as follows: {-Ltl2osulqmFnKIRoT5Q: {…}, -LtnKKxEWkEH7DbV7VB-: {…}}.
I can't however get the data rendered. fbData only shows []
This is what I have: (I know that I have to use a v-for loop for rendering, but below is only to see if anything is rendered at all, which in my case isn't)
<template>
<div id="main">
<div id="cardFront">{{fbData}}</div>
</div>
</template>
<script>
module.exports = {
data () {
return {
fbData:[],
}
},
created() {
var ref = firebase.database().ref("Users/MK01111000/cards")
ref.once("value")
.then(function(snapshot) {
this.fbData = snapshot.val()
console.log(this.fbData)
})
}
}
What I am looking for is a way to render my data at page load.
<div id="main">
<div id="cardFront">{{preRenderedData}}</div>
</div>
</template>
<script>
module.exports = {
data () {
return {
preRenderedData: null,
fbData:[],
}
},
created() {
var ref = firebase.database().ref("Users/MK01111000/cards")
ref.once("value")
.then(function(snapshot) {
this.fbData = snapshot.val()
})
},
beforeMount() {
this.preRenderedData = this.fbData
}
}

How you do you call a method once rendering is done in Vue?

I have am building a Vue app that includes a QuillJS editor in a tab. I have a simple setTab(tabName) Vue method that shows/hides tabs with the v-if directive.
methods: {
setTab: function (tabName) {
this.view = tabName;
if(tabName === 'compose') {
var editor = new Quill('#editor', {
modules: { toolbar: '#toolbar' },
theme: 'snow'
});
}
}
}
My tab is basically like this:
<div id="composer" v-if="tabName === 'compose'">
<!-- toolbar container -->
<div id="toolbar">
<button class="ql-bold">Bold</button>
<button class="ql-italic">Italic</button>
</div>
<!-- editor container -->
<div id="editor">
<p>Hello World!</p>
</div>
</div>
Currently, I'm getting an error because the #editor element does not yet exist when I am calling new Quill(...). How do I delay that QuillJS initialization on the page so that it doesn't happen until after the #editor is already there?
Use mounted hook.
mounted: function () {
// Code that will run only after the
// entire view has been rendered
}
Use this.$nextTick() to defer a callback to be executed after the next DOM update cycle (e.g., after changing a data property that causes a render-update).
For example, you could do this:
methods: {
setTab: function (tabName) {
this.view = tabName;
if(tabName === 'compose') {
this.$nextTick(() => {
var editor = new Quill('#editor', {
modules: { toolbar: '#toolbar' },
theme: 'snow'
});
})
}
}
}
A clean way to do this is not to rely on selectors but make Quill editor a self-contained component:
<template>
<div class="quill-editor">
<!-- toolbar container -->
<div ref="toolbar">
<button class="ql-bold">Bold</button>
<button class="ql-italic">Italic</button>
</div>
<!-- editor container -->
<div ref="editor">
<p>Hello World!</p>
</div>
</div>
</template>
<script>
...
name: "QuillEditor",
mounted() {
this.quill = new Quill(this.$refs.editor, {
modules: { toolbar: this.$refs.toolbar },
theme: 'snow'
});
}
...
</script>

vue.js – get new data information

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>

How to use same template on different components in vue js?

Currently I am using as
<template> ... </template>
<script src="test1.js"> </script>
Where all my business logics are written in test1.js. Now I need to use test2.js with same template. I need to reuse the template with different component.
My current code goes like this...
common.vue
<template>
<div>
{{ page }}
</div>
<template>
<script src="../scripts/test1.js"></script>
Where in test1.js
export default {
name: 'home',
components: {
test: test
},
data() {
return {
page: "test1",
}
}
}
But also i need to use common.vue in my test2.js. Which i am not able to import both the .js seperately.
In angular we can write the fallowing late bindig which binds .html and .js(in ui.router)
.state('home', {
url:'/',
templateUrl: 'templates/home.html',
controller: 'HomeController'
})
Is there something similar to this?
You can share the template through the id attribute.
Vue.component('my-comp1', {
template: '#generic',
data () {
return {
text: 'I am component 1'
}
}
})
Vue.component('my-comp2', {
template: '#generic',
data () {
return {
text: 'I am component 2'
}
}
})
new Vue({
el: '#app'
})
<div id="app">
<my-comp1></my-comp1>
<my-comp2></my-comp2>
</div>
<template id="generic">
<div>
<p>{{ text }}</p>
</div>
</template>
<script src="https://unpkg.com/vue"></script>