Skip to content
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

Support Automatic Mounting #75

Closed
phillipskevin opened this issue Jan 10, 2018 · 12 comments
Closed

Support Automatic Mounting #75

phillipskevin opened this issue Jan 10, 2018 · 12 comments
Assignees

Comments

@phillipskevin
Copy link
Contributor

phillipskevin commented Jan 10, 2018

This issue describes how we will make can-view-callbacks mount elements no matter how they are created (stache, document.createElement, etc). The steps to make this happen are outlined below:

1. Define tags as customElements

For browsers that support customElements, we will use customElements.define to register tags as custom elements.

2. Mark elements that are being created by stache

We will need to mark elements that are created by can-stache, so that we know they do not need to be "mounted" and we can avoid any performance degredation due to checking whether the element and its children need to be mounted. We will use a WeakSet to store these fragments.

3. Create a MutationObserver

For browsers that do not support customElements, we will create a MutationObserver that will listen for when elements are added and "mount" them if they were not created by stache (not in the WeakSet created in step 2). This code will be something like:

var obs = new MutationObserver(function MUTATION_HANDLER(mutationsList) {
    for(let mutation of mutationsList) {
        if (mutation.type === "childList") {
            for (let addedNode of mutation.addedNodes) {
                if (!inWeakSetCreatedInStep2) {
                    if (!addedNode[canSymbol.for("can.viewModel")] && viewCallbacks._tags[addedNode.tagName.toLowerCase()]) {
                        // element should be mounted
                    }

                    // also check children ( `makeArray(elem.getElementsByTagName("*"))` )
                }
            }
        }
    }
});
obs.observe(document.documentElement, { childList: true, subtree: true });

4. Move Component autoMount code

This code: https://github.com/canjs/can-component/blob/major/can-component.js#L228-L238

can now be done when a tag is registered in a browser that does not support customElements.

@phillipskevin phillipskevin self-assigned this Jan 10, 2018
@matthewp
Copy link
Contributor

matthewp commented Jan 11, 2018

Related to (2), once you register a tag with customElements.define you will have to deal with the fact that the callbacks will fire even for elements that stache created.

This is going to be tricky because currently

  1. can-component's are constructed when the element is created.
  2. can-stache creates a fragment for cloning.
    1. This fragment contains component's.
    2. Those "temporary" components will have their callbacks called.
    3. Need some way to prevent the component constructor from being called on these as there is side-effects.
  3. Need to prevent hydrated component's from being double-constructed (once by can-view-callbacks and once by the customElements callbacks).

@matthewp
Copy link
Contributor

To summarize the above problem more clearly, there are 2 problems:

  1. can-stache creates a fragment that only exists for cloning. If there are customElements.define registered elements, it might accidentally call the underlying can-component code on those (it shouldn't).
  2. can-view-target clones the above fragment, which might accidentally call the underlying can-component code. can-view-target will then also call the underlying can-component code.

@justinbmeyer
Copy link
Contributor

@matthewp could we check if the element is attached in the document? The fallback is to use mutation observers ... so only hooking up attached elements is fine.

@matthewp
Copy link
Contributor

You could if you don't want document.createElement("foo-bar") to work.

@phillipskevin phillipskevin changed the title Support Automatic "autoMount" Support Automatic Mounting Jan 11, 2018
@justinbmeyer
Copy link
Contributor

on creation, we could use MutationObserver on that one element ... possibly still speeding things up.

@matthewp
Copy link
Contributor

I'm not sure what you mean...

@phillipskevin
Copy link
Contributor Author

could we check if the element is attached in the document?

This would mean calling the tagHandler in a connectedCallback, right?

@matthewp
Copy link
Contributor

No, I think he means doing this:

customElements.define("foo-bar", class extends HTMLElement {
  constructor() {
    super();
    
    if(this.ownerDocument.contains(this)) {
      callbacks.tagHandler(this, { ... })
    }
  }
})

@phillipskevin
Copy link
Contributor Author

Ok. And if it's not in the document, set up a MutationObserver to listen for that element and call the tagHandler if it is inserted?

@matthewp
Copy link
Contributor

You can use connectedCallback to know if it is inserted, but doing this would mean that elements created outside of stache behave differently from those created via document.createElement('foo-bar') in user-code (in stache they are initialized before they are inserted).

@phillipskevin
Copy link
Contributor Author

We are definitely going to have this difference with the MutationObserver version, so it might be the best approach to just be consistent and do it this way all the time.

The primary use-case for auto-mounting (I think) is so that people don't have to do all this in JSBin:

var viewModel = new PaymentVM();
var frag = creditCardView( viewModel );
document.body.appendChild( frag );

@phillipskevin
Copy link
Contributor Author

This is closed in major.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants