lega911 / angular-light

Alight is a library for building interactive MVVM web interfaces/applications. (project is deprecated)
MIT License
274 stars 43 forks source link

Event directives #123

Closed lega911 closed 8 years ago

lega911 commented 8 years ago

Syntax

(event.modificators)="expression"

Examples

(keyup)="onKeyup($event)"
(keyup)="key=$event.keyCode"
(keydown.tab)="onTab()"
(keydown.enter)="onEnter($event)"
(keydown.13)="onEnter($event)"
(keydown.13.prevent)="onEnter($event)"
(keydown.control.enter)="onEnter($event)"
(keydown.control.shift.enter)="onEnter($event)"
(input)="value=$event.target.value"
(submit)="onSubmit()"
(submit.noprevent)="onSubmit()"
(click)="onClick($event)"
(click.noprevent)="onClick()"
(click.noprevent.stop)="onClick()"
(mousemove.noscan)="onMousemove($event)"

Modificators "prevent" and "stop" are on by default for click, dblclick and submit.

Modificators for all events

stop - calls stopPropagation
prevent - calls preventDefault
nostop - voids stopPropagation for click, dblclick, submit
noprevent - voids preventDefault for click, dblclick, submit
noscan - void $scan

Modificators for keyup, keypress, keydown

        enter: 13
        tab: 9
        delete: 46
        backspace: 8
        esc: 27
        space: 32
        up: 38
        down: 40
        left: 37
        right: 39

       alt
       control
       shift
       meta

aliases

alight.d.al.on.alias['my-keydown'] = {
  events: ['keydown']
  condition: (scope, event) ->
    return true if event.keyCode==13 && event.shiftKey && event.ctrlKey
    return false
}
dev-rke commented 8 years ago

Hm, i am not sure if i will like the notation of this feature. The brackets are not standard conform and currently i see not benefits to normal event directives. Can you please give some examples how this will work?

fend25 commented 8 years ago

Vote for @ symbol) Examples: http://vuejs.org/guide/events.html

lega911 commented 8 years ago

There are a few advantages: 1) You can catch any event, including custom events and events from other libs, e.g. (swipe)="onSwipe()", (my-custom-event-from-component)="receivedFromComponent=$event.value" You don't need make a directive for every event anymore.

2) more flexibility, you can filter events and modify behavor: (keydown.13.noscan)="send()" - executes the expression if a user press enter (13), after that $scan should be executes. example of navigation woth no js code: http://codepen.io/anon/pen/GodJeO?editors=1000

3) something else, forgot :)

We just should choose right style:

(click)="expression()" - angular2 @click="expression()" - vuejs on-click="expression()" - vuejs click.bind="expression()" - aurelia.io

propose your variant.

lega911 commented 8 years ago

after that $scan should be executes.

fix, shouldn't be executed, because of option "noscan"

dev-rke commented 8 years ago

I propose another syntax, because it would be more xml-like and could be realizable using a normal directive: al-on:click="eventHandler()"

Why not using e.g. al-on="click:eventHandler()"?

By the way, i think about to really 'bind' a handler function by referencing a callback instead of executing it on the event: al-on:click="eventHandler" This would give the eventHandler function automatically all parameters. But i also think this might break with current alight behaviour. What do you think? Lol, just saw vue.js uses also v-on:keyup.13="submit" So i prefer this style, because it should be compatible for serverside xhtml template parsers.

The question is: how similar/different should alight be to angularjs? Should it try to imitate the syntax? Or should it be an own framework?

lega911 commented 8 years ago

Why not using e.g. al-on="click:eventHandler()"?

al-on="click:eventHandler(), mousemove:mouseHandelr, keydown:keyHandler()"?

al-on:click="eventHandler" ... automatically all parameters.

There is only one parameter $event. It's not flexible, how would you do: "count++", "value = $event.value", "myFunc(localVar, 'customValue', 123)"

compatible for serverside xhtml template parsers

it's not important in 2016

The question is: how similar/different should alight be to angularjs?

We just should find out the best style, similarity is good (easy to migrate), but it's only one point of many.

dev-rke commented 8 years ago

It's not flexible, how would you do: [...]

So the best way might be: al-on:click="eventHandler()"

What about implementing multiple syntax styles? E.g. (click)="eventHandler()" and al-on:click="eventHandler()" do the same?

compatible for serverside xhtml template parsers

it's not important in 2016

You still provide an IE8 compatible build, this is also not important in 2016. I like alight, because it is still compatible to legacy browsers. There are several templating engines and productive systems which need an upgrade but it's expensive to upgrade a whole system at once, so there often are only redesigns with frontend improvements. Especially old CMS systems like TYPO3 (a widespread system in germany) still use xhtml templating engines. It is always good to follow existing standards to keep compatibility as long as it does not block a modern approach.

lega911 commented 8 years ago

al-on:click="eventHandler()"

symbol ":" already is used https://github.com/lega911/angular-light/issues/120 we can use ".", al-on.click="eventHandler()"

do the same?

yes

What about implementing multiple syntax styles?

I think it's ok @fend25 proposes change (event)="foo()" to @event="foo()", what do you think about it? I can make 3 styles about events, but it is too many

dev-rke commented 8 years ago

symbol ":" already is used

Thats bad, but i like the fact you support native xml namespaces. :-)

@fend25 proposes change (event)="foo()" to @event="foo()", what do you think about it? I can make 3 styles about events, but it is too many

yeah, in the best way we should all commit to one style. I like the parenthesis style (event)="foo()", because its compatible to angular.js 2.

Then i looked up the html standard: https://www.w3.org/TR/html-markup/syntax.html#syntax-attributes

  • attribute names must consist of one or more characters other than the space characters, U+0000 NULL, """, "'", ">", "/", "=", the control characters, and any characters that are not defined by Unicode.
  • XML-compatible attribute names are those that match the Name production defined in the XML specification [XML] and that contain no ":" characters, and whose first three characters are not a case-insensitive match for the string "xml".

So i also looked at the referenced xml standard https://www.w3.org/TR/REC-xml/#NT-Name to find out which chars are available:

[4] NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] [4a] NameChar ::= NameStartChar | "-" | "." | [0-9] [5] Name ::= NameStartChar (NameChar)*

I stripped off higher unicode char ranges to reduce complexity. To make the quoted syntax more understandable, i give you a 'simplified' validation regex (without unicode ranges):

/[A-Za-z:_][0-9A-Za-z:_-.]+/

Now i would like to focus on the unicode ranges: [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] These ranges explicit do not include parenthesis.

Last quote from the XML RFC:

Almost all characters are permitted in names, except those which either are or reasonably could be used as delimiters.

This is why the parenthesis syntax style is bad, same for the @-syntax style.

I also tested against the w3c validator to find some good examples. Copy the following test code into this validator https://validator.w3.org/#validate_by_input :

<?xml version="1.0" encoding="UTF-8"?>
<b>
<div class="working">
  <note al-onclick="fire()"></note>
  <note al-on.click="fire()"></note>
  <note al-on_click="fire()"></note>
  <note al-on--click="fire()"></note>
  <note al-onclick="fire()"></note>
</div>
<div class="not working">
  <note (click)="fire()"></note>
  <note @click="fire()"></note>
  <note al-on~click="fire()"></note>
  <note al-on+click="fire()"></note>
  <note al-on*click="fire()"></note>
  <note al-on[click]="fire()"></note>
  <note al-on{click}="fire()"></note>
  <note al-on|click="fire()"></note>
  <note al-on$click="fire()"></note>
  <note al-on,click="fire()"></note>
  <note al-on;click="fire()"></note>
</div>
</b>

I vote for implementing angular js 2 style and a fallback for al-on.click="fire()"

lega911 commented 8 years ago

symbol ":" already is used

I rethought, we still can use ":", al-on:click and al:on:click

I also tested against the w3c validator to find some good examples.

No one cares about it, different frameworks use styles that they want, maybe we shouldn't care about it too

Now i would like to focus on the unicode ranges: [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF]

My keyboard doesn't have these symbols, (ɨɟɄɶ...), there is no normal symbols

dev-rke commented 8 years ago

I rethought, we still can use ":", al-on:click and al:on:click

This is also ok for me. I try to keep a look on relevant things, but it is also needed to keep an eye for compatibility to older standards. Especially for such a relevant feature like this. I assume you will remove old event directives like al-click in favor for the new implementation, right? Then there should also be a compatibility for older standards or it wont be possible to use events anymore. Compatibility makes a framework usable for greater projects.

lega911 commented 8 years ago

I think it will be ok if I move them to a compatible/full build (maybe in next version)

I've made env.attrArgument for directives, it contains string after ':', e.g. for al-click:arg1.arg2="", env.attrArgument = "arg1.arg2" You can propose better name for this while no one uses it.

al-on:event.args="exp()" works in 0.12.12

dev-rke commented 8 years ago

env.attrArgument = "arg1.arg2"

Some framework events contain a dot to provide event namespaces (e.g. bootstrap). Wouldn't this collide? Is in your case the dot a separator for multiple events? What is arg2?

You can propose better name for this while no one uses it.

What do you mean?

By the way: why not using a syntax like al-on="eventName:eventHandler()"

lega911 commented 8 years ago

Some framework events contain a dot to provide event namespaces (e.g. bootstrap).

like this one? $('#myModal').on('shown.bs.modal', function () { it's problem, I will check it for keywords (stop, noscan...) to separate an event al-on:shown.bs.modal="func()"

Is in your case the dot a separator for multiple events? What is arg2?

env.attrArgument is a just string after ':', you can split('.') it or whatever...

What do you mean?

I mean that I can rename env.attrArgument if you have better name for this.

By the way: why not using a syntax like al-on="eventName:eventHandler()"

Give me an example how it would be in your style:

<input type="text"
    al-on:keydown.enter="onEnter($event, $index)"
    al-on:keydown.esc="cancelEditing()"
    al-input="value=$event.target.value" />

for example, angular1 had to use todo-escape and ng-submit for keydown: https://github.com/tastejs/todomvc/blob/gh-pages/examples/typescript-angular/index.html#L29

fend25 commented 8 years ago

By the way: why not using a syntax like al-on="eventName:eventHandler()"

It's too diffusively, isn't it?

compare: al-on="eventName:eventHandler()" @eventName="eventHandler()"

plus, in case of multiple events, @-syntax gets us a simple and obvious way to write it: @eventA="a()" @eventB="b()" and with al-on we should write multiple times al-on (it's too bulky) or think up a separator like al-on="eventA=a(); , eventB=b();" and if we want use comma in the right side of = symbol, e. g. for array or object, it will collide

what about () and @ - one symbol better than two:

dev-rke commented 8 years ago

like this one?

Yes.

Give me an example how it would be in your style:

Using Inline Objects:

<input type="text"
    al-on="{'keydown.enter':'onEnter($event, $index)', 'keydown.esc':'cancelEditing()'}"
    al-input="value=$event.target.value" />

As alternative we could use this syntax:

<input type="text"
    al-on-keydown-enter="onEnter($event, $index)"
    al-on-keydown-esc="cancelEditing()"
    al-input="value=$event.target.value" />

But this won't also work with bootstrap events, because the dot will not be handled.

The problem is, that handling events using parenthesis or @-signs cant handle multiple events at once:

<input type="text"
    (mouseover focus)="onHighlight($event)"
    @mouseover="onHighlight($event)"
    @focus="onHighlight($event)"
    al-input="value=$event.target.value" />

So i think about using a mapping / routing for each event:

<input type="text"
    al-on="'keydown.enter':keydown-enter,'keydown.esc':keydown-esc"
    al-on-keydown-enter="onEnter($event, $index)"
    al-on-keydown-esc="cancelEditing()"
    al-input="value=$event.target.value" />

al-on should handle the event and defines which virtual attribute handler will handle the event. keydown.enter:keydown-enter will handle the event (on the left side) and defines the virtual directive handler (on the right side, after the colon). The handler al-on-keydown-enter="onEnter($event, $index)" will be executed, when the event gets handled. This allows handling of multiple events. The problem will be, that alight would try to parse these virtual directives as regular directives and will fail. It would be great if alight would try to fetch the parent line of directive names, e.g. al-on-keydown-enter does not exist, try al-on-keydown. al-on-keydown does not exist, try al-on. if al-on is has a property that allows to handle child directives, then al-on will handle this event. :-) This parent line handling should initialized when the binding of an element runs. The referenced attribute al-on-keydown-enter should implicitly run al-on and this directive adds a reference to a lookup table. This reference helps the 'mapping al-on' to find out which handler attribute must be executed when an event occurs.. The problem is, that an alight directive can exist in the following formats:

al:on=""
al-on=""
data-al-on=""
data-al:on=""

Direct reading of this directives would be hard.

By the way, this would also allow redirecting events:

<input type="text"
    al-on="'keydown.enter':mouseover,'focus':mouseover"
    al-on-mouseover="onHighlight($event, $index)"
    al-input="value=$event.target.value" />

The great thing is, that this also could be used without defining a mapping:

<input type="text"
    al-on-mouseover="onHighlight($event, $index)"
    al-input="value=$event.target.value" />

I think the last variant would provide the necessary flexibility. I hope you understand what my idea is.

lega911 commented 8 years ago

The problem is, that handling events using parenthesis or @-signs cant handle multiple events at once

Why is it problem? You can't define multiple events at once physical because addEventListener accepts only one event at once. So you can use separated attributes for this: (mouseover)="" (focus)="", also it's rare case.

So i think about using a mapping / routing for each event:

You have thought up a problem and trying to solve it. Dots and colons are normal symbols, why do you try avoid them? angular2, vue.js, aurelia.io use them.

It would be great if alight would try to fetch the parent line of directive names, The referenced attribute al-on-keydown-enter should implicitly run al-on

It's wrong, because on directive al-html-by-id or al-html-bbb-error-here, I usually would have a error from al-html about wrong input syntax and sometimes al-html would just work wrong.

The problem is, that an alight directive can exist in the following formats:

We can cut it, if it's bad

By the way, this would also allow redirecting events:

I think your style is more difficult than one below, and it doesn't have advantages, except it can process camelCase events (but it's rare case which I can solve by a separate directive)

<input type="text"
    al-on:keydown.enter="onHighlight()"
    al-on:focus="onHighlight()"
    al-on:mouseover="onHighlight($event, $index)"
    al-on:input="value=$event.target.value" />

I'll be able to catch the bootstrap event by al-on:shown.bs.modal="foo()", so it's not a problem.

Current question is (event) vs @event, (al-on:event is in core already).

lega911 commented 8 years ago

So i also looked at the referenced xml standard https://www.w3.org/TR/REC-xml/#NT-Name to find out which chars are available:

We can use ":." by standard

lega911 commented 8 years ago

what about () and @ - one symbol better than two: Current question is (event) vs @event

we have +3 for @event and +1 for (event)

dev-rke commented 8 years ago

First: i will use whatever you decide for. :-) Second: i understood you would like to handle any type of event using this syntax.

So you need a syntax which allows nearly any kind of string. As i remember correctly you can also fire events like "?!@__9".

So as it is possible, your event handler would look like

<input type="text"
    al-on:?!@__9="onHighlight()"
    al-on:input="value=$event.target.value" />

This is a weird syntax.

Using

<input type="text"
    @?!@__9="onHighlight()"
    al-on:input="value=$event.target.value" />

is not even better, it is less readable.

Using

<input type="text"
    (?!@__9)="onHighlight()"
    al-on:input="value=$event.target.value" />

would be a bit better, but its also not a good syntax.

Using

<input type="text"
    al-on="'?!@__9':mouseover"
    al-on-mouseover="onHighlight()"
    al-on:input="value=$event.target.value" />

is more understandable and even more flexible.

I do not agree with:

You have thought up a problem and trying to solve it.

The other case, handling multiple events at once, is already possible by jQuery:

$('.myclass').on('mouseover focus', function() {/*...*/})

If you need to increment a counter, if an element is focused (e.g. by keyboard using TAB button) or if its hovered by the mouse, then you need to write the code twice. When using a router, its would be quite more flexible and has a bit more jQuery style:

<input type="text"
    al-on="'mouseover':myHandler, 'focus': myHandler, 'mouseover':triggersomething"
    al-on-myHandler="counter++"
    al-on-triggersomething="triggerSomething()"
    al-on:input="value=$event.target.value" />

You see that it would be able also to handle one event multiple times, to redirect it to different handlers (mouseover would increment the counter, also it would call the scope method triggerSomething()).

It's wrong, because on directive al-html-by-id or al-html-bbb-error-here, I usually would have a error from al-html about wrong input syntax and sometimes al-html would just work wrong.

No, you would not get an error: there is an attribute al-html-by-id. Next step is, that alight tries to resolve a matching directive. If it does not exist, it tries to resolve al-html-by. If this also not exists, it tries to resolve al-html, which exists. If the directive al-html has a property "processChildren" which must be true, in this case al-html would handle the directive al-html-by-id. When al-html has not this property, alight throws an error. This is not very much code to resolve parent directives, and it would significantly add more flexibility, especially for events.

I am not against a syntax like () or @, but i want you to think about if it would be useful to give a developer the full flexibility that e.g. jQuery provides. If you do not like the style of fetching parent events, it may be also possible to use data attributes:

<input type="text"
    al-on="'mouseover':myHandler, 'focus': myHandler, 'mouseover':triggersomething"
    data-myHandler="counter++"
    data-triggersomething="triggerSomething()"
    al-on:input="value=$event.target.value" />

Also i recommend to consider, that syntax highlighting systems (for the web, in an web development environment) might have a bit trouble using non-standard attributes like the proposed @ or () style.

lega911 commented 8 years ago

So as it is possible, your event handler would look like This is a weird syntax.

It's a problem of a custom developer who wants something like this, on the other hand you can make a specific directive for this event.

has a property "processChildren" which must be true

wow, it's good idea

al-on-myHandler="counter++"

You can't use camelCase for a name of attributes

handling multiple events at once, is already possible by jQuery:

Finally, jQuery calls addEventListener a few times anyway, not at once. A few al-on attributes give you the same effect.

You see that it would be able also to handle one event multiple times (mouseover would increment the counter, also it would call the scope method triggerSomething()).

you can define a few handlers in a one event: al-on:mouseover="counter++; triggerSomething()"

so, it looks more simple, less code

<input type="text"
    al-on:focus="counter++"
    al-on:mouseover="counter++; triggerSomething()"
    al-on:input="value=$event.target.value" />

that syntax highlighting systems might have a bit trouble using non-standard attributes like the proposed @ or () style.

Maybe highlighting systems will support Angular2 (style), when A2 is released. It seems this is a main reason for you why you want event-proxy-mapper.

fend25 commented 8 years ago

I totally disagree with this conception:

there is an attribute al-html-by-id. Next step is, that alight tries to resolve a matching directive. If it does not exist, it tries to resolve al-html-by. If this also not exists, it tries to resolve al-html, which exists.

It will brake directive object system and really confuse developers. alight.d.al.html is directive, but in the same time it has to be an object with child by.id

-Do you see gopher? -No. -And I don't. But he exists. DMB

lega911 commented 8 years ago

I've appended a few modifiers: control, alt, shift and meta

(keydown.control.enter)="onEnter($event)"
(keydown.control.shift.enter)="onEnter($event)"
lega911 commented 8 years ago

example: https://jsfiddle.net/lega911/0sq447zd/

fend25 commented 8 years ago

why (), not @ symbol?

lega911 commented 8 years ago

why (), not @ symbol?

I have not decided yet, I've made a interrogation - need more opinions, I'll fix it on the weekend.

lega911 commented 8 years ago

I've made fix, now events with dots works well:

        <div id="show0" al-on:shown.bs.modal="onShow(0)"></div>
        <div id="show1" al-on:shown.bs.modal.noscan="onShow(1)"></div>
        <div id="show2" al-on:shown.bs.stop.modal="onShow(2)"></div>

an event is words before first reserved word, for example above:

shown.bs.modal
shown.bs.modal
shown.bs

but, now it's more difficult to use, for example if you make a mistake: al:keydown.error.13="", then event is "keydown.error" instead of "keydown", so I will probably revert this change.

Also we can change a style: al-on:keydown="" -> on:keydown="", what do you think about it?

lega911 commented 8 years ago

we can use brackets for events like that:

<div id="show1" al-on:{shown.bs.modal}noscan="onShow(1)"></div>
<div id="show1" al-on:[shown.bs.modal]noscan="onShow(1)"></div>
dev-rke commented 8 years ago

@lega911:

It's a problem of a custom developer who wants something like this, on the other hand you can make a specific directive for this event.

Yeah, but the need to create an additional directive will be inconsistent. When providing a new feature like custom event handling, it should handle all cases.

Finally, jQuery calls addEventListener a few times anyway, not at once.

I know, but this makes jQuery event handling pretty easy. Also handling of bubbling events is handled by jQuery, i like that. Especially when you need to handle multiple buttons or inject additional elements dynamically, this has a very great performance.

Maybe highlighting systems will support Angular2 (style), when A2 is released.

Yes, maybe.

It seems this is a main reason for you why you want event-proxy-mapper.

Nope. I'll try to focus on standards, and i try to make things standards compatible. Why do i try to do that? When everybody implements it's own without respecting standards, several technologies won't work together. The bad thing about web development is, that there already are standards, which are not respected by browsers, templating engines, IDEs, proxy systems or other custom implementations. So when introducing new features, there should be a compatibility to existing standards. This was also my intention when requesting to add the 'data-' prefix for attributes. This is also the case, why jQuery is so succesful, it respects standards, provides backward compatibility down to the old IE6, and even fixes several different browser behaviour. Also nearly every web development framework provides a "reset.css" to standardize the default layout settings. There are standards, we just need to accept and use them. We can do our own, but we should also provide a standards compatible way.

an event is words before first reserved word, for example above:

This would get complex, because one has to look up if he needs to implement an own directive because he needs to use a reserved word in an event name.

but, now it's more difficult to use, for example if you make a mistake: al:keydown.error.13="", then event is "keydown.error" instead of "keydown", so I will probably revert this change.

See above, this might result in a complex situation. I like the idea using brackets, what do you think about splitting it into an handler for own events and an handler for browser events:

<div id="show1" al-on="{shown.bs.modal:onShow(1)},{shown.bs.stop:onHide(1)}"></div>
<div id="show1" al-on-keydown-control-enter-="onInput($event)"></div>

?

Another idea: If you do not like to resolve parent directives, you could simplify the handling by defining all of them as dynamic created directives on init:

events =
  keydown:
    Space: 32
    Enter: 13
    Tab: 9
  keyup:
    Space: 32
    Enter: 13
    Tab: 9

handlerReference = (scope, element, exp, env) ->

for key,event in events
  for char,keycode in event
    alight.d.al[key + char] = handlerReference

This won't handle custom events, so the user has to add his custom events before bootstrapping alight. When the user adds custom events to the API he also could define aliases. The advantage is, that the user not needs to define a whole directive, he only needs to register his events globally to ensure that directives can be generated when bootstrapping:

<div id="show1" al-on-my-event="onInput($event)"></div>
alight.utils.registerDirectiveAlias(alight.d.al.on, {'onMyEvent': 'shown.bs.modal'})

This will ensure the al-on directive will get the events, and it will also be generic enough to define own directive aliases. Maybe it might be useful to handle all default DOM events, by registering them as directive:

for y in document.body when y.substr(0, 2) is 'on'
  event = y.substr(2)
  console.log(event)

But i do not like this approach, the bracket approach above is easier, because it does not require the user to write any javascript.

@fend25:

It will brake directive object system and really confuse developers.

No, it won't. It extends the directive system. And also it should not confuse developers: When a directive method exists, the directive attribute gets handled by the directive method. When a directive method not exists, the directive attribute tries to resolve parent directives, which will only handle the attribute if the directive property "processChildren" is true. If there is no handling parent directive, alight throws an error that the original directive attribute cannot be resolved into any directive. So developing with this technique would not be more complex than now. Until a developer does not set the directive property "processChildren" to true, nothing changes in the handling.

lega911 commented 8 years ago

alight.utils.registerDirectiveAlias(alight.d.al.on, {'onMyEvent': 'shown.bs.modal'})

what if we declare aliases in alight.d.al.on.alias['myevent'] = 'shown.bs.modal mouseout'; or alight.d.al.on.alias['myevent'] = ['shown.bs.modal', 'mouseout']; and use it as

al-on:myevent="commonHandler($event)"
lega911 commented 8 years ago

Also handling of bubbling events is handled by jQuery, i like that.

is it when a parent handles events? alight can do this (nostop, noprevent)

This would get complex

I've removed the commit

an own directive because he needs to use a reserved word in an event name.

use an alias

But i do not like this approach

Neither do I

dev-rke commented 8 years ago

is it when a parent handles events? alight can do this (nostop, noprevent)

Yes, event bubbling means a parent DOM element handles events which occur on a child DOM element.

an own directive because he needs to use a reserved word in an event name.

use an alias

I do not understand what exact you mean. Aliasing directives would not work, this needs to be done before bootstrapping starts. So you need to define it manually, you can't use an alias to al-on.

lega911 commented 8 years ago

I do not understand what exact you mean. This would get complex, because one has to look up if he needs to implement an own directive because he needs to use a reserved word in an event name.

I've removed the commit, so we don't have this problem anymore.

So you need to define it manually, you can't use an alias to al-on.

It's ok, because you need it only for rare and specific cases, all normal events work well without it. Also you can make a custom directive: al-on-alias="myevent:shown.bs.modal mouseout" which makes any aliases you want, or like this: al-on-alias:myevent="shown.bs.modal mouseout"

and use it like: al-on:myevent="commonHandler($event)"

dev-rke commented 8 years ago
(keydown.control.enter)="onEnter($event)"
(keydown.control.shift.enter)="onEnter($event)"

I am not sure if this example is a good representation of what is needed in projects. Do you usually really need to handle several keycode events in your projects? Normally one writes his two or three lines of code to handle it, because there is more than a simple enter handling required. Also, e.g. if one needs to handle the input of a search form, there is typically a submit event to handle that, which already gets handled by the browser, when a user sends CTRL+Enter. So it is only required to handle the submit event of a form. The other point is, that there are not very much events, where you could apply this kind of handling. This approach is not really generic, so i think we should not implement this case as it is currently (* see below).

We should focus on generic events, e.g. what the DOM fires or what bootstrap uses, to improve compatibility to several frameworks. I am ok with () or @ syntax, because i propably won't use any of them.

alight.d.al.on.alias['myevent'] = ['shown.bs.modal', 'mouseout'];

I like this! :-)

Also you can make a custom directive: al-on-alias="myevent:shown.bs.modal mouseout" which makes any aliases you want, or like this: al-on-alias:myevent="shown.bs.modal mouseout"

and use it like: al-on:myevent="commonHandler($event)"

This also looks ok for me. Would you provide such an alias method by default in alight or should everybody write this on its own? By the way, i looked through some frameworks, mostly they use prefixes or dot separated namespaces for custom events. So i think an alias directive might not be very important, but it would be great to allow defining aliases in the directive alias property.

*) here is below :-) : I think i understand what you would like to do: simplify common use cases when handling events. But the current approach is too straight and less generic. When handle the offset of a scroll event (user scrolls down) e.g. in a onepage application, then i would like to slide in an image. But it should slide in after the user scrolled more than 200px. What we need here to get this generic is some kind of conditional handling (e.g. when event variable contains this and this and this, then fire the event).

brainstorming:

<input type="text"
  al-on-condition:keydown="$event.keyCode==13 && $event.shiftKey && $event.ctrlKey"
  al-on:keydown="onSpecialEnter()"
  />

Alternative syntax:

<input type="text"
  al-on:keydown="($event.keyCode==13 && $event.shiftKey && $event.ctrlKey) ? onSpecialEnter()"
  al-on:scroll="($event.ownerDocument.body.parentElement.scrollTop > 200) ? onSpecialEnter()"
  />

Bad thing of this approach is, we put logic into DOM. But we do the same when using predefined event constants.

An alternative would be to define aliases (see above) which can optionally handle a conditional function to handle the event (or to not doing it). This would be very flexible:

alight.d.al.on.alias['my-keydown'] = {
  events: ['keydown']
  condition: (scope, event) ->
    return true if event.keyCode==13 && event.shiftKey && event.ctrlKey
    return false
}
<input type="text"
  al-on:my-keydown="onSpecialEnter()"
  />

The my-keydown event only fires onSpecialEnter() method when the condition returns true. This would not increase complexity, and would simplify the event handling, because one can define custom global events with conditions. Also it would be simple to handle complex cases. It would be great to have the current scope as parameter in the condition to build complex conditions depending on scope variables.

What do you think?

lega911 commented 8 years ago

Normally one writes his two or three lines of code to handle it, because there is more than a simple enter handling required.

You can do this, modifiers "control+shift+enter" doesn't prevent you, I took them from A2

Would you provide such an alias method by default in alight or should everybody write this on its own?

Probably, but it should be not in the base build, because it's rare case

al-on-condition:keydown

It's not flexible, I can have a few keydown's for the one element

Alternative syntax:

You can do this now in spite of modifiers

What do you think?

It's good idea

dev-rke commented 8 years ago

You can do this now in spite of modifiers

Great!

It's good idea

What do you think beyond that it is a good idea? :-) Is it worth to implement a conditional handling?

lega911 commented 8 years ago

Is it worth to implement a conditional handling?

I think yes, it shouldn't be heavy (not much of code), and it doesn't prevent anything

lega911 commented 8 years ago

vuejs has added new modifiers:

New modifier for v-on: .capture. Attach the event listener in capture mode.

New modifier for v-on: .self. Only triggers the handler if the event was dispatched from the element itself, i.e. e.target === e.currentTarget.

Maybe we should make a possibility to make custom modifiers? (something like "condition" in aliases)

lega911 commented 8 years ago

Is it worth to implement a conditional handling?

done. example http://jsfiddle.net/lega911/vkp9q8gn/

event can be array or string

examples:

alight.d.al.on.alias['mykeydown'] = 'keydown blur';

alight.d.al.on.alias['mykeydown'] = ['keydown', 'blur'];

alight.d.al.on.alias['mykeydown'] = {
  event: ['keydown', 'blur'],
  condition: (scope, event) => {
    if(event.type == 'blur') return true
    return event.keyCode==13 && event.shiftKey && event.ctrlKey
  }
}
fend25 commented 8 years ago

@lega911 combining multiple events in one is pretty cool.

dev-rke commented 8 years ago

Cool! Didn't thought you would agree so fast with my condition approach :D

Maybe we should make a possibility to make custom modifiers? (something like "condition" in aliases)

I did not understand how to handle modifiers and what benefits they provide. Are they generic, can i use them for different events? The self modifier is ok for me and i understand the usage. But what kind of modifiers would a developer implement? It would be great to customize everything, but the question is, if there are potential use cases.

lega911 commented 8 years ago

I did not understand how to handle modifiers and what benefits they provide. Are they generic, can i use them for different events?

Yes, we can do something like this, e.g. modifier "self"

alight.d.al.on.modifier['self'] = (scope, element, event, env) => {
    if(element !== event.target) env.stop = true
}

for "enter" it could be:

alight.d.al.on.modifier['enter'] = {
  event: ['keydown', 'keypress', 'keyup'],
  fn: (scope, element, event, env) => {
    if(event.keyCode !== 13) env.stop = true
  }
}

also I can check a type of event inside of "fn"

It would be great to customize everything, but the question is, if there are potential use cases

Probably, majority of developers will not make own extensions, but they can use ready extensions as recipy which I make sometimes. For example I can make a plugin for alight 0.8 version which implement @event and :attr which we have just made.

It is easy to make, but there is a problem - every feature makes alight heavier.

dev-rke commented 8 years ago

This seems to be very simple to implement for developers and one can restrict the events where the modifier applies. I like the modifier approach now. :-)

And yes, every feature makes a framework bigger, but compared to angularjs is alight like a feather. :-)

lega911 commented 8 years ago

I like the modifier approach now. :-)

alight has hooks (alight.hooks.attribue, alight.hooks.directive, alight.hooks.scope) maybe we should move aliases and modifiers to alight.hooks.event.alias and alight.hooks.event.modifier? one point to extend alight?

dev-rke commented 8 years ago

one point to extend alight?

I agree.

lega911 commented 8 years ago

Look, modifiers and aliases do almost the same things, we need to combine them.

alight.d.al.on.alias['mykeydown'] = {
  event: ['keydown', 'blur'],
  condition: (scope, event) => {
    if(event.type == 'blur') return true
    return event.keyCode==13 && event.shiftKey && event.ctrlKey
  }
}
alight.d.al.on.modifier['enter'] = {
  event: ['keydown', 'keypress', 'keyup'],
  fn: (scope, element, event, env) => {
    if(event.keyCode !== 13) env.stop = true
  }
}
lega911 commented 8 years ago

alight has 4 modifiers: alt, control, meta, shift

modifier.alt = {
  event: ['keydown', 'keypress', 'keyup']
  fn: function(event, env) {
    if(!event.altKey) env.stop = true
  }
}

So when we use it as modifier: al-on:keydown.alt.13="" it just filters an event for altKey, and it is actived if the event in event list of the modifier (keydown for this example). When we use it as event al-on:alt.13 = "", it adds listeners on 3 events and filters them for altKey.

lega911 commented 8 years ago

an example of mykeydown (a version of one above) http://jsfiddle.net/lega911/14ynfvmh/ it uses the modifier as event and as modifier

lega911 commented 8 years ago

extra arguments: $event, $element, $value (from target)... ?

dev-rke commented 8 years ago

modifiers and aliases do almost the same things, we need to combine them

I thought about this before and decided against: When defining event aliases you are forced to the event directive and you couldn't extend other directives like modifiers can do it. Also using modifiers wouldn't match multiple events at once, but event aliases are able to do this. So we need to find a solution which is able to handle all this cases or we should keep it as it is.