why can't use window in vue template? - javascript

i set a property in window Object for using it globally, like this:
window.configs.foo=true;
but when use it like this:
<v-btn v-if="window.configs.foo">go</v-btn>
in console, i get this error:
[Vue warn]: Property or method "window" 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.
how i can use window object in vueJs template?

Because v-if is intended to be used on properties of your component. You cannot v-if over variables in global scope, or outside your component data or properties.
What you can do, instead, if to setup a computed property on your component that targets window.configs.foo:
new Vue({ // or maybe a component, this depend on how you're using it
template: `<div>
<v-btn v-if="showMyButton">...</v-btn>
</div>`
computed: {
showMyButton() {
return window.configs && window.configs.foo;
}
}
})
UPDATE:
If you have to reuse this in a lot of sites, there are two things that you can do:
Vuex
Using vuex to set the showMyButton as a vuex state. Then you can access it via:
v-if="$store.state.showMyButton"
And you can modify it via standard vuex mutations.
 Mixins
Maybe for some reason you don't want to use vuex. Then a way to reuse logic across many components is to use mixins.
const configAwareMixin = {
computed: {
showButton() {
return window.configs.foo;
}
}
}
// And then in your component:
Vue.component('stuff', {
mixins: [buttonAwareMixin],
template: `<div><v-btn v-if="showButton"></v-btn></div>`
})

Well, Alternate is to use $root in Vue. Define foo at your vue instance and it will be available in all the component with this.$root.foo.
Here is the official docs
Hope this helps

The only easiest solution is Vue.prototype.$window = window; in main.js from folder src and use can use window as $window inside your template. Hope can help you all.

I solved it by replacing the 'vue.js' with 'vue.min.js'! I don't know why.

Related

Set "this" keyword of App.vue in Vuex state

I found a seloution for how to access to vue-router and other services that works only inside the vue component. In the created option of App.vue i save the "this" of vue app inside state of vuex.
does it's a good way or can make some problems in future?
instead to write:
this.$router.push('route-name')
from some component, is save the this keyword in the state as context key for example,
and now from the Actions i'm using the context instead this
such like this:
state.context.router.push('route-name')
and its works..
my question is if its good solution or not.
I think that is not a good solution, if you want to use this, you should try with the actions or getter returned value, inside a Vue file instead using it directly in an action.
I'm giving an example and an explanation
Example:
Getter:
export function getRedirect(state){
return state.routerName
}
Vue file:
<template>
Page
<button #onClick="redirect" > Redirect </button>
</template>
<script>
import {mapGetters} from 'vuex'
export default: {
computed:{
...mapGetters('VuexStoreName',['getRedirect'])
},
methods:{
redirect(){
this.$router.push(this.getRedirect)
}
}
}
</script>
So, instead of using the redirect inside an JS file, creating an action and passing this inside the parameter or getting it by state, you'll be using it correctly in a Vue file.
The probblem setting this in the state is that when it changes the instance saved will not change, and you'll have to update every time and everywhere. And calling this inside a Vue file, it'll always be updated

Vuejs - Issue when removing a component via directives and the mounted/created event is being executed

I wanted my directive to work as v-if since in my directive I have to check access rights and destroy the element if it does not have access.
Here is my code
Vue.directive('access', {
inserted: function(el, binding, vnode){
//check access
if(hasAccess){
vnode.elm.parentElement.removeChild(vnode.elm);
}
},
});
vue file
<my-component v-access='{param: 'param'}'>
The issue is that i'm applying this directive to a component, it's removing the component but not the execution of functions called by the created/mounted hook.
In the component(my-component) there are functions in mounted/created hook. The execution of these functions are done and I don't want these functions to be executed. Is there a way to stop execution of the mounted/created events?
It is impossible to replicate the behavior of v-if in a custom directive. Directives cannot control how vnodes are rendered, they only have an effect on the DOM element it is attached to. (v-if is special, it's not actually a directive but instead generates conditional rendering code when the template is compiled.)
Though I would avoid doing any of the following suggestions if possible, I'll provide them anyway since it's close to what you want to do.
1. Extend the Vue prototype to add a global method
You definitely need to use v-if to do the conditional rendering. So all we have to do is come up with a global helper method which calculates the access permission.
Vue.prototype.$access = function (param) {
// Calculate access however you need to
// ("this" is the component instance you are calling the function on)
return ...
}
Now in your templates you can do this:
<my-component v-if="$access({ param: 'param' })">
2. Define global method in the root component
This is basically the same as #1 except instead of polluting the Vue prototype with garbage, you define the method only on the root instance:
new Vue({
el: '#app',
render: h => h(App),
methods: {
access(param) {
return ...
}
}
})
Now in your templates you can do this:
<my-component v-if="$root.access({ param: 'param' })">
Now it's clearer where the method is defined.
3. Use a global mixin
This may not be ideal, but for what it's worth you can investigate the viability of a global mixin.
4. Use a custom component
You can create a custom component (ideally functional but it needn't be) that can calculate access for specific regions in your template:
Vue.component('access', {
functional: true,
props: ['param'],
render(h, ctx) {
// Calculate access using props as input
const access = calculateAccess(ctx.props.param)
// Pass the access to the default scoped slot
return ctx.scopedSlots.default(access)
}
})
In your templates you can do this:
<access :param="param" v-slot="access">
<!-- You can use `access` anywhere in this section -->
<div>
<my-component v-if="access"></my-component>
</div>
</access>
Since <access> is a functional component, it won't actually render it's own component instance. Think of it more like a function than a component.
A bit overkill for your situation, but interesting nonetheless if you ever have a more complicated scenario.

Vue V-Bind to Programmatically Created Instance

I follewed this instructions to create a Vue instance programmatically. I use this to dynamically add component instances in project by user events. My problem now it, that my component to initialize needs a model. I regular I would use it like this:
<my-component v-model="variable"/>
But now I create this component with this code snippet within another components methods section:
import MyComponent from '../MyComponent'
...
add () {
const Component = Vue.extend(MyComponent)
const instance = new Component()
instance.$mount()
document.getElementById('app').appendChild(instance.$el)
}
I know using a $ref here is better, but it must work globally, so I didn't know how to add it else to the DOM. But just as side note.
Now i need to give this instance a v-model binding. I already know how to define props or slots, but not a model. In the official docu they mention something for that. But to be honest I don't understand it and didn't get it work.
Can anybody tell me how I have to extend my code to define the model for this instance? Something like instance.$model = this.variable would be awesome. Thank u!
Finally I got some kind of workaround for this. I'm not aware if there is a better solution out there, but this works for me.
The MyComponent used this description to handle the v-model. By this is emit the change event for the parent component. So The idea is simply to pass the model variable as property, work in MyComponent on a copy of this variable and emit changes to the parent. To catch this change event I can add to my instance the following:
const Component = Vue.extend(EditWindow)
const instance = new Component({
propsData: { content: this.variable }
})
instance.$on('change', value => {
this.variable = value
})
instance.$mount()
document.getElementById('app').appendChild(instance.$el)
I guess this is pretty the same as Vue actually does in the background (maybe?). But after all it works and I'm happy. Of cause I'm open for the 'correct' solution, if such one should exist.

Global variables in Vue.js when using CLI template

I need to declare a global variable that can be accessed by all components in my Vue.js app. All components should be able to change its value. I am using CLI template.
Any suggestions?
Thank you
Dhiaa Eddin Anabtawi
In general in vue it's not possible/recommended to directly change a property in a parent scope. The way communication happens is by passing props to child components and to send back events (possibly with values) to parents (see Passing Data to Child Components with Props and Sending Messages to Parents with Events).
If you want to have easily accessible global state, a clean way to do it is to use a "store" as described in the State Management chapter of the vue guide:
var store = {
state: {
message: 'Hello!'
},
setMessageAction (newValue) {
this.state.message = newValue
},
clearMessageAction () {
this.state.message = ''
}
}
...
var vm = new Vue({
...
data: {
sharedState: store.state
}
...
})
then you can use this.$root.$data.sharedState to access the global state, for exampe using this.$root.$data.sharedState.message to read the message or store.setMessageAction() to modify it (assuming you are importing the store symbol).
That said, at this point, you would be much better served by using vuex, the official solution for centralized state management, which is easier, cleaner and less error-prone.

How to watch for Vue instance property inside component?

I have a plugin that adds some property to Vue instance.
Then I can access this property inside components using this.$plugin.prop. How can I watch for its changes? I need to do something inside component based on this.$plugin.prop value but neither watch or this.$watch worked for me. I assume its because watch works in component context so I can't watch for variable outside component, for example
mounted() {
this.$watch('$plugin.prop', val => console.log(val));
}
doesn't work.
What is the right way to accomplish this?
Instead of mounted() try
watch: {
'$plugin.prop': function(value){
console.log(value);
}
}
The offical documentation on watchers in the Vue docs
You may use computed properties instead like so:
computed: {
pluginChanged() {
console.log(this.$plugin.prop.val);
return this.$plugin.prop.val;
}
Read more about computed properties here.

Categories