A safe way for deleting components in appended/prepended container lists

I have this appended container containing a list of LV components (each of which is an appended container in itself with its own list of LV components) and by following the instructions of @josevalim (thanks again, btw!) I have finally managed to properly maintain those components in terms of deleting them without having either of the two side effects below:

  1. My or LV code crash on the update after deleting one or more elements
  2. LiveView “resurrecting” the deleted elements when applying the next patch

Now, I do that by (discovering and) respecting the following rules:

  1. In order to delete an element (a component) in its appended container list I first need to render it as something like
<div id="some id" data-delete="true"></div>

However, for this to work properly later when I actually delete the element, the above code must be rendered as an alternative to the otherwise standard element code within the same id (of course) and within the same component template (I guess this is because of template fingerprints or something similar).

  1. I have a dummy element (display: none) in the DOM position and rendered after both the above mentioned appended container and all its children, for it is required that its updated callback is invoked by LiveView after all container/element updates and update callbacks have finished (if I was using the updated container for this, its updated callback would be called before its children’s updated callbacks).

  2. In the dummy element’s updated callback I simply querySelectorAll all the elements tagged for deletion and delete them (as per Jose’s suggestion).

It is however imperative of having such dummy element’s updated callback triggered on any and all subsequent updates (patches) for LiveView will try to restore the deleted elements which I find quite reasonable given it not being the one deleting them. This can be observed if instead of deleting the elements in the described fashion, one tries to delete them straightforward from the very elements’ updated callbacks as then they also deleted, but LiveView restores each one of them on the very next update (of whatever other element).

A problem that is not so easy to fix though is removing the hook listeners manually when they need to keep a reference to the LV object associated with the element in question (e.g. Drag and Drop listeners which eventually invoke this.pushEvent). Since removeEventListener function requires the actual listener function as an argument, it is not possible to pass one from the code in another element (the dummy element doing the deletion that has no access to the LV object associated with the element being deleted.

Now, my question/suggestion here relates to how can this be done properly. Could there be a LV function exposed to remove elements by the JS code so that their Hook’s destroyed callbacks get invoked (given that by removing them with el.parentNode.removeChild( el) their hook destroyed callback is not invoked).

Also, by exposing such a function, I guess that LiveView would become aware of the element being deleted so it would not try to “resurrect” it on the very next update.

@josevalim: Any thoughts?

PS. The @chrismccord Chrip app demo source code also kind of implies that deleting the elements in an appended/prepended container from the app JS code may have undesired consequences, as what he does there after deleting an element in the list is update the entire list which in a case with many elements is not an option over performance issues.

Another thing. Even when the listeners problem is overcome, there are still unresolved issues with multi-user access to the same data model when dealing with deleting/sorting elements on JS client side (for those will not be reflected or restored anywhere else if the connection is down even for a very brief moment).

I hope all this gets resolved with the planned support for the lists in LV.

Issue solved thanks to the fantastic idea by @sfusato:

1 Like