Vuejs setting the data is not reactive - vue.js

am completely new to Vuejs, sorry if this is a stupid question.
This is a nuxt app and am using an IntersectionObserver and depends on the element visibility am trying to change the internal state (data). but its not reactive unless i hit the refresh in vue dev tools.
so this is my approach
async mounted(){
let options = {
root: document.querySelector('#scroll-root'),
rootMargin: '0px',
threshold: 1.0
}
const testimonialStart:any = document.querySelector('#testimonial-start')
let startObserver = new IntersectionObserver(((entries,observer)=>{
entries.forEach((entry)=>{
if(entry.isIntersecting){
this.updateTesti(false)
}
else{
this.updateTesti(true)
}
})
}), options);
startObserver.observe(testimonialStart)
}
in the methods
updateTesti(st:boolean){
this.testiPrev = st
console.log(st,'state')
},
in the data
data(){
return {
testiPrev:false,
}
}
there is no issues in the intersection observer, in the console.log am getting the boolean value as expected.
what should i need to do to get reactivity here?
temporary solution:
I found that if I add testiPrev like below inside watch am getting the reactivity.
watch: {
testiPrev: function(){
}
},
this made me to ask one more question, do we need to explicitly include all the properties inside watch to achieve reactivity, if any better way please let me know.

Related

Property accessed during render but not defined in VUE

I receive an error when I load the page
My code looks like this :
<script>
methods: {
getPicture() {
var base = this;
axios
.get("http://localhost:3000/pictures/" + this.username)
.then(function (response) {
const { pictureData } = response.data;
base.studentImage = "data:image/jpg; base64," + pictureData;
console.log(base.studentImage);
});
},
}, //END OF METHOD
</script>
I want to display image in My front end. Where do I make a mistake ? Thank you in advance.
Welcome to Stack Overflow!
Sadly, I can't comment on this for more clarity, so my response has to be an answer.
This error occurs because the DOM and/or Vue Instance and/or Template uses the value "studentImage" before it is made available or defined incorrectly.
I recommend removing the variable "base" because Vue is very picky with its use of "this," so you usually wanna stay away from explicitly assigning "this" to a variable. As commented by Estus Flask below, this even applies to common JS. Check here for more information on referring to the correct "this" in JS.
Honestly, from what I can see with the code you've shared, that's likely the cause of your error. You'll want to define "studentImage" within your data object so Vue is aware of its existence.
Here is an example:
data: () => ({
studentImage: '',
}),
methods: {
getPicture() {
axios
.get("http://localhost:3000/pictures/" + this.username)
.then(function (response) {
const { pictureData } = response.data;
this.studentImage = "data:image/jpg; base64," + pictureData;
console.log(this.studentImage);
});
},
},
If it is not, then you'll want to make sure you're checking for "studentImage" before using it within the instance and or calling the method "getPicture," in the appropriate lifecycle.
I'm answering this assuming you're using the latest version of Vue, version three.
If this helps, please let me know!

vuejs serverPrefetch component data persists?

Stupid question but I haven't found a piece of doc that confirms it.
With vuejs ssr, if a value is set on a component data during serverPrefetch, is it meant to persist on client side?
export default {
data () {
return {
count:0
}
},
serverPrefetch(){
this.count = 1
return Promise.resolve(this.count)
},
mounted(){
// can I rely on this?
if(count===0){
// do stuff
}
}
}
My test shows that count is always zero on page refresh. If someone can please confirm this is standard behaviour and possibly explain why and/or direct me to a documentation about it.
Thank you

Vuetify Centralize Rules [duplicate]

