vuejs serverPrefetch component data persists? - vue.js

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

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!

Vue: Reset data object to original value after API call has failed

I have a Vue component which fetches data from a remote API passing it to a child component. When the user clicks a button, the API is called to submit the data. The API returns the updated data object and updates the component with the new data. So far, so good.
But I'm struggling for the error case. If the API call is not successful, I need to "reset" the apiData object to the initial state as received in the mounted() function. Otherwise the user would still see the values she changed but which actually failed to update.
Two apporaches come to my mind:
Refresh the data from the API for the error case
Copy the originally received data to a variable which then will be re-assigned in the error case
Or maybe there is some more "Vue-ish" way to achieve this?
<template>
<some-component v-model="apiData"></some-component>
</template>
data() {
return {
apiData: {}
}
},
mounted() {
this.apiData = ApiService.getData();
},
methods: {
async onSubmit() {
try {
const response = await ApiService.update(this.apiData);
this.apiData = response.data;
} catch (e) {
showErrorNotification();
// How to reset `apiData` to the initial state as in mounted() ???
}
}
}
The API gets an error and does not return us any new results.
If it doesn't return new results, we still have our old data (the data you want)
Aren't we waiting for this?
If so, what's the problem for us?

Vuejs setting the data is not reactive

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.

How to understand vue router navigation?

I am working with this piece of code:
<script>
import router from '../router'
export default {
name: 'Page2',
data () {
return {
id: 0,
msg: 'Hey Nic Raboy'
}
},
created() {
this.id = this.$route.params.id;
},
methods: {
navigate() {
router.go(-1);
}
}
}
</script>
This is printed on the second page Template B if you will but I am confused on the navigate portion. Specifically this:
navigate() {
router.go(-1);
}
I have not worked with VueJS router before, can anyone please explain what this code section does?
Vue-router package works in the same way with the HTML5 History mode as it stated in the vue-router documentation
This method takes a single integer as parameter that indicates by how
many steps to go forwards or go backwards in the history stack,
similar to window.history.go(n).
So basically this is what you should expect from router.go()
// go back by one record, the same as history.back()
router.go(-1)
You can also learn more about HTML5 History mode from here
Have a look at the router.go(n) documentation in the section on programmatic navigation. The example explains that n can be positive or negative and indicates the number of steps in the browser's history to move.
// go forward by one record, the same as history.forward()
router.go(1)
// go back by one record, the same as history.back()
router.go(-1)
So router.go(-1) is the equivalent of hitting the "back" button in your browser.

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)
}