I need to fire an event when page is done with rendering all the components. I have tried page mounted event but this fires only for the first time, I need to fire an event when the route changes on client side and new component is done rendering.
How about using updated . I used that for data changing state . Hope you get some help from this tips
updated: function () {
this.$nextTick(function () {
// Code that will run only after the
// entire view has been re-rendered
})
}
Related
I have a call in my created method which has an await.
I want to know that the results of that call are loaded so that i can conditionally show/hide things in the DOM.
Right now it looks like the DOM is being rendered before that method has completed. But I though that methods in created were called before the DOM rendered?
You're correct in assuming that the created hook runs before the component mounts. However, the lifecycle hooks are not waiting for async calls to complete. If you want to wait for that call to be completed and data to load, you can do so by using a Boolean that you set to true when your data has loaded.
Your template:
<div v-if='dataLoaded'>Now you can see me.</div>
in your vue instace
export default {
data () {
return {
dataLoaded: false
}
},
created () {
loadMyData().then(data => {
// do awesome things with data
this.dataLoaded = true
})
}
}
This way you can keep your content hidden until that call has resolved. Take care with the context when you handle the ajax response. You will want to keep this as a reference to the original vue instance, so that you can set your data correctly. Arrow functions work well for that.
I want to trigger an animation to run as soon as a component is mounted.
I've put the change inside a $nextTick function inside the mounted callback but it appears that it applies immediately before the component is first rendered, so the item appears in its final state without playing the animation to reach that point.
A fiddle of the problem is here: https://jsfiddle.net/j1oupq5e/1/
The relevant mounted function is as follows (width starts at 1px by default):
mounted() {
this.$nextTick(function(){this.width="100%"})
}
How can I make that animation start playing upon the component being mounted?
you can call setTimeout function :
mounted() {
let _this=this;
setTimeout(function(){
_this.$nextTick(function(){_this.width="100%"})
},0);
}
I have a bootstrap vue modal on a view page. When save is clicked, the save function emits an event. Works fine. When i close the modal and open it again then click on save, the save function is handled as expected emitting the function, however, it emits it twice (once for each time the modal was opened and closed. If i open and close the modal 5 times then click save, it calls the save function once but emits the function 5 times. I'm not sure how i can unbind the event when the modal closes using either typescript, vue, or bootstrap (any way other than jQuery :). Can anyone advise?
save() {
EventBus.$emit(MyEvents.RequestItemDetails);
}
// EventBus.ts
export const EventBus = new Vue();
export enum MyEvents{
RequestItemDetails = "request-item-details"
}
You've provided very little code for us to know what the problem actually is, but I'll take a guess.
If you're using a global event bus and you subscribe to an event on that bus from within a component, you need to make sure you unsubscribe from that event when the component is destroyed, otherwise your event handler function will be called multiple times because it gets registered multiple times on the bus.
For example:
import bus from './bus.js'
export default {
created() {
bus.$on('request-item-details', this.onRequestItemDetails)
},
destroyed() {
bus.$off('request-item-details', this.onRequestItemDetails)
},
methods: {
onRequestItemDetails() {
// Handle event
}
}
}
Your reply helped me find the solution. In my close method, all i needed to do was add "EventBus.$off('request-item-details')". That took care of it. Guilty of Overthinking again.
Thanks!
updated: function() {
console.log("updated");
this.$emit("render-vue", this.$el.offsetHeight);
},
This works fine enough...but in my app, props get updated (which I don't care about), and then there is a fetch() that updates data, resulting in additional DOM rendering.
So, that means that I get 2 'updated' events in succession! 👎🏾
Essentially, I only want updated to perform the $emit when data has been fully updated and the DOM is finished.
Currently, $emit is happening 2 times in succession, and that's not helping situation - IT DOM gets updated with props and then right after that another updated occurs when data gets updated after fetch().
Now, I could watch - tried that. The issue there is that $emit will fire off before all DOM is updated, sending the wrong info as argument.
It's almost like I need to watch a specific piece of data...and then, and only then, have updated send the $emit. 😖
For additional context, there are no children - this is a child component.
I can probably get this to work by using a setTimeout()...but come, on! That's sloppy! 👎🏾
In the code block that fetches the data, set a property to indicate new data is available
fetch(url)
.then(() => {
// whatever you're doing with the data
this.newData = true;
});
Check this state variable in the updated hook
updated() {
if (this.newData) {
this.$emit("render-vue", this.$el.offsetHeight);
this.newData = false;
}
}
Here is the example:
http://jsfiddle.net/hulufei/twr4thuh/7/
It just worked when bind onClick in virtual dom(like line 18), but If I comment line 18 and comment off line 8 to bind click with addEventListener, it failed.
So what's the problem?
TestUtils triggers events within react's synthetic event system, so the native event that addEventListener listens for is never going to be triggered. You will need to use the native click method on the element in your test:
var events = Events();
ReactTestUtils.renderIntoDocument(events);
events.refs.button.getDOMNode().click();
events.state.event.should.equal('click');
Additionally, you've misspelled clickHandler in your addEventListener definition.
jsfiddle
You can also simplify adding your event listener by reusing your prop definition:
componentDidMount: function () {
this.refs.button.getDOMNode().addEventListener('click', this.clickHandler);
},
Note:
Is there a reason why you want to use addEventListener instead of just passing an onClick attribute for your button? Unless there's a specific and good reason otherwise, i'd suggest doing things the react way when handling events for sanity :)
Edit
I originally mentioned that I did not know what TestUtils' SimulateNative.click did not trigger the event. I was wrong in thinking that it ever would since it would be simulating a native click event within the react even system. #thilo pointed me in the right direction :)
I had many problems while testing addEventListener, and I got the following conclusion.
You can create the events listener with pure javascript, jquery, but when running the tests with Jest I always had a problem.
The rendering of ReactTestUtils does not work directly with the document, and when we do:
For example, our events were added in the document, when rendering with ReactTestUtils it creates a div and renders it in the div, This way I could not get Simulate to trigger the call.
My first solution was to use jquery to create the listener and to test I did the render manually by appending the div in document.body, and triggered the events with the dispachEvent of javascript. But I thought the code was dirty, not the best way to work.
I made a sample code by adding the event and testing it with Jest, also have a test teaching to get all the listener that were created.
You can find the code here: https://github.com/LVCarnevalli/create-react-app/tree/master/src/components/datepicker
Component:
componentDidMount() {
ReactDOM.findDOMNode(this.datePicker.refs.input).addEventListener("change", (event) => {
const value = event.target.value;
this.handleChange(Moment(value).toISOString(), value);
});
}
Test:
it('change empty value date picker', () => {
const app = ReactTestUtils.renderIntoDocument(<Datepicker />);
const datePicker = ReactDOM.findDOMNode(app.datePicker.refs.input);
const value = "";
const event = new Event("change");
datePicker.value = value;
datePicker.dispatchEvent(event);
expect(app.state.formattedValue).toEqual(value);
});
Links:
window.addEventListener not triggered by simulated events: https://github.com/airbnb/enzyme/issues/426
Creating and triggering events: https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events