How to conditionally mount root vue instance? - vue.js

Is there a way, using vue, to check if the element where vue instance should be mounted actually exists? I have cases where this element would not exist and would like to avoid getting errors like these:
[Vue warn]: Cannot find element: #app
[Vue warn]: Error in created hook: "TypeError: Cannot convert undefined or null to object"
TypeError: this.$store.getters[(this.namespace...
The case is: I want to be able to check if the element actually exist before mounting vue. Don't try to mount if there is no element.

Just use vanilla JS to check for existing tag.
const storeMounted = new Vuex.Store({
state: {
string: "Store mounted"
}
})
if (document.getElementById('app')) {
new Vue({
el: "#app",
store: storeMounted,
computed: {
string() {
return this.$store.state.string
}
},
mounted() {
console.log('Mounted to #app')
}
})
}
const storeNotMounted = new Vuex.Store({
state: {
string: "Store not mounted"
}
})
if (document.getElementById('noApp')) {
new Vue({
el: "noApp",
store: storeNotMounted,
mounted() {
console.log('Mounted to #noApp')
}
})
}
<script src="https://cdn.jsdelivr.net/npm/es6-promise#4/dist/es6-promise.auto.js"></script>
<script src="https://unpkg.com/vuex#3.1.2/dist/vuex.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">{{string}}</div>
In the snippet above you can see that no errors are in the console, a Vue instance is mounted in a <div> that has an appropriate ID, and the other one is not mounted.
Vue is very useful - but it's "only" JavaScript :D
EDIT
I added Vuex, so you can see that it doesn't cause any problems.

Related

VueJs component using CDN link without npm

Want to use Laravel Vue Pagination
HTML
<ul>
<li v-for="user in users.data" :key="user.id">{{ user.name }}</li>
</ul>
<pagination :data="users" #pagination-change-page="getUsers"></pagination>
Vue/Js
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://unpkg.com/laravel-vue-pagination#2.3.1/dist/laravel-vue-pagination.umd.min.js"></script>
<script>
new Vue({
el: '#app',
data: {
users: [],
},
mounted() {
this.getUsers();
},
methods: {
getThoughts(page = 1) {
axios.get('/api/users?page=' + page)
.then(response => {
this.users = response.data;
});
}
}
});
</script>
Problem:
Loop is working fine when using the pagination component gives me the following error.
Error
[Vue warn]: Unknown custom element: <pagination> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
Tried
Vue.component('pagination', require('laravel-vue-pagination'));
Gives me following error
Uncaught ReferenceError: require is not defined
You are loading a component using UMD modules. When loading a UMD module through script tag, the module gets added to the window object. You can access it using window['module-name']. Then you need to register the component in Vue object just like u would do it normally.
In your case you need to add:
components: {
pagination: window['laravel-vue-pagination']
},
to your Vue component so umd module laravel-vue-pagination will be registered and ready to use inside template.

How to access the window object in vue js?

I have this vue js component:
<template>
<div>
hello world
</div>
</template>
<script>
export default {
name: 'mycomp',
data: function () {
console.error("window.google_recaptcha_public_key", window.google_recaptcha_public_key);
return {
}
},
mounted() {
let app = this;
console.error("window.google_recaptcha_public_key2", window.google_recaptcha_public_key);
},
}
</script>
<style scoped lang="scss">
</style>
returns:
window.google_recaptcha_public_key undefined
window.google_recaptcha_public_key2 undefined
where can I leave painless and happy all global configuration?
notice this configuration lives in my laravel backend. So I wont copy paste all values from the backend to the front end
U can use Vue.prototype in main.js file, or in file you import Vue
Vue.prototype.Hereanyname = window.hereanyname;
and in your Vue application, you can use it
Hereanyname.thefunction
Real example on Laravel
in main.js
import Vue from 'vue';
Vue.prototype.Routes = window.routes;
new Vue({
el: '#app',
template: '<App/>',
components: {App}
});
in your application
:href="Routes.route('laravel.route.here')"
So for your case
import Vue from 'vue';
Vue.prototype.GoogleRecaptcha = window.google_recaptcha_public_key;
new Vue({
el: '#app',
template: '<App/>',
components: {App}
});
inside application
mounted() {
console.log(this.GoogleRecaptcha)
}
In Vue3, you no longer have the global Vue instance, so you need to assign the window as a global property on your app...
// main.js
app.config.globalProperties.window = window
Then in your components, window will just work.
This info is from an impeccable source.
You should save your window variable in Vue.prototype
main.js
Vue.prototype.$authUser = window.authUser;
After that, you can access your data as follows:
Vue template
<div v-text="$authUser.name"></div>
Vue script
let name = this.$authUser.name;
window is available in the vuex store. This may help if you need to mutate the window property synchronously with other actions/mutations, give you a chance to validate what goes into it, or catch an error if the variable you intend to put there isn't available.
export default new Vuex.store({
state: {
windowFoo: window.foo,
googleRecaptcha: window.google_recaptcha_public_key
},
getters: {
windowFoo: (state) => state.windowFoo,
googleRecaptcha: (state) => state.googleRecaptcha
},
actions: {
barThenFooThenBaz({ commit }, { foo }) {
// ... do some other required actions first
commit("setWindowFoo", foo);
// ... do some dependent actions next
}
},
mutations: {
setWindowFoo(state, foo) {
state.windowFoo = foo;
}
}
});
Then from your Single File Component...
//....
computed: {
windowFoo() {
return this.$store.getters.windowFoo;
},
googleRecaptcha() {
return this.$store.getters.googleRecaptcha;
}
},
methods: {
async barThenFooThenBaz(foo) {
await this.$store.dispatch({
type: "barThenFooThenBaz",
foo: foo
});
// ... do something dependent on windowFoo being set
}
}
//....
Although the other answers here are totally acceptable, I've had issues using the Vue instance with Vue.prototype in main.js as our project has gotten larger, so I hope this helps!
Provide/Inject works nicely. Here's an example with Vue 3:
main.js
const app = createApp(App)
app.provide('recaptcha_key', window.google_recaptcha_public_key)
app.mount('#app')
MyComponent.vue
<script setup>
const { inject } from 'vue'
const recaptchaKey = inject('recaptcha_key')
</script>

Converting data to props for root component in Vue.js 2

This must be simple, but haven't cracked it...
In vue, I think I get how to pass props from parent to child components. And I understand that I have an app state in the data member of the vue instance. What I don't understand is how to get the data state into the root app as props.
It seems there are a few ways to organize a vue app, so here's what I'm trying to make work:
index.ts
import app from './app.vue'
export default new Vue({
// App Root Element
el: '#app',
render: (c) => c('app'),
components: {
app
},
data: {
someValue: 42
}
})
app.vue
<template>
<div>
Some Value: {{someValue}}
</div>
</template>
<script lang="ts">
export default {
props: ['someValue']
};
</script>
I assume it should be something like the following, but I don't know how to get a reference directly to the data - unless I keep a reference to it outside of vue for this purpose, but that seems like it should not be necessary:
render: (c) => c('app', { someValue: ??? }),
Use this to get data or property values inside your render method (or methods or computed values, etc). Don't use an arrow function to define your render function if you're going to use this inside it.
new Vue({
el: "#app",
render(c) {
return c('app', {props:{someValue: this.someValue}})
},
components: {
app
},
data: {
someValue: 42
}
})
Example.
console.clear()
const app = {
props: ["someValue"],
template: `<div>Some Value: {{someValue}}</div>`
}
new Vue({
el: "#app",
render(c) {
return c('app', {props:{someValue: this.someValue}})
},
components: {
app
},
data: {
someValue: 42
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="app">
</div>
As #RoyJ pointed out, this is the key section of the documentation.

Property or method "msg" is not defined on the instance but referenced during render

While trying out Vue and Vuex, i stumbled upon the following error message:
[Vue warn]: Property or method "msg" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
I fail to understand and solve this issue, mainly because msg is defined in the code under data. It's probably doesn't related directly to Vuex, but i faced it only when i started using Vuex.
Here is my code:
main.js:
import Vue from 'vue'
import App from './App.vue'
import { store } from './store.js'
Vue.component('app', App);
var vApp = new Vue({
el: '#app',
store,
render: h => h(App),
})
App.vue:
<template>
<div id="app">
<div v-text="msg"></div>
<input id="name-b" class="input" v-model="nameB" type="text" placeholder="Name B">
</div>
</template>
<script type = "text/javascript">
module.exports = {
name: 'app',
data() {
return {
msg: 'boooo'
}
},
computed: {
return {
nameB: {
get() {
this.$store.state.nameB
},
set(value) {
this.$store.commit('setName', value);
}
},
}
</script>
<style>
</style>
store.js:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
nameB: '',
},
mutations: {
setName: function(state, name) { state.locationName = name},
},
});
Thanks.
Problem solved.
It was a curly brackets issue and possibly the return in computed that is not needed...
This is a confusing error message.

What's the correct to modify VUE component via javascript?

javascript
(function(exports){
Vue.component('my-component',{
props: {
name: String
},
template: '<div>\
<h1>name: {{name}}</h1>\
</div>\
'
});
var myApp = new Vue({
el: '#myApp'
});
exports.app = myApp;
});
html
<div id="myApp">
<my-component name="yo!" ref="test"></my-component>
</div>
console or javascript to access component
app.$refs.test.name = "yo man";
Then console display Vue Warning as following.
What's the correct to modify VUE component via javascript?
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "name"
(found in component )
What you should be doing is binding the name directive to a variable in the parent app.
(function(exports){
Vue.component(
'my-component',
{
props: {
name: String
},
template: '<div><h1>name: {{name}}</h1></div>'
}
);
var myApp = new Vue(
{
el: '#myApp',
data: {
name: "Yo!"
}
}
);
exports.app = myApp;
}
))();
And your HTML
<div id="myApp">
<my-component v-bind:name="name" ref="test"></my-component>
<button v-on:click="name = 'Yo man'">Change</button>
</div>
JSFiddle