What is the proper and best way to queue events on Javascript and then fire them one after another in right order (FIFO).
I've made something like that:
https://jsfiddle.net/zjsh0xq3/
My component is getting state from Vuex / Vue instance and it is synchronized according to DOM position (if those components will be resized from outside). For this example I made List component with position of items but the idea is the same.
So after I fired method on ListItem the parent component List is storing those events (data for operation) in array. And after that events are fired in right order one after another, but only if there is callback from $emit fired to say to List that this state of List has been changed in Vuex / Vue instance.
Does anyone have any better idea to solve this case or somewhere ready tool for queuing events?
I thought about promises like in this article:
https://gist.github.com/anvk/5602ec398e4fdc521e2bf9940fd90f84
But I don't know how to prevent promises from being resolved until callback is called.
Thanks for advices!
Related
I have a component, which has an inner compnent, that holds an array in the data section.
when some value is pushed to the array, another div is created and the component re-renders.
<div class="engine" v-for="(dialect, index) in entitiesArray" :key="index" style="height: 36px;">
I want to fire an event when the ui finished. completely to re-render.
I tried the 'updated' method
from the documentation:
https://v2.vuejs.org/v2/api/#updated:~:text=also%3A%20Lifecycle%20Diagram-,updated,-Type%3A%20Function
The component’s DOM will have been updated when this hook is called, so you can perform DOM-dependent operations here. However, in most cases you should avoid changing state inside the hook. To react to state changes, it’s usually better to use a computed property or watcher instead.
Note that updated does not guarantee that all child components have also been re-rendered.
That does not work properly, and as stated in the documentation, even after adding 'await Vue.nextTick()', the element does not completely renders.
Additionally, I used a watcher for the array, which did not solve my issue.
the solutions I found are:
'await flushPromises()'
setTimeout() with 0 ms, so the code will be pushed to the end of the queue.
What would be considered as best practice to solve this issue?
In general, what is the best approach to wait ( or listen to some event) so that all UI changes and components re-rendering had been done entirely.
Thanks!
I need to send the data from the child component to its parent(by emitting), once the child is loaded without calling any event like click or keyUp,...
in other words, I want once the child component is mounted, it automatically sends some of its data to the parent and not necessarily calling an event to emit the data.
is there any way to do that in vue.js?
I'll thank you in advance
Instead of using the $emit i suggest using some store (state management).
It could be global store or sub store. A thing that will make it easier to watch changes in any parent/child components.
Imagine that you want to listen inside a very deep child component from a parent one. Would you start passing around events ?
The flow is :
1- Update the store from deep nested child
2- use a store getter in your parent component to watch/compute changes
Checkout vuex https://vuex.vuejs.org/
I suggest you read about how props work: https://v2.vuejs.org/v2/guide/components-props.html
One thing you could try to do is pass an Object as a prop, and mutate that object inside the child, than monitor object changes in the parent. But to be honest that conflicts with the ideas of how Vue works, and is based practice. So don't do it.
But what is the problem really? Emitting a custom event is by far the easiest thing to do, and fully in line with Vue.
In parent:
<my-comp #initiated="handleInitiated"/>
In child, after you did the initial work:
this.$emit('initiated', initiationData)
If you are worried about event propagation when using default event names, this solution with a custom event name solves that. Of course you can also always stop further propagation once you caught it.
The custom component
I've created a custom component for the navigation of my app. It consists of an ul element and all its li elements are dynamically created based on the items in the router's navigation list. This is happening in the attached event of the component. Nothing special is going on here.
The custom attribute
But because I want to have a good looking fancy menu I also created a custom attribute and implemented it into the root ul element of the custom component. In the attached event of the custom attribute I'd like to do some dom manipulations so that my menu looks like a menu created by those cool kids.
The problem
Although the attached event of the custom attribute is fired AFTER the attached event of the custom component, the dynamically created li items are not part of the dom in the attached event of the custom attribute yet.
The question
My assumption was that on attached event the view of the actual component is attached to dom and that all components before are also attached to the dom. And when I am done with the attached event the html that has been dynamically created here is also attached. Am I mistaken?
On a side note
I am aware of using TaskQueue could solve my problem. However, I would like to know if there's a different approach/solution first because I believe that moving things in time could cause a chain of paradoxes leaving you in a maintenance nightmare.
Allow me take away your misconception about the TaskQueue.
Your idea of moving things in time causing maintenance issues would certainly apply to using setTimeout() because that incurs an actual delay, and forces execution onto the next event loop. queueTask() is more or less the same thing (it uses setTimeout() internally).
However, the TaskQueue's queueMicroTask() method works very differently.
When you call queueMicroTask(), unlike queueTask() and setTimeout(), the task is scheduled for immediate execution on the same event loop. This is a very robust mechanism with a guaranteed execution order, and it's generally considered good practice to use it within attached() before doing any DOM manipulation.
In fact, queueMicroTask() is used internally by Aurelia in various places (mostly in binding and templating-resources). Two notable places are:
Property- and collection observers use it to "delay" notifying subscribers until all other bindings have completed their internal updating work
The repeat attribute uses it to set an ignoreMutations flag (and unset it after the queue is flushed) to prevent infinite recursion while updating its inner collection
You can generally consider there to be two "phases" to the bind() and attached() hooks: a non-queued, and a queued phase. The queued phase is when components do work that relies on the whole component graph to first be done with some other (usually recursive) process.
queueMicroTask() does not delay execution, just pushes it to the end of the call stack
It's the functional equivalent of passing the function as a callback to the end of the call stack, but saves you the trouble of writing the spaghetti code required to locate that last call and wire it all up. It's super clean.
all its li elements are dynamically created based on the items in the
router's navigation list. This is happening in the attached event of
the component
See, whenever you create anything during attached(), you can't rely on that thing being there during another component's attached() as this depends on the order of compilation/composition. That's an internal matter. This is especially true for custom attributes. Custom attributes (particularly those in style libraries) use the TaskQueue all over the place because it's the only way they can rely on the DOM being done.
Using queueMicroTask() here will guarantee two things:
It's executed when aurelia is completely done with the "first pass" of attacheds and rendering
It's executed immediately when aurelia is done with that - not even a microsecond delayed.
The best (and perhaps only correct) way to address this is indeed by using the TaskQueue - I promise :)
I have two components on a page, an update user form and a history of events. They are represented by two reducers (user and events). I want to update the list of events in the store (by calling an API) once once the user has been updated. However, the events should only be updated if the events component is on the page.
What's the best way to achieve this? The only solution I like the idea of is to have a success handler in a parent of the form / event components which dispatches an action which is caught by an effect which will then refresh the list of events. I was hoping this is something I could achieve entirely with effects but I obviously don't know if the component Is on the page and I also need access to route params. Does anyone have any better solutions or can see an issue with this solution?
Typically, the fact that the Events component is shown on the screen represents some sort of part of the "business logic/process" which is likely already represented in the store state. If it is not the case, you seem to need to add it in some way and update it when your events component initializes (ngOnInit) and is destroyed (ngOnDestroy).
Once it is part of the store, you can use it as part of the effect.
I have a page that uses a Kendo MVVM approach for two different elements, one providing file search results, the other a document upload facility.
The problem I am encountering is to do with the change event that both elements use - it seems that when one control fires a change event it is then picked up by the other control, which then attempts to process the event and passes it on, at which point it is picked up by the second control's change handler which processes it and passes it on to the first control's change handler. As you might expect, after around 1500 repetitions of this cycle, I see a Uncaught RangeError: Maximum call stack size exceeded message as the JavaScript engine runs out of memory.
At first I thought the problem was that the container of the second model was contained within the first, but even if they are completely separate on the page it seems as though the problem still shows up, so now I'm wondering whether the problem is related to the event being global to the page.
It seems that anything I do in my event handler in terms of trying to stopPropagation or stopImmediatePropagation - or even to set the event to null altogether - makes no difference to this behaviour. Tracing the call stack I can see it looping through Kendo's trigger call then through the event binding on my object and jQuery's dispatch loops that lead it back to Kendo, where it triggers the event handler on the other observable object.
Removing my bindings does not affect the problem, the change event is still bounced back and forth between Kendo and jQuery in the same way, it just doesn't run through my code.
The answer here was not a direct consequence of Kendo itself, so it would have been hard to answer from the question as I set it.
Inside the Observable container that was raising this error, I was using Isotope for layout. The step I had missed was that I had a relationship like this:
Parent [Observable]
-> Container
-> Child
-> Child
-> Child
One of the things that Isotope brings to the party is that for each item in the child collection, it adds a reference to its parent object.
When the child is Observable that creates a structure like this:
Parent [Observable]
-> Container <--┐
-> Child ---|
-> Child ---|
-> Child ---┘
This is an ideal situation for events to be propagated from child to parent, but because the properties in question were being automagically added by the libraries in question it was very hard to troubleshoot.
The solution was to remove the Container layer from the Observable model - it didn't need to trigger anything on change and so I wrapped it in a simple getContainer() closure and used that everywhere I was previously using it as a property. This protected it from the Observable object, breaking the circular reference without harming the functionality.
It may also be relevant that as far as I can tell the initiating event was a DOM change event rather than one of Kendo's own events. The problem may have been avoidable by using a custom Kendo namespace but that would have been a significant change in a complex application and guaranteed to cause a lot of side effects.