canjs / can-element

Make custom elements with CanJS
https://v3.canjs.com/doc/can-element.html
MIT License
3 stars 3 forks source link

Proposal: can-element + can-observe [+ can-jsx] #37

Open phillipskevin opened 7 years ago

phillipskevin commented 7 years ago

I think a version of can-element that combines Custom Elements with can-observe for observable data and (maybe optionally) can-jsx for templating is really compelling.

This was discussed on a recent live stream (11:32).

I think not requiring the use of classes makes the API much simpler. Something like this is what I'm proposing:

import element from 'can-element';
import { h } from 'can-jsx';

element({
  tag: 'my-cool-element',

  data: {
    first: 'Kevin',
    last: 'Phillips',
    get fullName() {
        return `${this.first} ${this.last}`;
    }
  },

  view() {
    return (
      <input type="text" value:bind="this.first">
      <input type="text" value:bind="this.last">
      <p>Hello, { this.fullName }!</p>
    );
  }
});

Internally, the data would be wrapped in can-observe and the view would be wrapped in the can-jsx render function. (We could also make this work with stache, but with jsx we'd want to wrap it like this)

I think this would be very easy for newcomers to understand since they just need to define their element in js and add it to the html.

@justinbmeyer used the selling point at the last DoneJS Chicago that CanJS apps are data + templates and this makes it very easy to set that up without jumping through hoops.

phillipskevin commented 7 years ago

@bmomberger-bitovi mentioned that maybe people would try to do

const data = {
  first: 'Kevin',
  last: 'Phillips',
  get fullName() {
      return `${this.first} ${this.last}`;
  }
};

element({
  tag: 'my-cool-element',

  data: data,

  view() {
    return (
      <input type="text" value:bind="this.first">
      <input type="text" value:bind="this.last">
      <p>Hello, { this.fullName }!</p>
    );
  }
});

data.first = "Brad";

Which would not work.

phillipskevin commented 7 years ago

@justinbmeyer convinced me that we will also need events: {} so that we can make events and values work together. This will be needed for interoperating with third-party code, but also so that an element can trigger an event and also listen to it.

We may not need to use this very often in guides, but we probably should make it work.

justinbmeyer commented 7 years ago

@phillipskevin Why wouldn't that work?

If someone gives us a POJO object, I think we should just canObserve( Object.create(object) )

bmomberger-bitovi commented 7 years ago

Observes can't dirty-check the underlying data. If you have:

var a = {};
var o = observe(a);
o[@@can.onKeyValue] = function(newVal) { console.log(newVal) };
a.foo = "bar";

the key value handler doesn't fire because o is not notified that a has changed.

I think new users will still find that confusing; maybe less confusing than the current way but still confusing. We should consider freezing objects that are passed in to can-element as data, at least in dev mode. That way it's clear that values can't be changed easily from the original prototype.

matthewp commented 6 years ago

TodoMVC: https://github.com/canjs/can-element/issues/43