-
Notifications
You must be signed in to change notification settings - Fork 52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ElementCollection should mount children's dom elements instead of Element itself #9
Comments
Hi @revyh, Thanks for taking the time to review the code and make a thoughtful comment to open a discussion about your suggested change 👍 First let me say I think this is worthy of discussion and whilst I haven't directly experienced an issue with the current approach while implementing element classes I don't want it to be difficult for others to extend so I'm keen to hear how best I can ease this process. My reading from your comment is that instead of Note on fixtures The fixture API is due to change, the access of the private Note on private properties and distribution of responsibilities Without the hacky fixture code there isn't access to any private properties, however to decouple we could simply pass a DOM element into the Where the responsibility for mounting the element to the DOM is a fair question? The way the tree of elements is set up everything inherits from the I know you're working on an element carousel example at the moment and we've discussed to some extent how best to approach this (whilst the question wasn't directly related to this issue). Can you provide a mock example of how you see this working in that scenario so I have something to play with, I find a good example is often the best way to decide on something. What you're doing with the carousel seems very similar to how a list might be constructed so I'm interested in how this approach improves things compared with a list. I think there would be some issues with backwards compatibility with such a change but not so many that it would be hard to switch should we implement the change (we had a similar issue when switching to the new event system). |
Glad to see that you are ready for the discussion. 👍
Yes, it seems you get it right. But I supposed to override
AFAIK, in this case
Good point. Actually, I thought about it before and I see no reasons why you need to separate elements from collections. Class So, If we assume that there is no separation beetween # just an illustration
createDOMElement: () ->
@_domElement = document.createElement(@_tagName)
@_setAttributes()
@_addDOMEventListeners()
for child in @children()
@ _domElement.appendChild Child.createDOMElement()
@_domElement All subclasses can specify it more accurately. This method only recursively creates dom tree, but doesn't mount it in Alternatively, instead of a single
Absolutely agree. I'll try to provide gist example tomorrow. |
Thanks for the response, I look forward to the gist. Just some quick thoughts,
I think this is a matter of interpretation. The point of asking external classes to use
This approach would seem to make it very difficult to change out a single element, for example if I want to drag an element from one position in a region to another having to call Wrongly or rightly CE doesn't support use a virtual DOM in the sense that React does and has no way to determine how to only re-render the parts that have changed if you ask it to re-build the top level region - there's no immutable state structure in CE or virtual DOM to compare against when re-building.
I understand this might just be an idea but as far as I'm aware there's no way to create a DOM element that's immutable in JS, if the function returned a DOM element it would be mutable? Even if the intention is to indicate to the user that this shouldn't be mutated (though I'm not clear from your description why this would be the case) I think you'd be better simply calling them I believe your reasoning behind this is to allow relevant JS for a DOM element to be called at the time an element is re-inserted into the DOM on save, as at the moment save exports the region in HTML format and re-inserts it into the page. I think this is an interesting option regardless of the other points discussed here (it would also be easy to rig up and wouldn't break the API for older releases as they would still work with the One thought on this though is that I'm concern about having JS logic for an element in non-editable mode within the element. The CE code base isn't typically present when non-editors view the content and so there's the potential for code that works in the editor to be missing or broken when the content is viewed without the CE code base. Typically the way I solve this type of integration as we've discussed is binding to the I don't however think it will necessarily stop the flicker that you see when the page is saved and each region remounted as the same process is undertaken. The change in noticeable flicker would be purely be down to the difference in performance between |
Seriously, I'm sorry. I personally don't like people who say "tomorrow" and disappear for a month. But I was very busy all this time. Exampleclass ContentEdit.CarouselElement extends ContentEdit.ElementCollection
constructor: (attributes) ->
super 'div', attributes
# instance of carousel class, supplied by some library
@_carousel = new Carousel()
mount: ->
@_carousel.init()
@_domElement = @_carousel.root()
@domElement().setAttribute 'data-ce-tag', @type().toLowerCase()
super
...
carousel = new ContentEdit.CarouselElement()
carousel.mount()
# at this point, the `carousel._domElement` looks like this
# <div class="carousel ce-element ce-element--type-carousel" data-ce-tag="carousel">
# <div class="carousel-list">
# <div class="carousel-track">
# </div>
# </div>
# </div>
#
# carousel library requires that all slides were added to it
# using it's `Carousel::add` method, which internally inserts slide's DOM element
# in innermost `carousel-track` div and binds needed event handlers
image = new ContentEdit.Image(imageAttrs)
carousel.attach(image)
# `carousel.attach` internally calls `image.mount`,
# which in turn appends `image._domElement` to `carousel._domElement`.
# So, at this point, the `carousel._domElement` looks like this
# <div class="carousel ce-element ce-element--type-carousel" data-ce-tag="carousel">
# <div class="carousel-list">
# <div class="carousel-track">
# </div>
# </div>
# <div class="ce-element ce-element--type-image"></div>
# </div>
#
# but it should be something like this
# <div class="carousel ce-element ce-element--type-carousel" data-ce-tag="carousel">
# <div class="carousel-list">
# <div class="carousel-track">
# <div class="ce-element ce-element--type-image"></div>
# </div>
# </div>
# </div> So, we have three options to solve this problem:
class ContentEdit.ElementCollection extends ContentEdit.Element
attachDOMElement: (element) ->
# note: `element` is CE element, not DOM element
# appending element._domElement to @_domElement
detachDOMElement: (element) ->
# note: `element` is CE element, not DOM element
# removing element._domElement from @_domElement
class ContentEdit.Element extends ContentEdit.Node
mount: ->
@_createDOMElement()
@_addDOMClasses()
@_addDOMAttributes()
@_addDOMEventListeners()
@parent() and @parent().attachDOMElement(@)
ContentEdit.Root.get().trigger('mount', @)
unmount: ->
@parent() and @parent().detachDOMElement(@)
@_removeDOMEventListeners()
@_removeDOMAttributes()
@_removeDOMClasses()
@_destroyDOMElement()
ContentEdit.Root.get().trigger('unmount', @)
class ContentEdit.CarouselElement extends ContentEdit.ElementCollection
attachDOMElement: (element) ->
position = @children.indexOf(element)
@_carousel.add position, element.domElement()
detachDOMElement: (element) ->
position = @children.indexOf(element)
@_carousel.remove position Other questionsGetters/setters and public methods
Maybe this is. Technically you right. We only use getters and public methods. No direct access to private members. Using
|
Hi @revyh - no worries on the delay (as you might imagine I often have the same issue). Thanks again for your detailed response, I've had an initial read through but need to go through in detail and write up a worthy response. I can definitely see some of the performance benefits of what your suggesting, some of which (for example createDOMElement and changing single element) I hope to implement in the next minor/bug release version. I think some however will clearly break backwards compatibility and so will have to be planned for a major release version - but I'm OK with that :) Anyway - I will be reviewing this more over the coming weeks, and will come back to you with comments and questions, thanks again for your insights :) |
Hi!
For now
Element
inserts itself into the parent element. I think it should be quite opposite - this code should be in theElementCollection
.First, it will give a better distribution of responsibilities. No longer need to access the private fields of the parent element in
Element::mount
.Secondly, it will facilitate extension of
ElementCollection
. For example, the integration of some carousel library in CE. Most likely it would require a new element -CarouselElement
which is a thin wrapper, adapter over the carousel library's api and represents a collection of slides (and therefore inherited fromElementCollection
). In particular, the addition of the slide's DOM elements in thedocument
should take place through the library's api and thus be part ofCarouselElement
. It can be easily implemented through the overloading ofattach
method onCarouselElement
. But with the current realization you still need to redefineElement::mount
(and maybe some other elements), so it only creates DOM element and doesn't attaches it to thedocument
if parent isCarouselElement
. Naturally this is not the best approach.Thirdly, it will simplify the implementation of the
Fixture
. Instead, to check whether the parent isFixture
, you can simply inherit theFixture
fromElementCollection
and override themount
.The same applies to
unmount
.I do not know how many changes and backward incompatibilities it can cause. At least, in CT
mount
should not be called on the focused element itself, but on its container.What do you think about it?
The text was updated successfully, but these errors were encountered: