matthewp / corset

Declarative data bindings, bring your own backend.
https://corset.dev/
BSD 2-Clause "Simplified" License
277 stars 2 forks source link

Child to parent bindings #108

Closed matthewp closed 2 years ago

matthewp commented 2 years ago

Discussed this elsewhere, but custom functions could be made to work with pretty easily. I could see something like this:

registerCustomFunction('--resolve', class {
  constructor() {
    this.state = 'pending';
    this.value = null;
    this.reason = null;
  }
  check([val], { rebind }) {
    Promise.resolve(val).then(resolvedValue => {
      this.value = resolvedValue;
      this.state = 'resolved';
    }).catch(err => {
      this.state = 'rejected';
      this.reason = err;
    });
  }
  call([]) {
    return { value: this.value, state: this.state, reason: this.reason };
  }
});

Which you could then use like this:

sheet`
  .page {
    --promise: --resolve(${Promise.resolve(1)});
    --state: get(var(--promise), state);
    class-toggle[--state]: true;
  }

  .page.pending {
    text: "Waiting";
  }

  .page.rejected {
    --reason: get(var(--promise), reason);
    text: "Oops, there was a problem: " var(--reason);
  }

  .page.resolved {
    --value: get(var(--promise), value);
    text: "We got: " var(--value);
  }
`;
JaneOri commented 2 years ago

dropping this idea in here from our convo yesterday related to the relationships between elements with behaviors; if I understood what you said correctly a moment ago, the idea above and this one are related at a deeper level in the implementation

omni-present behaviored element relationships


3 steps:

1) In the parent sheet: --var: listen(--foreign-var, select(".element-with-behavior")); resolves with the value of --foreign-var from the already behavior-bound element returned by select()

2) child behavior could have a dispatch fn in the context obj to dispatch --vars

3) when listen captures in the parent/recipient, call that parent's rebind()

And 3 notes:

matthewp commented 2 years ago

Another idea I've been looking at is inspired by the CSS toggle spec and looks like this:

mount(root, class {
  constructor(props, { stores }) {
    // Can save stores here if you want
  }
  bind(props, { stores }) {
    return sheet`
      #app {
        --app: "testing";
        store-root: app;
      }
      .child {
        store-set: app name "Wilbur";
      }
      .child button {
        event[some-event]: ${() => stores.get('app')?.set('name', 'Anne')};
      }
      .sibling {
        text: store-get(app, name);
      }
      #counter {
        /** Can pass a store to a child behavior */
        behavior: mount(counter, app);
      }
    `;
  }
});

Bikeshedding on the name:

matthewp commented 2 years ago

I went with the stores solution. It's available in 0.9.0 (a non-breaking change) and is documented here: https://corset.dev/learn/stores/

matthewp commented 2 years ago

Here's a fetch example: https://codepen.io/matthewp/pen/qBVvZWr