I need to display a spinner in vue for every component (this is the requirement).
For that I think about to do v-if="loading" inside component HTML.
My question is how to detect when component is loading complete? (meaning after the DOM is rendered, and the data-bind is resolved to the DOM elements)
According to Vue lifecycle when update function is trigger then the render is complete.
So for that I'll need to implement for every component update function that change the loading to false. is there eazy way to do that? for example one place to write the update function? can I do that without implement extends? any sophisticated way to do that?
What I need for example is this.loading available in Vue instance.
Of course you can do it. Vue mixins come in rescue.
Mixins are a flexible way to distribute reusable functionalities for
Vue components. A mixin object can contain any component options. When
a component uses a mixin, all options in the mixin will be “mixed”
into the component’s own options.
Notice that you should use mounted hook if you want to track when the component is inserted into DOM.
Called after the instance has been mounted, where el is replaced by
the newly created vm.$el. If the root instance is mounted to an
in-document element, vm.$el will also be in-document when mounted is
called.
mixin.js:
export default {
data() {
return {
loading: true
}
},
mounted() {
console.log('I have been mounted')
this.loading = false
}
}
And then register that mixin globally, so it will be available in all components:
import mixin from './mixin'
Vue.mixin(mixin)
Related
I have a question about how to use an event in one component to trigger an event in another. I have two components, both child components at the same level. If the updated() method is called in component1.vue, I want component2.vue method setValues() to fire.
This is what I have within created on component2.vue:
created() {
EventBus.$on('updatedComponent', () => {
this.setValues();
});
}
And this is within the updated() method in component1.vue:
EventBus.$emit('updatedComponent');
I have eventbus imported already in both components, and event bus does work (I tested with a console log within the $on) but it still does not work. Do I need to be placing my EventBus.$on in a different lifecycle hook?
Your problem seems to be related to not starting the updated(), You need to update the view to trigger the updated()
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.
I am making a dispatch call to my store inside the created hook of App.vue, in a child component I then ask for that with a getter, the issue seems to be that when loading the app the child component is loaded before the data has been placed in the store and does not display. I am confident about this from the fact that I console logged and the first log is the child component who is the consumer of the store. How can I guarantee that the dispatch which is needed site wide happens before any other components are loaded?
// App.vue
created() {
this.$store.dispatch(this.$mts.diseases.DISEASES_LIST) //note I tried with async/await to no avail
...
//child component
created() {
this.diseases = this.$store.getters.diseasesList
...
As noted I tried to make the code inside the App.vue asyn but it seems that's it's not loading first so this did not help.
You should put the property inside a computed in your child component, that way it will populate as soon as the value exists in your store
computed: {
diseases () {
return this.$store.getters.diseasesList
}
}
See the VueX docs
If you want to wait for the data to be available before loading the component then you can use v-if see docs
<your-component v-if="$store.getters.diseasesList">
...
</your-component>
Consider using Nuxt.js (https://nuxtjs.org/) it has fetch and asyncData support out of the box, so that component won't render before the promises they return are solved.
I am making a form built of Vue JS components. I have the following components tree (each component contains the child below it ex. User Registration Form has the Form component as its direct child).
User Registration Vue Component
Form Vue Component
Input Vue Component
Input Option Component
After all of the components have full rendered I need to run a function in the User Registration. I tried putting it in the mounted function in the User Registration Vue Component but it runs before the Option Components have completed their mounted function.
I read here: https://medium.com/#brockreece/vue-parent-and-child-lifecycle-hooks-5d6236bd561f that this should not happen and that the child component should be completely mounted before the running the parent mounted function, in this situation the User Registration Vue Component.
I have about 100 components total including the Form, Inputs and all of the options.
I can only run this desired code in the User Registration Vue Component after everything has fully rendered and loaded. I tried using the jQuery(document).ready function but I have inconsistent results (sometimes the document is ready before the forms have fully mounted).
Any recommendations?
If you need to know when a component has been created, mounted, updated etc. from a parent component, you can just define a listener using #hook: followed by the lifecycle hook name.
E.g. from the parent component to execute doSomething() once the child component is mounted:
<child-component #hook:mounted="doSomething" />
Since the child mounting could be delayed more that nextTick(), you can emit an event programatically:
Child JS:
{
mounted: function() {
this.$emit("mounted");
}
}
Parent HTML:
<child #mounted="childMounted">
Parent JS:
{
methods: {
childMounted: function() {
// TODO child action
}
}
}
An alternative is to use the child component as a mixin
Define a directive:
Vue.directive("mounted", {
inserted: function(el, binding) {
binding.value();
}
});
You don't need to modify the child, only the parent:
Parent HTML:
<child v-mounted="childMounted">
Parent JS same as in (2)
mounted() hook of the parent component does not guarantee that all child component also finish rendering.
To execute something once the whole view has been rendered use vm.nextTick() inside the the parent component's mounted() hook.
//User Registration Vue Component
mounted(){
this.$nextTick(()=>{
//execute your code
console.log('Finished rendering the complete view')
})
}
Refer to - mounted lifecycle hook
Thanks #Tires, it works well
Vue.directive("mounted", {
inserted: function(el, binding) {
binding.value();
}
});
Parent:
<child v-mounted="childMounted">
{
methods: {
childMounted: function() {
// TODO child action
}
}
}
Need help with enzyme for testing. I have checked here but it did not help: How does one access state on a nested React component wrapped by an HOC?
Here is my problem:
How can I check function called or not and state of Child component? I also need to check lifecycle, so I used mount of parent component, but haven't done that yet.
I used below when I rendered one component:
expect(searchWrapper.find('select [selected]').val()).to.equal('someId'),
but with mount, this is not working, as it complains searchWrapper.find(...).val is not a function which I believe is a cheer IO and it worked with render only. So what will be the proper way for me?
What I intend to do: On child select change, check (and/or match):
child func is called once
parent func is called once (called inside 1.)
parent state
child state
lifecyle methods like componentWillReceiveProps and componentDidMount etc
Result rendered output validation, also including some of the above.
Component is like
class ParentWrapper {
// state and other functions and lifecycle methods
render(){
....
<SearchWrapper {...props}/>
<ResultWrapper {...props}/>
}
}
class SearchWrapper {
// state
// lifecycle methods
// functions
}
and same for ResultWrapper.
Summarizing it again with some code:
I want to access state and other objects of Child node, just like we can do with root. Also access rendered objects like selected option from the select menu, for example.
// state is not a function, as it has to be called for root only, then whats the alternative
expect(parentWrapper.find(SearchWrapper).state().searchId).to.equal('someValue');
// Below also did not work, it worked when I tested SearchWrapper individually using render()
// which is a CheerIO function, but I need to use mount of parent for whole component testing
// and inter-component interactions
searchWrapper.find('.form1 select').simulate('change', {target : {value : 'someValue'}});
// sinon spy on change is triggered
expect(searchWrapper.render().find('select [selected]').val()).to.equal('someValue');
// AssertionError: expected undefined to equal 'someValue'