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

Custom element interop #12

Closed
Rich-Harris opened this issue Nov 21, 2016 · 11 comments
Closed

Custom element interop #12

Rich-Harris opened this issue Nov 21, 2016 · 11 comments

Comments

@Rich-Harris
Copy link
Member

It might be possible to do this sort of thing:

import define from 'svelte/custom-elements/define.js';
import MyComponent from './components/MyComponent.html';

define( 'my-component', MyComponent );

Svelte might mean that custom elements finally make sense... no need to choose between a) writing reams of vanilla JS or b) using a framework that defeats the object of using custom elements

@mohsen1
Copy link

mohsen1 commented Nov 30, 2016

...or provide an option to compile to custom elements? From output code I can see how easy it is to do that.

Since custom elements need dash separated names compiler should convert camelCase to kebob-case.

@snuggs
Copy link

snuggs commented Nov 30, 2016

@Rich-Harris Ironically i've implemented this within the past few weeks. I've figured it out with your exact API coincidentally (even the ability to use new xComponent format.

You and I seem to see eye to eye on recent patterns and is the reason i haven't (fully) drank the ng/em kool-aid. Besides the element based stuff I don't have much usage for the other tools the framework provides. Even "routing" is simplistic enough for me to have a mini extension. No need for anything else but I digress.

The trick was 2 abstractions (extensions). One to HTMLElement and the other to DocumentFragment So basically a "template" is a document fragment and is a nice abstraction as you can easily create one off document.createDocumentFragment and this is what the default <template> element returns from template.content it's how you access the contents and DocumentFragment dom as you may already know. There is one major caveat you may already know about but I didn't. Must use the native-shim.js webcomponent fill or some REALLY funky stuff starts happening with firing constructors super(). Also event callbacks don't get bound like you'd think. Spent 2 days battling this last week in Hawaii before finding a solution. I think it has something to do with the state of registering webcomponents being torn in migration.
CustomElements v0

// dynamically register
document.registerElement ( // less flexible
  `svelt-button`,
  {extends: 'button', prototype: Object.create (HTMLSveltButton.prototype)}
)

let element = document.registerElement (`svelt-button`, {extends: 'button'})

// dynamically create
// existing custom svelt element in the dom (i.e. <svelt-button></svelt-button>)
document.createElement (`svelt-button`, `button`)

// one caveat is this method seems to fire off the constructor (but not events)
// for existing elements in the DOM as a side effect when `creating` a new element.

CustomElements v1

// dynamically register
// preferred but DEFINITELY need a polyfill as the spec is not fully matured.
// Pulled my hair out a bit on this one.
window.customElements.define ('my-svelte', SveltComponent)

With those two abstractions i've been able to build pretty much any interface even down to the event binding. Happy to contribute.

Let me know where I can help!

P.S. I gotta tell ya my most liked feature so far with all the components is how you get an attributes mutation observer out of the box! And can control which attributes are "watched" for mutation. it's baked into the spec @mohsen1

capture d ecran 2016-11-30 a 02 21 59
capture d ecran 2016-11-30 a 02 20 52
capture d ecran 2016-11-30 a 02 23 46

@PaulBGD
Copy link
Member

PaulBGD commented Jul 11, 2017

So here's a few issues I think we'll have to solve before implementing something like this:

  1. Defining a custom element is global, while svelte currently uses imports + manually listing components that the component needs.
  2. Custom elements are supposed to have names with a dash in them, while svelte components are generally named after class name conventions (CamelCase vs lisp-case)
  3. Does svelte define the component? Does the user define each component?

@snuggs
Copy link

snuggs commented Jul 11, 2017

@PaulBGD

  1. could possibly set a global env of window or pass in window.customElements
  2. Don't forget about hyphen names can't be started with one i.e. <-foo> but <foo-> is valid (but a little hackey to me). Currently having that discussion with my colleagues Element Naming Conventions devpunks/snuggsi#86
  3. I believe user is extending from a Svelte component. Which seems to be the norm these days

Hope this helps.

@Rich-Harris
Copy link
Member Author

I think maybe we could close this issue — I'm still not really sold on web components, so I'd be hesitant to add the maintenance burden of having the compiler support them directly. It's already very easy to use custom elements like so:

import Counter from './Counter.html';
import { register } from 'svelte-custom-elements';

register('my-component', Counter, [ 'value' ]);

document.body.innerHTML = '<my-component value="42"></my-component>';

You could even use svelte-custom-elements inside Svelte components:

<my-component value="{{value}}"></my-component>

<script>
  import './my-component'; // contains the first three lines of the example above

  export default {
    data() {
      return { value: 42 };
    }
  };
</script>

@snuggs
Copy link

snuggs commented Jul 11, 2017

@Rich-Harris far better for Svelte to wrap around a custom element. As it's no different than a canonical dom node at that point from Svelte's perspective. Trust me on this one. You're def digging in the right direction and don't really have to do anyting much with the svelte-custom-element "plugin"(?). I'll be working on updating the specs for Custom Elements soon.

@snuggs
Copy link

snuggs commented Jul 11, 2017

@Rich-Harris re not buyin' it yet... Agreed on not sold 100% but we're working on that sales pitch ;-)

