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

[BUGFIX beta] Fix no-jQuery EventDispatcher to prevent events bubbling up when event.stopPropagation() was called #17874

Merged
merged 2 commits into from
Apr 7, 2019

Conversation

simonihmig
Copy link
Contributor

Fixes #17840

Copy link
Member

@chancancode chancancode left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏

@chancancode
Copy link
Member

Does return false work?

@@ -360,6 +360,8 @@ export default EmberObject.extend({
event.preventDefault();
event.stopPropagation();
break;
} else if (event.cancelBubble === true) {
break;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably not the correct nesting for this. I believe we want to check the flag after the event handler has run (currently the check runs when if there is no event handler). Further, we probably need the same treatment for the action branch too. So I think you probably want to do it before setting the target to the parent.

I feel like this should have failed the test tho?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, we should make sure to mirror the jquery/dom behavior that stop propagation doesn’t prevent handlers on the same element from firing

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On that note, we probably have another bug here. It seems like the code assumes you can either have a component element handler, or action handlers, but not both. I believe with the possibility of forwarding action modifiers through splattributes, this is no longer a correct assumption.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we want to check the flag after the event handler has run

In what scenario would that not be the case? viewHandler(target, event) will always be called before our cancelBubble check, right?

Further, we probably need the same treatment for the action branch too.

I tried to add a (failing) test for that, until I realized that the action handler will not receive a reference to the event, so cannot call stopPropagation(), or do I miss something?

stop propagation doesn’t prevent handlers on the same element from firing

How would there be two different handlers (component event handlers) on the same element? You mean the case you mentioned in your following comment? 👇

It seems like the code assumes you can either have a component element handler, or action handlers, but not both. I believe with the possibility of forwarding action modifiers through splattributes, this is no longer a correct assumption.

Hm, I see... 🤔Good catch! But we should tackle this in a separate PR I think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

viewHandler(target, event) will always be called before our cancelBubble check, right?

You're right!

action handler will not receive a reference to the event

Don't we pass it here?

How would there be two different handlers (component event handlers) on the same element? You mean the case you mentioned in your following comment? 👇

Yep that's one case I could think of. I think you could also do <div {{action "foo"}} {{action "bar"}} />? Does that work? (Looking at the code, I'm not sure where this is handled.)

But we should tackle this in a separate PR I think?

Seems fine to me!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we pass it here?

Yes, but that goes to

handler(event: Event): boolean {
let { actionName, namedArgs } = this;
let bubbles = namedArgs.get('bubbles');
let preventDefault = namedArgs.get('preventDefault');
let allowedKeys = namedArgs.get('allowedKeys');
let target = this.getTarget();
let shouldBubble = bubbles.value() !== false;
if (!isAllowedEvent(event, allowedKeys.value())) {
return true;
}
if (preventDefault.value() !== false) {
event.preventDefault();
}
if (!shouldBubble) {
event.stopPropagation();
}
join(() => {
let args = this.getActionArgs();
let payload = {
args,
target,
name: null,
};
if (typeof actionName[INVOKE] === 'function') {
flaggedInstrument('interaction.ember-action', payload, () => {
actionName[INVOKE].apply(actionName, args);
});
return;
}
if (typeof actionName === 'function') {
flaggedInstrument('interaction.ember-action', payload, () => {
actionName.apply(target, args);
});
return;
}
payload.name = actionName;
if (target.send) {
flaggedInstrument('interaction.ember-action', payload, () => {
target.send.apply(target, [actionName, ...args]);
});
} else {
assert(
`The action '${actionName}' did not exist on ${target}`,
typeof target[actionName] === 'function'
);
flaggedInstrument('interaction.ember-action', payload, () => {
target[actionName].apply(target, args);
});
}
});
return shouldBubble;
}
, and there the event is not passed to the action handler. I believe this is how the action helper API works: the (element space(!), not closure) action helper passes only the custom args, not the event itself. That's why we have things like bubbles=false.

I think you could also do <div {{action "foo"}} {{action "bar"}} />? Does that work? (Looking at the code, I'm not sure where this is handled.)

We have that loop here:

for (let index = 0; index < actions.length; index++) {

And a related test here, though for different events:

['@test allows multiple actions on a single element']() {
let clickActionWasCalled = false;
let doubleClickActionWasCalled = false;
let ExampleComponent = Component.extend({
actions: {
clicked() {
clickActionWasCalled = true;
},
doubleClicked() {
doubleClickActionWasCalled = true;
},
},
});
this.registerComponent('example-component', {
ComponentClass: ExampleComponent,
template: strip`
<a href="#"
{{action "clicked" on="click"}}
{{action "doubleClicked" on="doubleClick"}}
>click me</a>`,
});
this.render('{{example-component}}');
runTask(() => {
this.$('a').trigger('click');
});
this.assert.ok(clickActionWasCalled, 'the clicked action was called');
runTask(() => {
this.$('a').trigger('dblclick');
});
this.assert.ok(doubleClickActionWasCalled, 'the doubleClicked action was called');
}

I tried to add a similar test with the same event type, but that fails, as the first handled action handler returns early here:

So this seems to be not supported at this point. I guess it should be possible, by always looping over all action handlers, and return false if at least one returned false!?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, wait a second. Actually that test with multiple {{action}} does pass w/ jQuery, so it seems it should actually work, but doesn't in no-jQuery mode!

I'll bring up another PR to fix this specific issue. As for the issue in this PR, I think this PR should be ok as it is?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we go: #17876

@simonihmig
Copy link
Contributor Author

Does return false work?

Yes, I think so! This was already covered in a test:

['@test events bubbling up can be prevented by returning false'](assert) {

simonihmig added a commit to simonihmig/ember.js that referenced this pull request Apr 7, 2019
@chancancode chancancode merged commit fb416b0 into emberjs:master Apr 7, 2019
@simonihmig simonihmig deleted the fix-stop-propagation branch April 7, 2019 16:57
simonihmig added a commit to simonihmig/ember.js that referenced this pull request Apr 7, 2019
wycats pushed a commit that referenced this pull request Apr 8, 2019
…e element-space actions

Related to #17874 (comment)

(cherry picked from commit 52b6657)
wycats pushed a commit that referenced this pull request Apr 8, 2019
…e element-space actions

Related to #17874 (comment)

(cherry picked from commit 52b6657)
(cherry picked from commit f194a2d)
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

Successfully merging this pull request may close these issues.

2 participants