Vue Js show and hide a div with a Toggle - vue.js

Hey all so I am brand new into the world of Vue.
I am building an app With a Rails API back end and Vue front end.
I am trying use a Toggle Switch (found in this repo Git Hub ) to send true attribute back to my model and open a hidden div on the page.
Right now I am getting these errors and i have no idea how to proceed. I dont think i am understanding their docs as I've read them but not really understanding what i need to do..:
vue.esm.js?efeb:1897 RangeError: Maximum call stack size exceeded
at VueComponent.Vue._render (vue.esm.js?efeb:3553)
at VueComponent.updateComponent (vue.esm.js?efeb:4069)
at Watcher.get (vue.esm.js?efeb:4482)
at new Watcher (vue.esm.js?efeb:4471)
at mountComponent (vue.esm.js?efeb:4076)
at VueComponent.Vue.$mount (vue.esm.js?efeb:9057)
at VueComponent.Vue.$mount (vue.esm.js?efeb:11953)
at init (vue.esm.js?efeb:3127)
at createComponent (vue.esm.js?efeb:5983)
at createElm (vue.esm.js?efeb:5930)
vue.esm.js?efeb:1897 RangeError: Maximum call stack size exceeded
at VueComponent.Vue._render (vue.esm.js?efeb:3553)
at VueComponent.updateComponent (vue.esm.js?efeb:4069)
at Watcher.get (vue.esm.js?efeb:4482)
at new Watcher (vue.esm.js?efeb:4471)
at mountComponent (vue.esm.js?efeb:4076)
at VueComponent.Vue.$mount (vue.esm.js?efeb:9057)
at VueComponent.Vue.$mount (vue.esm.js?efeb:11953)
at init (vue.esm.js?efeb:3127)
at createComponent (vue.esm.js?efeb:5983)
at createElm (vue.esm.js?efeb:5930)
Here is my AppToggle.vue
<template>
<div>
<AppToggle v-model="isToggleOn" onText="Hide Map" offText="Show Map"/>
</div>
</template>
<script>
export default {
name: "AppToggle",
data() {
return {
isToggleOn: true
};
}
};
</script>
and here is my Signup.vue component where the toggle is called:
<template>
... Some form stuff up here...
<app-toggle #click.prevent="toggleSmsDiv()"/>
<div id="smsDiv" v-if="isToggleOn">TEST DIV ITEMS</div>
... More form stuff down here...
</template>
<script>
import AppToggle from "#/components/AppToggle";
export default {
name: "Signup",
components: {
AppToggle
},
data() {
return {
isToggleOn: false,
first_name: "",
last_name: "",
email: "",
password: "",
password_confirmation: "",
error: ""
};
},
created() {
this.checkSignedIn();
},
updated() {
this.checkSignedIn();
},
methods: {
toggleSmsDiv() {
this.isToggleOn = !this.isToggleOn;
},
signup() {
this.$http.plain
.post("/signup", {
email: this.email,
password: this.password,
password_confirmation: this.password_confirmation
})
.then(response => this.signupSuccessful(response))
.catch(error => this.signupFailed(error));
},
signupSuccessful(response) {
if (!response.data.csrf) {
this.signupFailed(response);
return;
}
localStorage.csrf = response.data.csrf;
localStorage.signedIn = true;
this.error = "";
this.$router.replace("/products"); // Change this to User Dashboard
},
signupFailed(error) {
this.error =
(error.response && error.response.data && error.response.data.error) ||
"Something went wrong. Please try again.";
delete localStorage.scrf;
delete localStorage.signedIn;
},
checkSignedIn() {
if (localStorage.signedIn) {
this.$router.replace("/products"); //Change this to User Dashboard
}
}
}
};
</script>
<style>
</style>

You got Maximum call stack size exceeded because of you are using your AppToggle component inside AppToggle component which that cause the recursively call itself.
I'm not sure how do you import this package since I can't find it on npm. It seems the author of this package want us to copy TailwindToggle.vue manually.
So your AppToggle.vue would be:
// Same as TailwindToggle.vue
<template>
...
</template>
<script>
...
</script>
<style lang="postcss"> // Make sure you Vue config support postcss` language
...
</style>
And your Signup.vue would be:
<template>
...
<AppToggle v-model="isToggleOn" onText="Hide Map" offText="Show Map"/>
<div id="smsDiv" v-if="isToggleOn">TEST DIV ITEMS</div>
...
</template>
...
I'm not sure this will works since the style of TailwindToggle seems have to import some pieces from somewhere else (not sure). If it not working may be you can looking at its dist file and copy involved style and paste it into yours AppToggle.vue. But if it possible I would recommend you to another package instead.
Hope this helps.

Related

Getting "Cannot read properties of undefined (reading 'commit')"NUXT

I am new to Vue and stuck. I am trying to send user input data from a form into a vuex store. From that vuex store, an action will be called (fetching from API) and I would like that data back into my app and components.
<template>
<div>
<h1>APP NAME</h1>
<form action="submit" #submit.prevent="sendCityName()">
<label for="query"></label>
<input
type="text"
id="query"
v-model="cityName"
>
<button type="submit">Submit</button>
</form>
<h3>{{ lat }}</h3>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
data() {
return {
cityName: ''
}
},
computed: {
coordinates () {
return this.$store.state.lat
}
},
methods: {
sendCityName() {
this.$store.commit('fetchCity', this.cityName)
}
},
}
</script>
Here is my index.vue and getting the error "Cannot read properties of undefined (reading 'commit')"
here is my store.js. I want to use the lat and lon across my app.
export const state = () => ({
lat: '',
lon: ''
})
export const mutations = {
SET_LAT(state, payload){
state.lat = payload
},
SET_LON(state, payload){
state.lon = payload
}
}
export const actions = {
async fetchCity({ commit }, cityName) {
// make request
axios.get(
`https://api.openweathermap.org/geo/1.0/direct`, {
params: {
appid: "xxxxxxx",
q: cityName,
}
}).then((response) => {
commit('SET_LAT', response.data[0].lat);
commit('SET_LON', response.data[0].lng);
});
},
};
When I button submit I get the error "Cannot read properties of undefined (reading 'commit')"
Here is my working repo with the fixes mentioned below.
There are 3 things in your code:
remove vuex from package.json and run yarn again, that one is already baked into Nuxt as stated in the official documentation, those are the only steps needed
all the files inside of store will be namespaced by default for you, since you do have store/store.js, the proper syntax will be
async sendCityName() {
await this.$store.dispatch('store/fetchCity', this.cityName) // 👈🏻 store prefix
}
since you do use the axios module, you should have the following in your action (using the async/await syntax since it's more modern and preferable)
async fetchCity({ commit }, cityName) {
const response = await this.$axios.get(
`https://api.openweathermap.org/geo/1.0/direct`, {
params: {
appid: "3d91ba5b3c11d13158a2726aab902a0b",
q: cityName,
}
})
commit('SET_LAT', response.data[0].lat)
commit('SET_LON', response.data[0].lng)
}
Looking at the browser's console, you also have some errors to fix.
I can also recommend an ESlint + Prettier configuration so that you keep your code error-proof + properly formatted at all times.

TypeError: Cannot read properties of undefined (reading '__ob__') in Nuxt

The error appears when navigating from one page with the ProductCard component to another.
I believe the error comes from the data fetching or the mounted(), but I haven't been able to solve it. The ProductCard component is just a visual one with some props. So the error must be here.
Full error:
client.js:228 TypeError: Cannot read properties of undefined (reading '__ob__')
at VueComponent.Vue.$destroy (vue.runtime.esm.js:4004:18)
at destroy (vue.runtime.esm.js:3175:27)
at invokeDestroyHook (vue.runtime.esm.js:6148:59)
at invokeDestroyHook (vue.runtime.esm.js:6153:9)
at invokeDestroyHook (vue.runtime.esm.js:6153:9)
at invokeDestroyHook (vue.runtime.esm.js:6153:9)
at VueComponent.patch [as __patch__] (vue.runtime.esm.js:6501:30)
at VueComponent.Vue.$destroy (vue.runtime.esm.js:4010:8)
at destroy (vue.runtime.esm.js:3175:27)
at invokeDestroyHook (vue.runtime.esm.js:6148:59)
My page .vue file template:
<template>
<main>
<ProductTabs></ProductTabs>
<div
v-if="productsLoading"
class="spinner-border"
style="width: 3rem; height: 3rem"
role="status"
>
<span class="sr-only">Loading...</span>
</div>
<v-container v-else fluid>
<v-row d-flex justify="center">
<ProductCard
v-for="product in products"
:key="product._id"
:product-title="product.productName"
:product-price="product.price"
:product-img1="product.img1"
:product-img2="product.img2"
></ProductCard>
<br />
</v-row>
</v-container>
</main>
</template>
My page .vue file script:
<script>
export default {
path: '/',
name: 'ProductsPage',
components: { ProductTabs },
// variables
data() {
return {
products: [],
productsLoading: false,
}
},
// call the get Poducts method
mounted() {
this.getAllProducts()
},
// get products from api and save into products array
methods: {
async getAllProducts() {
this.productsLoading = true
try {
const data = await this.$axios.$get('api/products')
this.products = data
this.productsLoading = false
return this.products
} catch (err) {
this.productsLoading = false
return err
}
},
},
}
</script>
I have the same issue, i resolved with keep-alive props on component:
<Nuxt keep-alive/>
More info on official doc
https://nuxtjs.org/docs/features/nuxt-components/
[Update]
You can also check if you have on some component:
data() {
return {
};
},
Seem it is the main problem
The issue was coming from a component hover event, nothing related to the code snippet above.
Maybe you can just use the created with the async functiion. or better still use the fetch provided by Nuxt.
asynce fetch() {
this.productsLoading = true
try {
const data = await this.$axios.$get('api/products')
this.products = data
this.productsLoading = false
} catch (err) {
this.productsLoading = false
return err
}
},
Also i dont think you will need a return in the promise

vue3: new value and old value returned by deep watcher always same

const app = {
data(){
return {
form: {
name: '',
password: ''
}
}
},
watch: {
form: {
handler(form, oldForm){
console.log(form, oldForm);
},
deep: true
}
}
}
Vue.createApp(app).mount('#app')
<script src="https://unpkg.com/vue#next"></script>
<main id="app">
<form>
<input v-model="form.name"/>
<input v-model="form.password"/>
</form>
</main>
I got a deep watcher for an object value, accroding to the document, it should get both the previous and current value of the object been watched.
But in this code, the new value and the old value returned by the deep watcher are always same. Why is that?
Thanks a lot for anyone help!
Ref: watch
Note: when mutating (rather than replacing) an Object or an Array and watch with deep option, the old value will be the same as new value because they reference the same Object/Array. Vue doesn't keep a copy of the pre-mutate value.
Use setup to watch instead...
import {reactive} from 'vue'
setup() {
const form = reactive({
name: '',
password: ''
})
watch(
() => ({ ...form }),
(newVal, oldVal) => {
console.log('Form changed', newVal, oldVal)
}
)
return {
form
}
}

How to bind a dynamic <img> id to a JavaScript event?

I am learning how to use a Javascript image annotation library called Annotorious. I want to integrate it into a Vue app and it works fine if I do this:
<img :src="photo.url" id="dog" :alt="photo.title" />
However, I am getting a console error if I do it like so:
<img :src="photo.url" :id="photo.id" :alt="photo.title" />
My full code for the SFC named PhotoDetail.vue: Codesandbox link
<template>
<div>
<h1>Test Photo</h1>
<img :src="photo.url" :id="photo.id" :alt="photo.title" />
</div>
</template>
<script>
import { Annotorious } from '#recogito/annotorious'
export default {
name: 'photoDetail',
data() {
return {
photo: {}
}
},
methods: {
async getPhoto() {
this.photo.url = await 'https://picsum.photos/id/237/400/'
this.photo.id = 'dog'
this.photo.title = 'nice dog'
},
annotatePhoto() {
try {
const anno = new Annotorious({
image: this.photo.id // image element or ID
})
// Attach listeners to handle annotation events
anno.on('createAnnotation', function(annotation) {
console.log('Created!', annotation)
})
} catch (error) {
console.log('anno error', error)
}
}
},
async created() {
await this.getPhoto()
},
mounted() {
this.annotatePhoto()
}
}
</script>
Console error:
anno error TypeError: Cannot read property 'nodeType' of undefined
at new e (annotorious.min.js:1)
at VueComponent.annotatePhoto (cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/components/PhotoDetail.vue?vue&type=script&lang=js&:51)
at VueComponent.mounted (cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/components/PhotoDetail.vue?vue&type=script&lang=js&:84)
at invokeWithErrorHandling (vue.runtime.esm.js:1853)
at callHook (vue.runtime.esm.js:4213)
at Object.insert (vue.runtime.esm.js:3136)
at invokeInsertHook (vue.runtime.esm.js:6336)
at VueComponent.patch [as __patch__] (vue.runtime.esm.js:6555)
at VueComponent.Vue._update (vue.runtime.esm.js:3942)
at VueComponent.updateComponent (vue.runtime.esm.js:4060)
Any advice? Thank you!
First, you need to make sure the img component is rendered before calling annotatePhoto function. That is, after getPhoto function:
async mounted() {
await this.getPhoto()
this.annotatePhoto()
}
You also need to change the way you update the photo variable like this, or the DOM won't update:
this.photo = {
url: await 'https://picsum.photos/id/237/400/',
id: 'dog',
title: 'nice dog',
}
Working example: sandbox

How To run a function in Vuejs after the component in created?

I have created a component which has a function which makes external API calls and then fills an array. I used created() life hook to run the function for the 1st time. I am passing a variable from the parent component into this component and then based upon this variable change I want the function to run again.
How do I achieve this.
Attaching my code below
<template>
<div>
<p>{{ data_to_show_on_mainpage }}</p>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'GetCategoryItemsAndDisplayOne',
props: ['categoriesfordisplay','ismainpage', 'catalogselected'],
data(){
return {
IsMainPage_1 : "",
data_to_show_on_mainpage : [],
}
},
watch: {
catalogselected: function(){
this.GetItemsToShowonMainPage()
}
},
methods:{
changevalue(){
console.log("i am reducing it to emplty after change of catalog")
this.IsMainPage_1 = this.catalogselected
this.data_to_show_on_mainpage = []
},
CatlogService(catlog_name,category,gender,mainpage){
let url = "http://localhost:5000/xyz/" + (this.catalogselected).replace(/'/g,"%27") +"/api/"+ (gender) + "/catalogvis/" + (category) +"/items"
console.log(encodeURI(url))
axios.get(encodeURI(url)).then((resp)=>{
this.data_to_show_on_mainpage.push(resp.data.response.Results.results[0])
})
.catch(err => {
console.log("we got an error the url is " + url)
console.log(err);
})
},
GetItemsToShowonMainPage(){
this.changevalue()
if(this.categoriesfordisplay.men_for_display.length>0){
for(let i =0;i<this.categoriesfordisplay.men_for_display.length;i++){
let category = this.categoriesfordisplay.men_for_display[i].replace(/"/g,"%27");
this.CatlogService(this.catalogselected,category,'men',this.ismainpage)
}
}
if(this.categoriesfordisplay.women_for_display.length>0){
for(let i = 0;i<this.categoriesfordisplay.women_for_display.length;i++){
let category = this.categoriesfordisplay.women_for_display[i].replace(/"/g,"");
this.CatlogService(this.catalogselected,category,'women',this.ismainpage)
}
}
},
},
created(){
this.GetItemsToShowonMainPage()
}
}
</script>
<style>
</style>
How Do i trigger the GetItemsToShowonMainPage() function whenever the catalogselected varaible is changed.
It looks fine.
As #a-lau says, make sure the parent is updating the catalogselected prop
Btw, you can write your watcher this way and remove completely the created hook:
watch: {
catalogselected: {
handler: "GetItemsToShowonMainPage",
immediate: true
}
}
If you still have issues you might want to write a minimal reproduction on https://codesandbox.io/s/vue