The following code has been written to handle an event after a button click
var MainTable = Vue.extend({
template: "<ul>" +
"<li v-for='(set,index) in settings'>" +
"{{index}}) " +
"{{set.title}}" +
"<button #click='changeSetting(index)'> Info </button>" +
"</li>" +
"</ul>",
data: function() {
return data;
}
});
Vue.component("main-table", MainTable);
data.settingsSelected = {};
var app = new Vue({
el: "#settings",
data: data,
methods: {
changeSetting: function(index) {
data.settingsSelected = data.settings[index];
}
}
});
But the following error occurred:
[Vue warn]: Property or method "changeSetting" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option. (found in <MainTable>)
Problem
[Vue warn]: Property or method "changeSetting" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option. (found in <MainTable>)
The error is occurring because the changeSetting method is being referenced in the MainTable component here:
"<button #click='changeSetting(index)'> Info </button>" +
However the changeSetting method is not defined in the MainTable component. It is being defined in the root component here:
var app = new Vue({
el: "#settings",
data: data,
methods: {
changeSetting: function(index) {
data.settingsSelected = data.settings[index];
}
}
});
What needs to be remembered is that properties and methods can only be referenced in the scope where they are defined.
Everything in the parent template is compiled in parent scope; everything in the child template is compiled in child scope.
You can read more about component compilation scope in Vue's documentation.
What can I do about it?
So far there has been a lot of talk about defining things in the correct scope so the fix is just to move the changeSetting definition into the MainTable component?
It seems that simple but here's what I recommend.
You'd probably want your MainTable component to be a dumb/presentational component. (Here is something to read if you don't know what it is but a tl;dr is that the component is just responsible for rendering something – no logic). The smart/container element is responsible for the logic – in the example given in your question the root component would be the smart/container component. With this architecture you can use Vue's parent-child communication methods for the components to interact. You pass down the data for MainTable via props and emit user actions from MainTable to its parent via events. It might look something like this:
Vue.component('main-table', {
template: "<ul>" +
"<li v-for='(set, index) in settings'>" +
"{{index}}) " +
"{{set.title}}" +
"<button #click='changeSetting(index)'> Info </button>" +
"</li>" +
"</ul>",
props: ['settings'],
methods: {
changeSetting(value) {
this.$emit('change', value);
},
},
});
var app = new Vue({
el: '#settings',
template: '<main-table :settings="data.settings" #change="changeSetting"></main-table>',
data: data,
methods: {
changeSetting(value) {
// Handle changeSetting
},
},
}),
The above should be enough to give you a good idea of what to do and kickstart resolving your issue.
Should anybody land with the same silly problem I had, make sure your component has the 'data' property spelled correctly. (eg. data, and not date)
<template>
<span>{{name}}</span>
</template>
<script>
export default {
name: "MyComponent",
data() {
return {
name: ""
};
}
</script>
In my case the reason was, I only forgot the closing
</script>
tag.
But that caused the same error message.
If you're experiencing this problem, check to make sure you don't have
methods: {
...
}
or
computed: {
...
}
declared twice
It's probably caused by spelling error
I got a typo at script closing tag
</sscript>
Remember to return the property
Another reason of seeing the Property "search" was accessed during render but is not defined on instance is when you forget to return the variable in the setup(){} function
So remember to add the return statement at the end:
export default {
setup(){
const search = ref('')
//Whatever code
return {search}
}
}
Note: I'm using the Composition API
Adding my bit as well, should anybody struggle like me, notice that methods is a case-sensitive word:
<template>
<span>{{name}}</span>
</template>
<script>
export default {
name: "MyComponent",
Methods: {
name() {return '';}
}
</script>
'Methods' should be 'methods'
If you use two times vue instance. Then it will give you this error. For example in app.js and your own script tag in view file. Just use one time
const app = new Vue({
el: '#app',
});
I got this error when I tried assigning a component property to a state property during instantiation
export default {
props: ['value1'],
data() {
return {
value2: this.value1 // throws the error
}
},
created(){
this.value2 = this.value1 // safe
}
}
My issue was I was placing the methods inside my data object. just format it like this and it'll work nicely.
<script>
module.exports = {
data: () => {
return {
name: ""
}
},
methods: {
myFunc() {
// code
}
}
}
</script>
In my case, I wrote it as "method" instead of "methods". So stupid. Wasted around 1 hour.
Some common cases of this error
Make sure your component has the data property spelled correctly
Make sure your template is bot defined within another component’s template.
Make sure you defined the variable inside data object
Make sure your router name in string
Get some more sollution
It is most likely a spelling error of reserved vuejs variables. I got here because I misspelled computed: and vuejs would not recognize my computed property variables. So if you have an error like this, check your spelling first!
I had two methods: in the <script>, goes to show, that you can spend hours looking for something that was such a simple mistake.
if you have any props or imported variables (from external .js file) make sure to set them properly using created like this;
make sure to init those vars:
import { var1, var2} from './constants'
//or
export default {
data(){
return {
var1: 0,
var2: 0,
var3: 0,
},
},
props: ['var3'],
created(){
this.var1 = var1;
this.var2 = var2;
this.var3 = var3;
}
In my case it was a property that gave me the error, the correct writing and still gave me the error in the console. I searched so much and nothing worked for me, until I gave him Ctrl + F5 and Voilá! error was removed. :'v
Look twice the warning : Property _____ was accessed during render but is not defined on instance.
So you have to define it ... in the data function for example which commonly instantiate variables in a Vuejs app. and, it was my case and that way the problem has been fixed.
That's all folk's !
In my case, I forgot to add the return keyword:
computed: {
image(){
this.productVariants[this.selectedVariant].image;
},
inStock(){
this.productVariants[this.selectedVariant].quantity;
}
}
Change to:
computed: {
image(){
return this.productVariants[this.selectedVariant].image;
},
inStock(){
return this.productVariants[this.selectedVariant].quantity;
}
}
In my case due to router name not in string:
:to="{name: route-name, params: {id:data.id}}"
change to router name in string:
:to="{name: 'router-name', params: {id:data.id}}"
In my case I was trying to pass a hard coded text value to another component with:
ChildComponent(:displayMode="formMode")
when it should be:
ChildComponent(:displayMode="'formMode'")
note the single quotes to indicate text instead of calling a local var inside the component.
If you're using the Vue3 <script setup> style, make sure you've actually specified setup in the opening script tag:
<script setup>
I had lapsed into old habits and only created a block with <script>, but it took a while to notice it.
https://v3.vuejs.org/api/sfc-script-setup.html
Although some answers here maybe great, none helped my case (which is very similar to OP's error message).
This error needed fixing because even though my components rendered with their data (pulled from API), when deployed to firebase hosting, it did not render some of my components (the components that rely on data).
To fix it (and given you followed the suggestions in the accepted answer), in the Parent component (the ones pulling data and passing to child component), I did:
// pulled data in this life cycle hook, saving it to my store
created() {
FetchData.getProfile()
.then(myProfile => {
const mp = myProfile.data;
console.log(mp)
this.$store.dispatch('dispatchMyProfile', mp)
this.propsToPass = mp;
})
.catch(error => {
console.log('There was an error:', error.response)
})
}
// called my store here
computed: {
menu() {
return this.$store.state['myProfile'].profile
}
},
// then in my template, I pass this "menu" method in child component
<LeftPanel :data="menu" />
This cleared that error away. I deployed it again to firebase hosting, and voila!
Hope this bit helps you.
It seems there are many scenarios that can trigger this error. Here's another one which I just resolved.
I had the variable actionRequiredCount declared in the data section, but I failed to capitalize the C in Count when passing the variable as a params to a component.
Here the variable is correct:
data: () => {
return{
actionRequiredCount: ''
}
}
In my template it was incorrect (notd the no caps c in "count"):
<MyCustomModule :actionRequiredCount="actionRequiredcount"/>
Hope this helps someone.
Most people do have an error here because of:
a typo or something that they forgot to declare/use
the opposite, did it in several places
To avoid the typo issues, I recommend always using Vue VSCode Snippets so that you don't write anything by hand by rather use vbase, vdata, vmethod and get those parts generated for you.
Here are the ones for Vue3.
You can of course also create your own snippets by doing the following.
Also make sure that you're properly writing all the correct names as shown here, here is a list:
data
props
computed
methods
watch
emits
expose
As for the second part, I usually recommend either searching the given keyword in your codebase. So like cmd + f + changeSetting in OP's case to see if it's missing a declaration somewhere in data, methods or alike.
Or even better, use an ESlint configuration so that you will be warned in case you have any kind of issues in your codebase.
Here is how to achieve such setup with a Nuxt project + ESlint + Prettier for the most efficient way to prevent bad practices while still getting a fast formatting!
One other common scenario is:
You have a component (child) extending another component (parent)
You have a property or a method xyz defined under methods or computed on the parent component.
Your are trying to use parent's xyz, but your child component defines its own methods or computed
Sample code with the problem
// PARENT COMPONENT
export default {
computed() {
abc() {},
xyz() {} // <= needs to be used in child component
},
...
}
// CHILD COMPONENT
export default {
extends: myParentComponent,
computed() {
childProprty1() {},
childProprty2() {}
}
}
The solution
In this case you will need to redefine your xyz computed property under computed
Solution 1:
Redefine xyz and copy the code from the parent component
// CHILD COMPONENT
export default {
extends: myParentComponent,
computed() {
xyz() {
// do something cool!
},
childProprty1() {},
childProprty2() {}
}
}
Solution 2
Redefine xyz property reusing parent component code (no code redundancy)
// CHILD COMPONENT
export default {
extends: myParentComponent,
computed() {
xyz() {
return this.$parent.$options.computed.xyz
},
childProprty1() {},
childProprty2() {}
}
}
For me it happened because I wrote method: instead of methods: (plural). It's a silly mistake but it can happen :)
In my case it was the methods: { } I had put the } before my method functions so for example I had it like this methods: { function , function }, function, function so some of the functions that were out of the curly braces were not included inside the methods function.

Init data from Ajax Request Vue2

Vue.component('config_input', {
template: '#config-input-template',
data() {
paterns: []
},
computed: {
testData() {
return this.paterns;
}
},
mounted() {
var self = this;
axios.get('/listPatern').then(function (response) {
self.paterns = response.data;
}
},
});
In the console show testData is an empty array. It means that inside success ajax request function the 'paterns' value didn't update yet.
Can anybody show me how to assign data response to 'paterns' value?
I have tried using axios for ajax in vue.
Unfortunately I wasted 3 hours because it did not matter what code I tried, the data was simply not being initialized.
I resorted to vue-resource and my code was working the first time!
Include the vue-resource library and change your mounted method like so:
mounted() {
var self = this;
this.$http.get('/listPatern').then(function (response) {
self.paterns = response.data;
}
}
You could read VueJS Tutorials carefully.
When you pass a plain JavaScript object to a Vue instance as its data option, Vue will walk through all of its properties and convert them to getter/setters using Object.defineProperty. This is an ES5-only and un-shimmable feature, which is why Vue doesn’t support IE8 and below.
The getter/setters are invisible to the user, but under the hood they enable Vue to perform dependency-tracking and change-notification when properties are accessed or modified. One caveat is that browser consoles format getter/setters differently when converted data objects are logged, so you may want to install vue-devtools for a more inspection-friendly interface.
The reactive data which VueJS do something like "proxy",thus you couldn't assign to value in Object.
// Wrong
self.paterns = response.data;
// Right
self.someObject = Object.assign({}, self.someObject, response.data)
And data should be a Object
data: {
patterns: []
}

VueJS reactive binding to module export

I'm new to Vue and I'm trying to bind a component value to a property of an exported object. The initial value is set correctly but it's not reactive. I'm not sure I'm using the right terminology, but the relevant sections are
// Settings.js
export const settings = { showOverlay: true }
// Overlay.vue
<template>
<div v-show="enabled"> Some stuff </div>
</template>
<script>
import { settings } from "../js/Settings.js";
export default {
data() {
return {
enabled: settings.showOverlay
};
}
};
</script>
Now, I know that the exported object (settings) is a read-only view onto the object, because that's how modules work, so probably Vue can't put its hooks into it. The thing is, I want the setting to be "owned" by this Settings service, which is responsible for persisting the values between page loads, but I don't feel like the service should have to be aware that the component wants to watch a value and take care of manually triggering updates on the component when the value changes -- I probably just misunderstand the pattern I'm supposed to use for cases like this.
This is being built with Webpack / babel, if that makes any difference.
I'm feeling a little bit sheepish at the moment. I went down a little rabbit hole based on some syntax I saw in your question and that let to a whole bunch of unnecessary gyrations. The syntax was this:
data() {
return {
enabled: settings.showOverlay
};
}
Which, for some reason, I interpreted as "well sure, whenever enabled changes, settings.showOverlay is going to change because Vue is reactive".
Yeah, no.
In that code, settings.showOverlay is just the initial value for the enabled property. The enabled property will be reactive, but in no way is it going to pass values to the settings object. Basically the data function returns an object with an enabled property that has an initial value of whatever settings.showOverlay is and then that object is turned into a reactive object.
If you want the changes made in Vue to be passed along to your settings object then all you need to do is expose the settings object on Vue's data object.
data() {
return {
settings,
};
}
Now if you have code like
<div v-show="settings.showOverlay"> Some stuff </div>
<button #click="settings.showOverlay= !settings.showOverlay"></button>
settings.showOverlay will not only be reactive in the Vue, but in the settings object. No need for any of the hoops I jumped through below (/facepalm).
FWIW I believe some of the links I mentioned in the comments are referring to the data object itself. The data object needs to be a plain javascript object, not necessarily all the properties on it.
In other words, in
data() {
return something
}
something must be a plain javascript object.
Original Answer
I've done this in a couple ways in my Vue apps. In my first app I wanted to do the same thing, store the settings in an external module that could manage persisting the settings and expose those settings on my Vue. I ended up writing a class that looks like this.
class Settings {
constructor(){
// read settings from persisted solution
}
get(key){
// return "key" from settings
}
set(key){
// set "key" in settings
}
save(){
// save settings to persisted solution
}
}
export default Settings
And then used that in my Vue like this.
import Settings from "./settings"
new Vue({
data:{
someSetting: Settings.get("someSetting")
}
})
And then some point later, trigger set() and save(). That point for me was whenever a route change was triggered, I'd just set all the settings back to the Settings object and then save.
It sounds like what you have is you're exporting an object that has getter/setter properties possibly something like this.
export const settings = {
overlay: stored.showOverlay,
get showOverlay(){
return this.overlay
},
set showOverlay(v){
this.overlay = v
}
}
Where you maybe trigger a save when set is triggered. I like that idea better than the solution I described above. But getting it to work is a little more work. First I tried using a computed.
new Vue({
computed:{
showOverlay: {
get(){ return settings.showOverlay }
set(v) { settings.showOverlay = v }
}
}
})
But that doesn't quite work because it doesn't reflect changes to the Vue. That makes sense because Vue doesn't really know the value changed. Adding a $forceUpdate to the setter doesn't work either, I expect because of the caching nature of computed values. Using a computed in combination with a data property, however, does work.
new Vue({
data(){
return {
showOverlay_internal: settings.showOverlay
}
},
computed:{
showOverlay: {
get(){ return this.showOverlay_internal }
set(v) {
settings.showOverlay = v
this.showOverlayInternal = v
}
}
}
})
That changes both the state of the Vue and triggers the change in the settings object (which in turn can trigger persisting it).
But, damn, that's a lot of work.
It's important to remember sometimes, though, that the objects we use to instantiate Vue are just plain old javascript objects and we can manipulate them. I wondered if I could write some code that creates the data property and the computed value for us. Taking a cue from Vuex, yes we can.
What I ended up with was this.
import {settings, mapSetting} from "./settings"
const definition = {
name:"app"
}
mapSetting(definition, "showOverlay"
export default definition
mapSetting does all the work we did above for us. showOverlay is now a computed property that reacts to changes in Vue and updates our settings object. The only drawback at the moment is that it exposes a showOverlay_internal data property. I'm not sure how much that matters. It could be improved to map multiple properties at a time.
Here is the complete code I wrote that uses localStorage as a persistence medium.
function saveData(s){
localStorage.setItem("settings", JSON.stringify(s))
}
let stored = JSON.parse(localStorage.getItem("settings"))
if (null == stored) {
stored = {}
}
export const settings = {
overlay: stored.showOverlay,
get showOverlay(){
return this.overlay
},
set showOverlay(v){
this.overlay = v
saveData(this)
}
}
function generateDataFn(definition, setting, internalName){
let originalDataFn = definition.data
return function(){
let data = originalDataFn ? originalDataFn() : {}
data[internalName] = settings[setting]
return data
}
}
function generateComputed(internalName, setting){
return {
get(){
return this[internalName]
},
set(v){
settings[setting] = v
this[internalName] = v
}
}
}
export function mapSetting(definition, setting){
let internalName = `${setting}_internal`
definition.data = generateDataFn(definition, setting, internalName)
if (!definition.computed)
definition.computed = {}
definition.computed[setting] = generateComputed(internalName, setting)
}