@Rich-Harris
Copy link
Member Author

@snuggs There are some tangible benefits to components not being DOM nodes — for example, suppose you have a component like this...

<!-- App.html -->
<Foo bar='{{bar}}' baz='{{baz}}'/>

...which has a computed property qux that's derived from bar and baz. At the moment, if bar and baz change simultaneously...

app.set({
  bar: 1,
  baz: 2
});

...then Foo only needs to recompute qux once, and no DOM manipulation is wasted. Whereas if it's a DOM node instead...

<!-- App.html -->
<my-foo bar='{{bar}}' baz='{{baz}}'/>

...then Svelte has to generate code like this:

foo.bar = state.bar; // invokes a setter, which in turn updates `qux`
foo.baz = state.baz; // invokes a setter again!

Now, one possible solution to that problem is for updates to happen asynchronously. But then you have a programming model that's much harder to reason about, because the DOM isn't necessarily the way you'd expect it to be given your application state.

This is just one small manifestation of the problem, but the point that it illustrates is that the DOM is kind of a bad API for building state-driven applications. They're good for some things, but I just don't think they're a great substance to build complex apps out of. I say that as someone who was very enthusiastic about web components at the beginning!

@snuggs
Copy link

snuggs commented Jul 11, 2017

@Rich-Harris well... could just do work and trigger a render flag on rAF (requestAnimationFrame). That way if your code runs within 16ms good to go (Which it shouldn't be longer than an order of magnitude less than that). Secondly going off community traction most aren't interested in attribute changes. (Not a valid excuse tho for this use case).

Lastly i feel a touch of a strawman situation as the spec as stated here prohibits any JS operation to the same constraints. I was thinking about on the fly updating observedAttributes however perusing the spec defines this operation being put on the queue stack during defineition and is not a live list. (Perhaps it should be if you have any interest in this feature).

One should be able to use or set attributes on a custom element no different than if an <img> was in a Svelte component. Hence why I said your particular repro is merely an edge case of the DOM that would be there if custom elements didn't exist.

Thanks for the chat. Felt good riding on a 🦄 for a little bit but I think my $0.25 is up my friend. Meaning...what I proposed is great in theory. But Svelte doesn't have to do anything but sit back as it's in good position already.

Below are the implementation operations the platform must abide by. It's the isRunning flag that's why the attribute list can't be live:
capture d ecran 2017-07-10 a 22 47 39

FWIW jQuery has been doing the same for almost a decade now. "Fast" often just has to be "Fast enough". And human perception of "instant" is 30ms. That's why benchmarks are futile. But that's a topic for another day: :trollface: ❤️

// what React proposes for coupling with jQuery testing.

    $('input', this.getDOMNode()).prop({
      indeterminate: true,
      checked: false
    })

No magic trick there. Just plain (sequential) property updates to the HTML attribute. Any library would be in the same boat if needing transactional attribute updates. Luckily have the callback stack that only queues updates. They are processed non-determinantly so could possibly get off 2 operations in 1 on the queue but haven't tested so could be wrong.

@Rich-Harris
Copy link
Member Author

Going to close this in favour of #797, which is a proposal for outputting custom elements directly from the compiler.

@snuggs
Copy link

snuggs commented Aug 30, 2017

@Rich-Harris agreed! Wherever I can help let me know. I rock with you.

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

No branches or pull requests

4 participants