First of all, it sounds like this situation is perfect of use eventBus but I am just experimenting. What I feel this will element the process of emitting and capturing event.
Say that in my main.js I have declared:
Vue.prototype.$someHeight = 200
in some of my component I tried to change it to:
this.$someHeight = 300
But when I use it in my vue, it still says:
{{ $someHeight }} // output: 200, and I was expecting 300
So, how to override it? Is this a good practice?
Every vue component is an instance of Vue. When you define a variable (such as $someHeight) on Vue's prototype, all new components created will get their own copy of such variable.
Which means every Vue component will have its own copy of $someHeight with value of 200. Now even if you set its value to 300, all other components will have the old value of 200.
It is not a good practice. You should only define functions on prototype: Vue doc
Now you can either use Vuex for this or create a global vue instance and use it to store your global variables. Vuex recommended of-course!
Related
I'm trying to understand if there is any significant difference between declaring a variable vs declaring a new Vue data property to assign values. (other than reusability and reactivity using Vue data property).
Example -
//Using variable
var result = "Passed";
//Using Vue data property
this.result = "Passed";
As you stated, creating a property with a value as a data property will allow it to be tracked in Vue's reactivity system. It will make that property available in the template section of your component whereas creating a standard variable will not be available this way.
Such as:
<template>
<div>{{greeting}}</div>
</template>
<script>
export default {
data () {
return {
greeting: 'hi',
}
}
}
</script>
Something to keep in mind, especially as you are scaling an app and it get's bigger, or you anticipate it will get bigger - is to only track data properties that are intended to be reactive. Storing static values as data properties is wasteful and can end up bogging down your app as it grows - because Vue has to track each of those properties for reactivity.
Basically, if you need a variable to be reactive, or exposed to the component template, create a data property for it. Hopefully that is straight forward and I explained it well.
Here are the Vue docs:
https://v3.vuejs.org/guide/data-methods.html#data-properties
I need to know the correct usage and the best practice of the setup function provided by vue3's Composition API.
I checked in my current project where developers actually use the setup function instead of creating the component with the traditional approach.
If it is just a design principle or improvement something then where we should apply these. I read the official documentation but instead, they didn't explain the concept, they just provided the list of arguments available in this function.
MyBook.vue
<template>
<span>Warning:- {{warning}}</span>
<button #click="warning = !warning">toggle</button>
</template>
<script>
import { ref } from 'vue'
export default {
props: ['warning'],
setup(props, context) {
const warning = ref(props.warning)
return {
warning,
}
},
}
</script>
<MyBook
:warning="true"
/>
As you can see above, I can't use the same name of a property to data attribute for a component but in the case of setup, we can do this and update the value. (as property should not change within component).
The Vue devtool is also showing the setup as a different category.
setup sets up an instance and returns properties that it should have. The purpose of Composition API, which setup is a part of, is to replace Options API, where an instance is determined by component options. So setup is the replacement for data, methods, computed, watch and lifecycle hooks.
As the reference explains, setup also replaces beforeCreate and created lifecycle hooks, the rest of hooks are set inside of it.
There is no conflict between data and props in setup function because props is accessible as setup parameter, i.e. warning and props.warning are accessible at the same time. In a template, they aren't and shouldn't be distinguished, they instance properties, the solution is to not allow name conflicts. They have been previously available with $data.warning and $props.warning magic keywords but their use wasn't encouraged. If warning value differs from a prop of the same name, and both should be available in a template, it should have a different name.
I have a component that has complex rendering logic.
I try to carry out this logic to helper classes, for simplifying.
To do this, in the data section (for reactivity), I create class references as follows:
export default {
data: () => ({
state: new InitialState(this),
query: new QueryController(this)
})
}
As I understand it, at this point the context of this is not yet defined.
So, I have two questions.
1) Is there a way to pass the this component context in the data section (without lifecycle hooks)?
2) Is the approach with references to external classes of vuejs philosophy contrary?
Component instance is already available when data function runs, this is one of reasons why it has been forced to be a function.
Due to how lexical this works with arrow functions, it's incorrect to use them to access dynamic this. It should be:
data() {
return {
state: new InitialState(this),
query: new QueryController(this)
};
})
The problem with InitialState(this) is that the entire component instance is passed instead of relevant data, this breaks the principle of least privilege.
Despite Vue isn't focused on OOP, there's nothing wrong with using classes. One of possible pitfalls is that classes may not play well with Vue reactivity because it puts restrictions on the implementation. Another pitfall is that classes cannot be serialized to JSON and back without additional measures, this introduces limitations to how application state can be handled.
As I understand it, at this point the context of this is not yet defined.
Only because of the way you've written the code. The component instance does exist and is available. It is sometimes used to access the values of props for determining the initial values of data properties.
For example, here is an example from the documentation:
https://v2.vuejs.org/v2/guide/components-props.html#One-Way-Data-Flow
export default {
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}
}
The reason why your code doesn't work is because you are using an arrow function. If you change it to the following then this will be available:
export default {
data () {
return {
state: new InitialState(this),
query: new QueryController(this)
}
}
}
See also the note here:
https://v2.vuejs.org/v2/api/#data
Note that if you use an arrow function with the data property, this won’t be the component’s instance, but you can still access the instance as the function’s first argument
As to your other question about whether using classes like this is contrary to Vue...
I don't think the use of classes like this is encouraged but they can be made to work so long as you understand the limitations. If you have a clear understanding of how Vue reactivity works, especially the rewriting of properties, then it is possible to write classes like this and for them to work fine. The key is to ensure that any properties you want to be reactive are exposed as properties of the object so Vue can rewrite them.
If you don't need reactivity on these objects then don't put them in data. You'd be better off just creating properties within the created hook instead so the reactivity system doesn't waste time trying to add reactivity to them. So long as they are properties of the instance they will still be accessible in your templates, there's nothing special about using data from that perspective.
I think computed is a better way to do what you want
export default {
computed:{
state(){
return new InitialState(this);
},
query(){
return new QueryController(this);
}
}
}
I want to store some of the variables between components in global window object, so I do window.showFilters = !window.showFilters
In the components, I am trying to use watcher on window object like
watch: {
"window.showFilters": {
handler: () => {
console.log(window.showFilters);
},
deep: true
}
},
However, this doesn't work so I have to use Vuex which I would like to use only for business data rather just code variables.
Is there are a right way to watch for variables in the window object?
If you do not want to use the Vuex store (which is the recommended way - and you can simply separate your business and application state in 2 different Vuex modules) then you are advised to store such variables in the root Vue instance (the one in your main.js)
The problem with global variables (a.k.a window properties) is that they are not reactive. You could try to use this.$set(window, 'showFilters', true) but this is ugly and might not work.
But even putting your variables inside the root Vue instance you still need to inform your components about the changed value - and you can do this by emitting events only (a watcher can only watch for changes inside the same component - not between components)
I resolved like this:
mounted(){
setInterval(()=>app.myownwatcher(), 100)
},
methods: {
myownwatcher(){
// do something with window.showFilters
}
}
It does not the best way but work :/ When my app run, I create a interval who update my model
I am searching for the best approach to change Vue variable value outside of component. I'm using Vue webpack as well.
I have created a project using vue webpack.
Inside its default App.vue file, I have a variable. For example, let's take showModal and its default value is false.
Then I built it in a single javascript file.
<button>Register</button> {{-- event button --}}
<div id="guest"></div> {{-- Here I'm rendering my template --}}
<script src="{{ asset('js/vue-js/guest.js') }}"></script> {{-- builded Javascript file --}}
And the problem is that I want to change my showModal variable to true, but the event button it is not on my component template.
What is the best approach to accomplish this?
If you want to access a vue component outside of vue you could register it to the window object and access it then from anywhere.
window.myVueComponent = new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
Now you can access it from anywhere else with
window.myVueComponent.myValue = 123
But this "style" is called global namespace pollution for reasons.
;)
I think it is better to extend your vue app so that the button is also within the vue-handled components.
Firstly, best approach wise it's prevalent to think about the relationships between your existing components and their relationships. So for instance if the information your trying to pass will be used in a direct sibling or further down the chain you could choose props.
If your dealing with two components that share no direct relationship other than there current state you will need to extrapolate to either using the repository pattern or Vuex (flux like state management library) where we can then pass a reference to state or into properties in the repository pattern.
FooRepository.js
export default class FooRepository {
SomeRef
ManyRef = []
addRef(name) {
this.someRef = name;
}
addRefs(names){
this.ManyRef.push(names);
}
}
The above can be instantiated in your App Layer and shared between your components using an instance property https://v2.vuejs.org/v2/cookbook/adding-instance-properties.html
Dependent on your apps size it might be time to include Vuex where we can save a reference directly into our state and use it in a simmilar manner as the repo pattern. Though as it's an officially supported package the setup and use is much simpler:
const store = new Vuex.Store({
state: {
ref: null
},
mutations: {
saveRef (state, compRef) {
state.ref = compRef
}
}
})
This is a very basic store that shows how we could save a reference into it, we can then access this reference in our components once we've registered the store inside main. This can be done using this.$store.state.ref. These two approaches are considered the best approach over simple simple props and or something like the event emitter for components that share no direct relationship.
Create a new Vue instance just for emitting, call it global event manager.
global.event = new Vue()
when you want to emit an event ( like modal )
global.event.$emit('close-modal', (optional msg))
when you want to close modal :
// In your component
created(){
var App = this;
global.event.$on('close-modal', (optional msg)=>{
this.showModal = false;
})
}
Similarly do it for opening the modal. If you are using the vue CDN (normal js file of vue), instead of global.event use window.event while creating and only event while using. In browser if a variable which is undeclared is used then it refers to the window object.