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

Proxy events do not work in nested components #1043

Closed
fcamarlinghi opened this issue Jul 23, 2014 · 10 comments
Closed

Proxy events do not work in nested components #1043

fcamarlinghi opened this issue Jul 23, 2014 · 10 comments

Comments

@fcamarlinghi
Copy link

New to Ractive but already loving it, especially components! It seems though that proxy events are somewhat broken when it comes to nested components:

<mycontainer>
  <!-- Events won't work on the following: -->
  <mybutton label="Test button" on-myclick="buttonClick" ></mybutton>
</mycontainer>

You can see this behavior in action in this fiddle. Am I missing something obvious or is it a bug?

@codler
Copy link
Member

codler commented Jul 24, 2014

You need to do like this http://jsfiddle.net/e2x6S/4/ . There is one step btw you need to pass the event, in mycontainer.

@Rich-Harris
Copy link
Member

There's an open issue - #782 - discussing ways that events could bubble up from one component to the next, much as DOM events do. Until then @codler's is probably the best solution (there are some other suggestions in that issue, which may or may not be suitable depending on your use case). I'll close this in favour of #782, hopefully we can make some progress on it soon.

@fcamarlinghi
Copy link
Author

I see, thanks for the quick replies and work-around.

I did see the issue you mentioned, but it didn't seem to apply directly to my situation. I'm still new to Ractive, but I tend to consider components more as self-contained custom element definitions rather than template partials. As such it's not really about event bubbling up to the parent (though I can see why that could be useful too), but rather to being able to listen to component events directly, using the same proxy event declarative syntax, regardless of where they are placed in my templates. Looking forward to see how everything evolves.

@Rich-Harris
Copy link
Member

Ah, my misunderstanding, sorry. Is this more the sort of thing you meant? (The on-log='log' is a bit ugly, I realise.)

@fcamarlinghi
Copy link
Author

My apologies for not not being clearer from the start, it has to do with my (mis)understanding of how Ractive components work.

Lately I've been playing with Web Components libraries such as Polymer and found the approach to consider everything as an element to be very intuitive, but still too early in development to be implemented in production (mainly because of Shadow DOM). That said, Ractive components are very similar to Polymer elements as they implement rendering, data-binding, declarative event binding, etc. Below you can see a custom dropdown element definition in Polymer and the same in Ractive:

<!-- Polymer -->
<cc-button tooltip="{{tooltip}}" on-click="{{click}}" disabled="{{disabled}}"></cc-button>
<cc-menu visible="{{open}}">
  <template repeat="{{option in options}}">
    <cc-menu-item value="{{option[datakey]}}" on-click="{{select}}">{{option[labelkey]}}</cc-menu-item>
  </template>
</cc-menu>

<!-- Ractive -->
<cc-button tooltip="{{tooltip}}" on-tap="click" disabled="{{disabled}}"></cc-button>
<cc-menu visible="{{open}}">
  {{#options:i}}
    <cc-menu-item value="{{options[i][datakey]}}" on-tap="select">{{options[i][labelkey]}}</cc-menu-item>
  {{/options}}
</cc-menu>

Problem is, I'm not able to use the on-tap="select" on each cc-menu-item item, and the same is true every time I have to use a component inside another one.

I can fix the example above by using the workaround suggested by @codler, but I'll lose a lot of flexibility and encapsulation as I'll have to make my container components (such as cc-menu) aware of every event fired by their children. Event bubbling would probably fix the problem in some cases, but by reading the solutions posted in #782 will add complexity over the simpler on-tap="select" syntax.

@Rich-Harris
Copy link
Member

Got you. There's an interesting discussion to be had here around whether things like on-tap and on-mouseover, in the context of an inline component, should mean 'bind tap and mouseover handlers to the top-level elements inside the component, and fire proxy events when they happen' - that way you wouldn't have to explicitly listen for and fire those events within the component definition.

I'm sure it would raise all sorts of fun issues around naming conflicts (e.g. if a component author thought it'd be fun to fire events named click), and events that bubble versus those that don't, but it would make it possible to treat components more like elements. In the example you gave, that'd be pretty useful. In the Polymer case, they just are elements (conceptually at least), so they don't have the same distinction.

@fcamarlinghi
Copy link
Author

Yes, that's an interesting discussion. As you already highlighted, I guess that binding to the first top-level element might lead to a whole lot of unforeseen consequences and issues. It also assumes that components will always have a single top-level element, which is not always the case (like in the dropdown example above).

Maybe a viable approach might be similar to the way Knockout is going to handle components in their upcoming 3.2 release, that is optionally supporting registering components as simple custom elements. You can read an introduction here. This isn't the default behavior, so users have to opt-in for that explicitly.

A similar approach might save compatibility with existing components while at the same time open up interesting possibilities: event registration and bubbling would be handled by the browser, styles could be applied using custom element names instead of CSS classes, CSS rules wouldn't need to be rewritten to make style encapsulation work, etc.

I'm totally speculating here though...

@14to9
Copy link

14to9 commented Jun 19, 2015

Hi guys, it seems that proxy events might have a bug when used in partials with explicit contexts, too?

When I fire a proxy event from within a partial referenced thusly:

{{#each items}}
  {{ > partial { foo: "bar" } }}
{{/each}}

partial

  <a class='test-button' on-click='myClickEvent'>Test</a>

The generated event has what appears to be a wholly corrupt keypath value (browser console output):

component: Object...
context: Object...
index: Object...
keypath: "${{foo:"bar"}}"
name: "myClickEvent"
node: a.test-button: MouseEvent
__proto__: Object...

Certainly not the string I was expecting for keypath ..

@martypdx
Copy link
Contributor

@14to9 That's the expected result. The context, index and keypath are determined by the containing context in the template. This:

{{#each items}}
  {{ > partial { foo: "bar" } }}
{{/each}}

really is syntax sugar for:

{{#each items}}
  {{#with { foo: "bar" } }}
    {{ > partial }}
  {{/with}}
{{/each}}

So if you really want to pass foo: "bar" as part of the partial context, then also explicitly add the outer context keypath:

{{#each items}}
  {{ > partial { foo: "bar", item: this, keypath: @keypath } }}
{{/each}}

Then in your event you can access:

this.event.context.keypath

@14to9
Copy link

14to9 commented Jun 19, 2015

@martypdx Thanks very much for the thoughtful and timely response. 👍

I hadn't seen the @keypath binding before, I'll dig into that too

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

5 participants