RFC: <proposal #someId [bind]="expr" (event)="doWork()"> #133

Closed
mhevery opened this Issue Oct 31, 2014 · 351 comments

Projects

None yet
@mhevery
Member
mhevery commented Oct 31, 2014

Update from @IgorMinar on 2014-11-10:

To summarize the constraints, the solution we are looking for must match the following:

  1. Be valid HTML (see this issue for discussion about [, ], (, ) chars in attr name of valid html).
  2. Be Web designer friendly
  3. Interoperable with web-components (don't interfere with web component initialization)
  4. Support debugging story via inspecting rendered DOM during development
  5. The syntax must clearly distinguish between 1) string literal; 2) expression (binding); 3) expression (events) 4) declaration of variables. All of this must be clear from template without understanding which directives are active or what the internal semantics of these directives are.
  6. Support additional info or options for each binding in order to avoid directive specific micro-syntaxes (this enables for tooling to work without hardcoding support for micro-syntaxes). This must be supported in a way that preserves constraint no. 5.

Please don't propose any new solutions that don't meet all of the criteria above.


Original description:

Current proposal for Angular2 syntax is

<element #someId [bind]="expr" (event)="doWork()">

There are several issues with this proposal which have come to light recently. These are:

element.setAttribute('[foo]', 'exp') does not work.

In all browsers tested (Chrome, IE, Safari) the above is an error. This means that it is not possible to create a valid template programatically. It is still possible to parse and read the template.

It seems like that calling setAttribute requires first character to be a-z|_ specifically - is not allowed as starting character nor are any of these ~!@#$%^&*(){}.

Third part HTML shorteners don't support non-standard characters.

Tools such as Slim, Haml, JADE don't support generating special characters. This would break the backend servers such as Ruby on Rails.

SVG requires valid XML and []()# is not valid XML.

This seems to be in the spec, but at least Chrome browser does not have issues with these characters. Need to investigate more.

Straw-man Proposal

We go back to slightly longer text based syntax

<element var="someId" bind-value="expr" on-event="doWork()">

The caveat is the ng-repeat syntax, which requires a parameter

<ul>
  <li bind-ng-repeat--item="items">{{item}}</li>
</ul>

Why the weird ng-repeat syntax? We want ta make sure that all bindings starting with bind- have right hand side as a valid expression. item in items is not a valid expression, it is a micro syntax. For this reason we need a way to pass the local-var-name into ng-repeat as a string literal, and not as an expression. Hence the ng-repeat--<local-var-name-literral>="expression".

Please leave your thoughts here.

Background

The reasoning behind the new syntax can be found here: https://docs.google.com/document/d/1kpuR512G1b0D8egl9245OHaG0cFh0ST0ekhD_g8sxtI/edit#heading=h.xgjl2srtytjt

@SanderElias
Member

I do prefer the readability of the current proposal. The setAttribute issue can be worked around.
Dropped into the console, and did some testing. I came up with this:

   function addAttribute(element, attrName, attrValue) {
      var tmpElm = document.createElement('p');
      tmpElm.innerHTML = "<p " + attrName + "='"+attrValue+"'><p>";
      var newAttr = tmpElm.children[0].attributes[0].cloneNode(true);
      element.setAttributeNode(newAttr);
   }

Witch is pretty rough, but I checked, and it does comply to all W3C standards. Well except for enabling attributes with 'exotic' names.

And with the straw-man proposal, if a user wants to create his own ngRepeat, how would that be possible?

@mprobst
Contributor
mprobst commented Oct 31, 2014

FYI for a JavaScript renamer like JSCompiler, the ng-repeat--item="items" would be easier to grok with ng-repeat="item in items". If you rename the subsequent use-sites of item, you also have to do so for the declaration. I think if you don't want microsyntaxes, you'll most likely have to go with additional attributes (ng-repeat="items" ng-repeat-var="item", could default to $item or so).

@PascalPrecht
Member

@SanderElias I think there are a couple of problems with that snippet:

  • It requires to work with strings and .innerHTML which leads to xss vulnerabilities. Which is also why angular works with DOM elements instead of strings because we get xss security by default
  • I'm not sure if the order of attributes is defined, which means you can't really rely on .attributes[0].cloneNode(true);. If there's more than one attribute, you might get some ugly side effects.
  • All in all it feels more like a hack

I like the bind-* and on-* syntax much more (also because I got used to it when reading all the design documents). @mhevery is the caveat with ng-repeat the only reason why you first decided to use [] and () over bind-* and on-* ?

@caitp
Contributor
caitp commented Oct 31, 2014

if all of the angry tweets and blogs are to mean anything at all, it seems people don't want a new syntax anyways. easier for them if it's as similar as possible to before.

@SanderElias
Member

@PascalPrecht, Sure it feels like a hack, because it is one. I whipped up the snippet in a couple of minutes to show a way around the limitation of element.setAttribute(). And you can be sure on the order of attributes if you create the html-snippet under the functions control. If something like this ends up in the source, it sure needs to check the strings for XSS attacks.

@caitp creating new paths always can count on resistance. On my part, the jury is still out on this one.

@caitp
Contributor
caitp commented Oct 31, 2014

the question is, what do we get out of the "new paths", if it's just change for the sake of change then it's not really doing any good. even though the grammar is technically easier to write parsers for, tools have been writing parsers for and working with angular expressions for a while now, so it seems like a moot point.

I'm really not sure we're getting any kind of "win" by making these changes.

@caitp
Contributor
caitp commented Oct 31, 2014

Unless we can demonstrate that it's really easier for people to use, then I would say don't even bother with it

@SanderElias
Member

@caitp Hmm, to me the win is better readable templates, and less boilerplate. The parser argument is indeed no reason whatsoever in this case. However, if the parser gets more then a few % slower as a result of the straw-man proposal, it is an valid point.

@PascalPrecht
Member

I'm still interested in what causes the original decision to use [] and (). There are four proposed syntaxes for template bindings but there's no official discussion on why the team decided to use [] and () over bind-* and on-*, or even one of the other two. Does anybody know why that is?

@otodockal
Contributor

on-* syntax looks much more natural to use, it's similar to DOM events syntax and the current ng-event syntax.

@akircher

Must you have a different left-hand syntax for binding and events?
Every event handler I have seen is a function call that contains a '()'. If Angular2 enforces this natural limitation that event handlers must be functions ( searching for a '(' and a ')' on the right-hand side), then you could use the same syntax for both binding and event. Two possibilities are:

<proposal ng-id="someId" ng-bind="expr" ng-event="doWork()">
<proposal _id="someId" _bind="expr" _event="doWork()">

As for ng-repeat, no novel ideas. I think that the bubble up event syntax (^click) will also need a work around for these problems. I would just try to keep the syntax as close to javascript as possible.

<ul>
  <li ng-for="let item of items">{{item}}</li>
</ul>
@PascalPrecht
Member

@akircher yes there is a reason why angular needs a new binding syntax. In order to work with any elements (that means native and custom) a new syntax is required that unifies the layer. Angular doesn't and shouldn't know about custom elements (or custom events). I've written an article about that which tries to explain it (http://pascalprecht.github.io/2014/10/25/integrating-web-components-with-angularjs/).

@sheerun
sheerun commented Oct 31, 2014

Hey! I paste my proposal from Google Gruups with syntax that resolves custom elements problem and doesn't break HTML attributes.

I've seen Angular plans to adopt following syntax for data binding:

<web-component [title]="exp" (close)="exp()">

Please don't break HTML syntax :( I love Angular because I can write templates for it in any templating language like SLIM or HAML or Jade (you can't do it in Ember or React, for example). Introducing non-standard characters in attributes makes templating languages unusable (as well as syntax coloring and introspection in IDE).

Also, I really don't like this syntax for passing arguments [ng-repeat|person]="people"

  • for me it's not clear what it does
  • it allows for passing only string arguments
  • both "person" and "people" are arguments to ng-repeat. Why to treat one differently?
  • it severely breaks existing syntax that developers are used to

I propose to use _ prefix for properties that need to be processed by Angular first. Then, use microsyntax instead of ugly [ng-repeat|parameter] syntax to provide parameters.

<web-component
    _string-parameter="Hello world"
    _fancy-string-parameter="Hello ${exp}"
    _one-way-bind=":exp"
    _two-way-bind=".exp"
    _on-click="exp()"
    _ng-repeat=".person in :people"
    _ng-if=".isVisible">

There are many advantages of such syntax:

  1. You don't break HTML syntax, most IDE and templating languages support leading _
  2. Attribute names only tell whether pass binding to Angular or not. Exact meaning is determined by Angular or its plugin.
  3. "templating system should be conflated with the binding system"
  4. Contrary to your current choice this syntax is extensible, and plugins can implement their own microsyntaxes.
  5. It allows for seamless WebComponent integration, as _ prefixed properties are handles by Angular before they are renamed and passed to WebComponent.
  6. I think it's lot more pleasurable to read and understand

All to all, my proposal is:

  1. Angular processes only properties beginning with _
  2. Angular supports out of the box following microsyntaxes:
    • exp - string interpolation (default, can include ES6 interpolation syntax ${exp})
    • exp() - javascript expression (like _on-click="exp()")
    • :exp - one way bind (familiar syntax!)
    • .exp - two way bind (natural syntax too)
    • exp1 in exp2 - special syntax for _ng-repeat, exp1 and exp2 are re-interpreted as microsyntaxes
  3. Plugins and future versions of Angular can define their own microsyntaxes.

Did I miss something? I beg you to consider this. I really love Angular 2.0 decisions so far, except for binding syntax.

Few additional notes:

  1. I wanted javascript expression to be the default, but I think it should be string interpolation. The same way HTML treats all attibute values as strings by default. So for example _type="text" means literally type="text", but _type=".inputType" does double binding, and _type="inputType()" evaluates an expression.
  2. This syntax also makes distinction between one-way and two-way binding. Your current choice doesn't.
  3. Plugins could create microsyntaxes from existing ones, similar to _ng-repeat's "exp1 in exp2" syntax.
  4. Such syntax is really "pluggable" and fast. Angular only reads properties beginning with _ and passes them through "middleware" set of functions with interface like call(e: HTMLElement, attribute: string, value: string).
@sheerun
sheerun commented Oct 31, 2014

Of course if for some reason Web Component uses _property, you can use __property in template.

p.s. code fragments were missing from my previous comment. they are present now.

@akircher

@PascalPrecht I understand that Angular2 needs a generic binding/event handling syntax in order handle custom events from web components. Am I missing something else from your post? (very helpful btw)

But what I am saying is that the parser could take

<proposal ng-id="someId" ng-bind="expr" ng-event="doWork()">

and seeing that expr doesn't have a '()' and that doWork does have a '()' could rewrite this to

<proposal ng-id="someId" bind-bind="expr" on-event="doWork()">
@oliverkra

And if some native functions were rewritten to work with this new model?

HTMLInputElement.prototype._setAttribute = HTMLInputElement.prototype.setAttribute;

HTMLInputElement.prototype.setAttribute = function (attribute, value) {
    if (/\[[a-zA-Z]+\]/.test(attribute)) {
        for (var i in this.attributes) {
            if (this.attributes[i].name === attribute) {
                this.attributes[i].value = value;
            }
        }
    } else {
        this._setAttribute(attribute, value);
    }
}

var element = document.querySelector('#test');
element.setAttribute('[value]', 'New value');
@pkozlowski-opensource

@EisenbergEffect I'm probably missing things (still trying to catch up with all the design decisions in 2.0....) but my understanding was that the idea was to bind to properties and not to attributes. So I guess what you are proposing is: bind-[property name]="expression", right? Sorry if this is just nitpicking - I'm just trying to make things clear in my head.

@EisenbergEffect

Yes. Properties. Sorry.

Note: I've deleted the original comment because it got messed up, something related to sending it through email. I've added the full version below.

On Fri, Oct 31, 2014 at 9:22 AM, Pawel Kozlowski notifications@github.com
wrote:

@EisenbergEffect https://github.com/EisenbergEffect I'm probably
missing things (still trying to catch up with all the design decisions in
2.0....) but my understanding was that he idea was to bind to properties
and not to attributes. So I guess what you are proposing is: bind-[property
name]="expression", right? Sorry if this is just nitpicking - I'm just
trying to make things clear in my head.


Reply to this email directly or view it on GitHub
#133 (comment).

Rob Eisenberg,
President - Blue Spire
www.durandaljs.com

@PascalPrecht
Member

I'm pretty much agreeing with @EisenbergEffect 's proposal (which again is bind-* and on-*) but i'm also very curious about what caused the decision to use [] and () in the first place. Maybe there's something we miss with bind-* and on-* that is covered with [] and ().

@EisenbergEffect Do you have a simple sample what the actual definition of such a micro syntax would look like then (from a component point of view)?

@sheerun
sheerun commented Oct 31, 2014

@EisenbergEffect I think we can all agree there's need for some kind of prefix / suffix to mark attributes that need to be processed by Angular into properties.

  • Current choice is [value]="something", but this is not a correct HTML attribute, and breaks lot of things I mentioned.
  • Your proposal is bind-value="something" which I think is little too verbose probably is already used by existing components
  • My proposal is _value="something"

It doesn't matter if Angular will choose to split bind-ng-repeat to two properties. What matters is, there's prefix that tells Angular to process this attribute first.

I don't think the on- prefix is good choice, because lot of components are using on-click or similar already. bind-on-click could be OK. As well as _on-click.

I think it's important to change microsyntax for double binding from exp to .exp, because the default should be the same as HTML's i.e. string (I explained it in my previous comment).

@PascalPrecht
Member

@sheerun

@EisenbergEffect I think we can all agree there's need for some kind of prefix / suffix

  • Yes, not necessarily a prefix/suffix but a syntax that unifies custom attributes/events in a declarative DSL. And yes we all agree on it, that's why this change has been introduced in the first place.

to mark attributes that need to be processed by Angular into properties.

  • Angular doesn't "process attributes into properties". It either binds to properties or attributes depending on the particular attribute and if it's reflected as property or not. So binding to an attribute is rather a fallback (as far as I understood the design documents)

Your proposal is bind-value="something" which I think is little too verbose probably is already used by existing components

  • I think bind- and on- are pretty straight forward.

It doesn't matter if Angular will choose to split bind-ng-repeat to two properties.

  • It actually does matter in order to have a simple DSL do express such things.

bind-on-click could be OK

  • IMO that's rather not a good choice, since we don't really bind to events. We listen to them.

I think it's important to change microsyntax for double binding from exp to .exp, because the default should be the same as HTML's i.e. string (I explained it in my previous comment).

  • I find .whatever rather confusing since that is what people know as CSS selectors.
@EisenbergEffect

I'm wondering if we can take a muli-faceted approach here.

For the core binding capability:

1.bind-property="{{expression}}" - default binding behavior
2.on-event="{{expression}}" - add event handler
3.${expression} - string interpolation (aligns with ES6 syntax)
4. Support a microsyntax hook that would allow directives like ng-repeat to translate ng-repeat="person in people" into two attributes: bind-ng-repeat="people" ng-repeat-local="person" once during template compilation.

On top of this core layer, add a backwards compatibility/convenience layer:

  1. Support property="{{expression}}" for convenience and backwards compatibility with 1.x. During compilation this will result in the removal of the attribute. A special debug mode for the compiler would re-write it as bind-property="expression" so that it could be seen in the DOM inspector. In production the attribute would be absent to avoid the cost of re-writing.
  2. Support {{expression}} inside of HTML content. This should be treated as a string interpolation exactly the same as ${expression} for backwards compatibility. (Opt-in only via a config setting on the templating engine.)
  3. Support an optional plugin to Angular 2.0 which contains a few directives like ng-click. This would be for backwards compatibility only. These directive would use the microsyntax hook to translate themselves into the new syntax.
  4. Upon release of ng2.0 mark the {{expression}} for html content only as obsolete and the optional ng-click, etc. directives as obsolete. This is planned obsolescence from the get go. This will help people to incrementally port but give them a timeline to upgrade to the new syntax after they get their app up and running.

Support a generalized mechanism for binding options, built into the expression syntax.

  1. An example of a one time binding might be: property="{{some.property & one_time}}"
  2. An example of a custom trigger for a two-way binding might be <input value="{{some.property & trigger:'blur'}}" />
  3. Note that binding options are similar to filters. While filters act on the output of the expression by transforming it in various ways, binding options would act on the binding object itself, allowing the manipulation of it directly.

Note that none of these proposals support directionality in bindings. If we are to support one-way and two-way binding, I would recommend the following:

  1. bind- and {{}} expressions use the "default binding" for a given property. By default this means one-way binding for everything except form control values, which default to two-way.
  2. directive properties would default to one-way but the developer could change the default for a given property using metadata annotations
  3. all defaults could be overridden on a case-by-case basis using the binding options mechanism described above i.e. property="{{some.property & one_time}}"
@caitp
Contributor
caitp commented Oct 31, 2014

@EisenbergEffect I feel like the "more than one way to do the thing" scenario is something we probably want to avoid...

Unfortunately, we obviously don't want to stick with exactly the original style either, but I think we should definitely try to minimize the size of the bridge that people have to cross in order to learn NewFangledAngular

@hfjallemark

I would look at the "more than one way to do the thing" as an optional backward compatibility layered on top. Meaning; support and educate the ideal 2.0 syntax in the core but provide extensibility points to allow layering different syntaxes on top to ease migration paths for developers with large 1.x codebases.

@caitp
Contributor
caitp commented Oct 31, 2014

it means we have an extra set of duplicate functionality to support, and we also confuse users who aren't sure which one to use in their apps. It's no good, strongly advise against it.

@pkozlowski-opensource

I tend to agree with @caitp here - those compatibility layers don't come for free (both in terms of initial coding, maintenance, mental burden for users etc.). Instead of providing a compatibility layer for, say, ng-click and the like it would be IMO better to come up with a migration tool that would help with translating existing templates.

But I would really prefer that we figure out the best syntax for 2.0 first and cover the migration story afterwards.

@EisenbergEffect

This is what you teach people:

  • property="{{expression}}" - default binding behavior
  • on-event="{{expression}}" - add event handler
  • ${expression} - string interpolation

That's it. Then, under the hood, you remove the expressions from the DOM to prevent the various web component issues, etc. In debug mode only, you add them back as bind- attributes so they can be seen when inspecting the DOM.

You then add a section to your documentation geared towards existing Angular 1.x users that explains that they can use {{expression}} for string interpolation in html content by turning that on, but it's planned for obsolescence in 20xx. The preferred method is ${expression}. You also tell them that there is a set of optional directives they can drop in that enable ng-click etc. which are also planned for obsolescence in 20xx. But these things are provided to ease their migration experience. You then, side-by-side, explain how the temporary migration-purposed directives map to the new system.

In your documentation you have an "advanced" section that explains how to create directives with microsyntax. You can also explain that everything gets converted to bind- under the hood and thus it can be used directly if desired.

It's trivial to implement the fallback syntax and attribute re-writing. It's also not much code to maintain and would be planned for removal in 20xx from the beginning.

@caitp
Contributor
caitp commented Oct 31, 2014

I don't think we should be doing any re-arranging under the hood here. It's making things worse, rather than better. Ideally, "what you write is what you get", there should be a limited set of constructs that you can write, but they should be reasonably powerful, so that you can express a meaningful application with them.

Less is more here, imho. Minimizing the burden on (new and old) users, and minimizing the cost of maintaining the framework, is a big win.

@Francisc

I didn't like the #, [], () syntax because it makes markup not look like markup and because it's another abstract convention you need to learn, similar to directives' @, =, & for example. I feel the same about adding anything that doesn't start with [a-z], like underscores.

I agree with Caitlin, there should be some sort of gain by changing this part of Angular and I know there's been a lot of criticism about the decision to change this part. Frankly, I've never felt this needs addressing.

Having a string that says what it does is better than an abstract notation.
Using --item is also kind of abstract and very "singular" in the case of ngRepeat.

@EisenbergEffect

My proposal attempts to solve both the technical issues at hand while also dealing with the migration story which the rather large Angular 1.x community is asking for. As far as I know, it is the only proposal from either inside or outside the team that attempts to handle both of these needs. The syntax is also the simplest of any of the proposals and it aligns nicely with Polymer as well.

Let's look at what Polymer does:

  • attribute="{{expression}}" - default binding behavior
  • on-event="{{expression}}" - add event handler
  • {{expression}} - string interpolation
  • _attribute="{{expression}}" - encode attribute for edge cases (which will likely go away in the next year)

My proposal changes {{}} to ${} in order to make the stringification explicit. This is necessary since we bind to properties rather than attributes. My proposal also recommends using the more meaningful bind- rather than _ for attribute name encoding, when required. What I'm proposing also improves the debugging story by re-writing the expressions in debug mode.

It's really very little work for us to do this.

@caitp
Contributor
caitp commented Oct 31, 2014

It's a good discussion to have and I'm glad we're having it in the open so people can follow it and understand why whatever decision eventually is made =)

I am still not too keen on it, it sounds a lot like snake oil to me. I think not only should we "say" that it's going to be possible, but demonstrate that it actually is possible and better.

@lgalfaso
Member

@EisenbergEffect we cannot use just
property="{{expression}}"
the main issue is that it may clash with an existing html attribute (and in some cases, attributes that you may also want to specify without creating any binding at all)

@sheerun
sheerun commented Oct 31, 2014

If we decide to always use prefix, there's no need to keep ng-cloak. For example you want to leave:

<button value="Hello ${world}" on-click="displayHelloWorld()">

There will be flash of Hello ${world} before Angular kicks in. Also Web Component consumes both value and on-click before Angular has chance to interpolate value and convert on-click to function that operates on $scope.

Now consider always using a prefix for Angular properties:

<button _value="Hello ${world}" _on-click="displayHelloWorld()">

The value attribute won't be displayed in html, nor passed to web components until Angular kicks in, parses _value, and adds value property with already interpolated text. The same goes for on-click - it's not passed to Web Component, nor is clickable until Angular kicks in and assigns to onClick property converted function that operates in proper scope.

Please understand that:

  1. You can't use plain attribute="something"
  2. You can't use on-something="exp()"

Cheers

@EisenbergEffect

@lgalfaso What do you mean by "clash with an existing html attribute"? This syntax works fine for almost every existing databinding library today. What problem have they not accounted for? (Aside from what I've already explained can be solved by removing things during compilation.)

@lgalfaso
Member

@EisenbergEffect without a mechanism to know what is a binding and what is not, how can you tell what is what on
<input autocorrect="on" value="foo.bar">
?

@EisenbergEffect

@sheerun You misunderstand the compilation process for ng2.0 a little bit.

There is no need to avoid the flash of expressions if the compiler has already extracted them during compilation. HTML contained inside of the template element is dormant. WebComponents are not upgraded until that content is cloned, based on my tests, and they will not see it if the attribute is removed.

The primary reason for not using plain html attributes was for debugging reasons. That can be solved by re-writing during debug mode.

@approots

Agree with @Francisc. Explicit is better than implicit.

@EisenbergEffect

@lgalfaso In your example, neither of those properties are bound. If you want to bind value then you would do this <input autocorrect="on" value="{{foo.bar}}">

@jeff-blaisdell

+1 for everything @caitp is saying.

@sheerun
sheerun commented Oct 31, 2014

@EisenbergEffect Then your "Databinding with Web Components" document misleads people :)

The effect of this process is that the Web Component would first flash the expression binding literal “{{exp}}” and then be replaced by the actual expression value. This is clearly not the desired behavior.

@EisenbergEffect

That is incorrect information, yes. It needs to be updated. (Incidentally, I just discovered that error. Sorry.)

@pkozlowski-opensource

@EisenbergEffect good point about <template>. Just one question, though, for my own understanding: would all of Angular2 partials would have to live inside <template> tag? How would one bootstrap an application then? Would it mean that router would point to components instead of random HTML chunks?

@EisenbergEffect

@pkozlowski-opensource Currently html is contained inside of templates. Bootstrapping an application would involve specifying a "root component". This allows to remove all the problems with ng-cloak, etc. It also makes it super easy to have an application splash screen while things are loaded, great for mobile or web. This is still being debated. As you can see, there are a bunch of related issues here. This what I would favor though.

@pkozlowski-opensource

@EisenbergEffect OK, this starts to make sense. But we should really remove this comment from the design doc:

The effect of this process is that the Web Component would first flash the expression binding literal “{{exp}}” and then be replaced by the actual expression value. This is clearly not the desired behaviour.

If it is incorrect because this was the part that made me convinced that we absolutely need a prefix. Based on your comment it doesn't seem to be the case and for me it changes things substantially. But well, still wrapping my head around this stuff here...

@EisenbergEffect

I've added a note to the original document.

@SanderElias
Member

+1 for @EisenbergEffect approach. I think that for many developers this will be a big help in the understanding and migration to 2.x. And there must be indeed an really hard date, that marks the drop-off of the old stuff. For my personally, I really did like the current proposal, but I can understand the issues with it.

@caitp
Contributor
caitp commented Oct 31, 2014

Anyways, with magic prefix/suffixes, I think we want to avoid the whole thing with "users can't use -start as a suffix" bug that we had in 1.x for a long time, which led to making the directive API even crazier. Have to be very, very careful with these prefix/suffixes to make sure they don't limit what a user can do.

@donaldpipowitch

I like the on-* and bind-* syntax and I would use something like bind-repeat-var, too.

The original proposal seems to be a little bit to far away from HTML and I always liked that Angular uses plain HTML. Can I even style the current proposed syntax via CSS selectors (e.g. when compilation breaks for some reason like deactivated JS). E.g. is this even a valid CSS selector? [[bind]="expr"] I would guess this needs some sort of escaping.

@yamafaktory

+1 on the on-* and bind-* as it makes a lot of sense regarding to what Polymer is already doing, which seems 'natural' https://www.polymer-project.org/docs/polymer/databinding.html .

@akircher

Per my previous comments, could someone please explain why must we have separate syntaxes for on- vs bind-? Is it just to be super clear what is being done? Event listeners are almost always functions and Bindings almost never are. I think the distinction is already pretty clear without separate syntaxes.

<proposal ng-value="expr" ng-click="doWork()">

In this example, "value" can be any property and "click" can be any event. You know value is a binding (bind-) because there is no () and you know click is an event (on-) because there is a ()

To me this is by far the cleanest syntax, it prevents naming collisions, and the right-hand side will always be an expression.

@EisenbergEffect

In Angular, you can actually observe functions so unfortunately it's not
that simple.

On Fri, Oct 31, 2014 at 2:58 PM, akircher notifications@github.com wrote:

Per my previous comments, could someone please why must we have separate
syntaxes for on- vs bind-? Is it just to be super clear what is being done?
Event listeners are almost always functions and Bindings almost never are.
I think the distinction is already pretty clear without separate syntaxes.

In this example, "value" can be any property and "click" can be any event.
You know value is a binding (bind-) because there is no () and you know
click is an event (on-
) because there is a ()

To me this is by far the cleanest syntax, it prevents naming collisions,
and the right-hand side will always be an expression.


Reply to this email directly or view it on GitHub
#133 (comment).

Rob Eisenberg,
President - Blue Spire
www.durandaljs.com

@akircher

If you are just observing the function's properties, I don't think that would be a problem just omit the () for the binding. Or do you mean observe it being called?

<proposal ng-value="doWork" ng-click="doWork()">
@EisenbergEffect

You can observe it being called.

@caitp
Contributor
caitp commented Oct 31, 2014

he means the result of the function, yeah. it wouldn't make sense to "observe" the result of an ng-click handler, though.

@hfjallemark

How would that work with custom web components? E.g. <my-custom-component ng-something="isThisAProperyOrAnEventHandler">

@akircher

Oh okay, thanks I can see the reason now. Still seems like that type of thing should be handled in the class though rather than in the template. But defer to your ideas!

@hfjallemark that would be a property since you did not explicitly put (). If you had put

<my-custom-component ng-something="isThisAProperyOrAnEventHandler()">

then it would be an event handler for the "something" event

@caitp
Contributor
caitp commented Oct 31, 2014

you could always add event listeners from javascript --- it could even be made nice and declarative in AtScript, but this kind of sucks for ES5

@EisenbergEffect

All: We've spent some more time discussing this. @IgorMinar is going to post an update here with some brainstorming shortly.

@sheerun
sheerun commented Oct 31, 2014

I think embedding variable name in attribute's name (ng-repeat--<local-var-name-literral>) is seriously limiting and non-extensible. I can understand 1-1 mapping between attribute names and property names, but including property's value in attribute name seems really fishy for me.

We want ta make sure that all bindings starting with bind- have right hand side as a valid expression.

Not too good idea too. For example bind-src="http://${imageUrl}". Is that a valid expression? Probably not. On he other hand, right-hand side is always some kind of syntax interpreted by Angular.

@EisenbergEffect Proposed microsyntax of expression could be {{ expression }}. That's nice.

He proposed some kind of "expression filters", to achieve for example one-time binding:

value="{{ expression | bindOnce }}"

That's little verbose, but Angular could provide "shortcut" microsyntaxes:

  • value=":expression" => value="{{ "expression" | bindOnce }}"
  • value=".expression" => value="{{ "expression" | bind }}"
  • ng-repeat="item in items" => ng-repeat="{{ items | ngRepeat:item }}"

That could give us clear definition of microsyntax: a simple string -> string function that transforms string to proper expression (or the same string if microsyntax don't apply). Microsyntaxes could form a middleware for pre-processing attribute values. Sample microsyntax defintion:

@Microsyntax()
function bindOnceSyntax(expression: string): string {
  if (expression[0] == ":")
    return "{{ '" + expression+ "' | bindOnce }}";
  else
    return expression;
}

One could specify microsyntax for only one element like @Microsyntax({ element: 'ng-repeat' })

@EisenbergEffect What do you think?

@mhevery
Member
mhevery commented Oct 31, 2014

A lot of questions are around why we are changing the syntax. The reasoning is described here: https://docs.google.com/document/d/1kpuR512G1b0D8egl9245OHaG0cFh0ST0ekhD_g8sxtI/edit#heading=h.xgjl2srtytjt

@caitp
Contributor
caitp commented Oct 31, 2014

I think it's likely people here might have already read through that issue =)

@e-oz
e-oz commented Oct 31, 2014

I understand reasons for changing but I don't see any difference between

bind-ng-repeat--item="items"

and

bind-ng-repeat="item in items"

while second variant more readable, predictable and still makes possible things like bind-ng-repeat="(key, value) in collection"

@caitp
Contributor
caitp commented Oct 31, 2014

bind-* isn't really compatible with the microsyntax from before, it's basically "you give me a path to a property, nothing else". I don't think it's worth it, tbh.

@e-oz
e-oz commented Oct 31, 2014

thanks @caitp for clarification.
Well, then, as I proposed already in that doc file:
2 properties:

bind-ng-repeat="people" bind-ng-repeat-item="person"

more words but without microsyntax.

@mhevery
Member
mhevery commented Oct 31, 2014

Constraints:

  1. Attribute names can only contain keys which pass element.setAttribute(key, value); This means no special characters in key. (Tricking the browser into setting the attribute has 36x performance hit: http://jsperf.com/setspecialcharectersattribute)
  2. There may be a shorthand syntax (such as {{}}) which may get converted to canonical syntax during HTML compile.
  3. The debug mode syntax is the canonical syntax. (i.e. we may chose not to translate #2, but if we do translate it needs to be same syntax, not yet another syntax)
  4. We need to work with web-components (This means that we can not place expression on the attribute names. The attribute name needs to be escaped somehow.)
  5. Need to be able to distinguish between string literals and expressions without knowing which directives are active. (There may be none, see #4)
  6. We need to have a simple microsyntax which does not violate #5.

Proposal for canonical syntax.

<ul on-click="doSomething()">
   <li bind-ng-repeat="people" ng-repeat-named="person">{{person.name}}</li>
</ul>

Proposal for shorthand syntax

The above syntax is too verbose, so lets apply shorthand equivalent syntax.

<ul on-click="doSomething()">
   <li bind-ng-repeat="people; named:person">{{person.name}}</li>
</ul>

We can optionally make it even shorter by opting for [] over bind-. This means that []/() is alternate but equivalent syntax to bind-/on-.

<ul (click)="doSomething()">
   <li [ng-repeat]="people; named:person">{{person.name}}</li>
</ul>
@mhevery
Member
mhevery commented Oct 31, 2014

@jamm Why not ng-repeat="item in items" Good question.

  1. We need to know if the item in items is literal or expression. This leads us to bind-ng-repeat="item in items" or [ng-repeat]="item in items".
  2. The micro syntax is a problem. Imagine an IDE trying to validate the expression. The IDE knows what expressions are and it can highlight them and allow click through. But the IDE can not know about micro syntax (unless it parses the directive, which is too complex). So from the IDE's point of view the above is not a valid expression. It is also not a valid expression from the point of view of the framework either. Either all expressions support item in items or none of them. The parser can't change the rules for a particular directive. This leads us to <div bind-ng-repeat="items" ng-repeat-named="item"> Notice there are now two. The first one is bind-ng-repeat this is an expression, we know that it is expression because of bind- syntax. The second is just ng-repeat-named which is string literal, we know it is literal because of lack of bind-.
  3. The above syntax sucks! so we need to do better. How about <div bind-ng-repeat="items; named:item">

No one is changing syntax for the sake of changing it. There are technical reasons why we need to change it. The rules are:

  1. The key must be escaped in some way. (Escaping value is not good enough.)
  2. The expression vs literal interpretation of the string needs to be done without any further knowledge outside the template and must be consistent.

The implication of this is that it throws a serious wrench to our micro-syntax. In angular v1.x you cant tell if an attribute as an expression or literal, and that causes all kinds of problems. One of which is that it is not compatible with WebComponents.

@EisenbergEffect

@mhevery I think we should continue to debate "1. The key must be escaped in some way." @IgorMinar and I are not sure that's a fundamental requirement. If we remove that constraint there may be some other options open to us. Let's keep thinking about it...

@mhevery
Member
mhevery commented Oct 31, 2014

@EisenbergEffect if you don't escape the key you will break constraint:

rule 3 (#133 (comment)) The debug mode syntax is the canonical syntax. (i.e. we may chose not to translate #2, but if we do translate it needs to be same syntax, not yet another syntax)

@EisenbergEffect

@mhevery Yeah, I know 😄 I guess I'm not entirely sure we need that constraint. I know that removing it can make things a bit asymmetrical, but I'm not sure that's a real problem. Mainly, I think that the attribute value-based syntax is nicer enough to justify doing that. (Ducks...)

@akircher

Does the new syntax still need to support bubble up events e.g, (^click)? Or is that no longer needed?

@mhevery
Member
mhevery commented Oct 31, 2014

@akircher yes I think we still need to distinguish between bubble and no bubble.

@fardelian

@mhevery If you make in an operator so x in y evaluates to an iterator there won't be any change in the way ng-repeat is used.

@caitp
Contributor
caitp commented Oct 31, 2014

it would be more like x of y for javascript compatibility, but it still kind of screws up filters. that's an interesting idea, though.

@caitp
Contributor
caitp commented Oct 31, 2014

@IgorMinar what do you think of something like that? then ng-repeat could still be a single attribute, and still be explicitly an expression binding.

@e-oz
e-oz commented Oct 31, 2014

I like idea of new micro syntax with semicolons as separators - it looks very native for HTML (as style declaration) and it can be converted into JS object just by adding { and } to string (and then interpolate).
Also as escaping: maybe let's use "ng:" prefix for Angular core directives? It will not prevent users to invent any custom names of directives (even will help them to add own prefixes in same manner).
So with 2 these proposals it can look like:

<div ng:repeat="named: person; in: people">
  <oz:example ng:on-click="sendGift()" ng:bind="person">Send gift to {{person}}</oz:example>
 <!-- bubble event and another variant for variable instead of mustaches --!>
 <oz:example ng::on-click="sendGift()" user:directive="person">Send gift to :person:</oz:example>
</div>

It doesn't even break HTML syntax :)

Also want to note, that micro syntax with semicolons is explicit, so you don't have to remember in which order you need to write variables to get result, especially when things are not trivial and have few variants of writing (as with ng-options).

@sheerun
sheerun commented Oct 31, 2014

The micro syntax is a problem. Imagine an IDE trying to validate the expression. The IDE knows what expressions are and it can highlight them and allow click through. But the IDE can not know about micro syntax (unless it parses the directive, which is too complex).

@mhevery How IDE knows ng-repeat-named="person" defines new variable without parsing directive?

How do you solve directionality?

If you use .person microsyntax for two-way binding and :person for one-way, now it's clear.

<ul on-click="doSomething()">
   <li ng-repeat=".people" ng-repeat--named=":person">{{ person.name }}</li>
</ul>
@sheerun
sheerun commented Oct 31, 2014

If you additionally use prefix for Angular properties, IDE knows which attribute values contain microsyntax.

<ul _on-click="doSomething()">
   <li _ng-repeat=".people" _ng-repeat--named=":person">{{ person.name }}</li>
</ul>
@caitp
Contributor
caitp commented Oct 31, 2014

honestly, the iterator thing is a really good approach:

  1. it lets people write custom ngRepeats if they want to
  2. it would work the same for ngOptions, if we want something like that (or any other repeatable construct)
  3. it has a consistent syntax
  4. it fits into the binding attribute syntax.
  5. it matches javascript syntax

This sounds like a win to me, tbh.

@fardelian

I'd like to expand on the idea given by @jamm by making it a JS object: <div ng-repeat="{name:person, in:persons, start:100, limit:20, step:-1}"> so now we can cram all the features we want in there and it resolves to a perfectly valid expression (an object) as required by Angular2. Alternatively, all this could be achieved with a custom JS iterator as in my first proposal, but I like the object way much more.

@rkirov
Contributor
rkirov commented Oct 31, 2014

Even if we support iterators in angular expressions, we need solve how to write other custom decorators that have a number of one-way bindings and attributes passing. Some more straw-man proposals.

Canonical Syntax proposal

Same as Misko's proposal but separating the instantiation of the directive from the bindings.
<div ng-repeat bind-ng-repeat-iterable=”people” ng-repeat-iter-name=”person”></div>

This is very explicit and very non-human writable.

Note: it is not clear from reading HTML alone that the compiler will be binding to the NgRepeat instance properties and not the DOM element properties for all bind-ng-repeat-* bindings. Should we make that more explicit in the canonical syntax?

Shorthand Syntax proposal

Given the instantiation attribute is boolean attributes in the canonical syntax, we can use the value as a macro. Macros are string only maps and lists that translate as follows
ng-repeat=”#A {[iter]: people, name: person}”
becomes ng-repeat bind-ng-repeat-iter="people" ng-repeat-name="person"

Positional attributes can be supported
ng-repeat=”#A [[people], person]”
becomes
ng-repeat bind-ng-repeat-0="people" ng-repeat-1="person"

Note: #A means we are doing purely string-based macros, i.e. any expressions as keys or values are not evaluated. I think it is important to make that distinction clear (someone can come up with better symbol for this, #A is the just the first thing that came to mind (attributes, angular, etc.))

@fardelian

@rkirov How would those macros be evaluated. What is the difference between evaluating #A {people, person} and person in people?

@caitp
Contributor
caitp commented Oct 31, 2014

I think we could combine the iterable name and iterator into a single construct, so that it could be reused by many different directives.

In 1.x, we return a function from parsed expressions, which acts as an evluator for the expression. Assignable expressions have an added property called "assign", which acts as an assignment to the path of the expression.

In this vain, if we see an iterator, the expression could become a function which returns the iterator (or tries to), and subsequently a function which gets the "scope name" of the iterator, something like this:

ASSERT(expr.iterator);
seen = new WeakMap;
for (let item of expr.iterator()) {
  if (!seenItem(item)) {
    addNewItem(expr, item);
  } else {
    seen.set(item, true);
  }
}
for (let item of existingCollection) {
  if (!seen.get(item) {
    removeItem(item);
  }
}

function addNewItem(expr, item) {
  seen.set(item, true);
  var element = template.clone();
  element.ng[expr.name()] = item;
  addToDOMLater(element);
}

I mean, it would take a bit more work to be fully fleshed out, it's basically pseudo-code, but it seems like not a bad pattern to me.

@akircher

@caitp I think I like it. Could you provide some examples of how this syntax would look outside of ng-repeat?

@rkirov
Contributor
rkirov commented Oct 31, 2014

@fardelian The macros just turn one string-based attribute into multiple string-based attributes, for the convenience of the html author. This is pre-angular land, there is no meaning to the strings beyond the simple constructs of <String, String> maps and lists, which the macro understands. It that sense it might be deceiving to write it in JS syntax.

After the macro runs, the compiler takes over all bind-* expressions and parses them to the full extension of the angular expression syntax. At this point, if we accept the person in people as valid expression, angular parses it and passes it to the NgRepeat instance (which is the line of thought @caitp is entertaining).

@caitp
Contributor
caitp commented Nov 1, 2014

@akircher I think it would look more or less the same --- the expression parser would give you a function which tries to fetch an iterator (eg, expressionText[Symbol.iterator] in JS), so you could easily iterate over each item in the collection. The name of the iterated value would also be accessible from the returned value, so you'd have an easy way to populate a scope. It would even be possible to have helper functions in angular which would automatically insert/remove HTML elements, and automatically update scopes of repeated children.

Using these tools, the implementation of ngRepeat could be turned into something like 3 lines, and easily implemented / extended by third party code, while most of the generic work would be handled by the framework itself (getting the iterators, iterating, updating scopes, etc)


Basically what I'm getting at is, any expression that looks like foo of FilterChain would return an iterator getter, which would be something like this in 1.x:

function getter() { /* retrieve the property at the referenced path, apply filters, etc */ }
getter.name = "foo";

And to use it in JS, you'd just do this:

for (let iteratedItem of getter()) {
  // operate on the iterated item
}

so it just extends the syntax of the expression language to include a concept of iterators, which would be bind-able.

@var-foo
var-foo commented Nov 1, 2014

+1 for the last comment @jamm made. I think that is very easy to understand and being able to wrap in braces to create a valid js object with no further work just feels right.

@mLaird
mLaird commented Nov 1, 2014

I have a JSON array that provides a dropdown for user selection (via ng-dropdowns), and upon selection need to use the JSON value to route to a new ng-view. I haven't solved this, but I realize that ng-route, a logical operation, expects an anchor tag , a view element, and this violates separation of function. In other words, the logic of routing currently depends on a view element construct. The new router should use only some defined string(s) character(s) - however and from wherever they are presented.

@e-oz
e-oz commented Nov 1, 2014

and one more thing: with that semicolon (or object, as @fardelian propose) microsyntax we can eliminate problem with collision of directives attributes.
For example, if we have 2 directives and both of them expect to have attribute "src" in element but we want to give them 2 different meanings of "src".
For example:
AngularJS 1.x

<media-box video-player src="video.mp4" subtitles="{{turnOnSubtitles}}" src="subtitles.txt">
</media-box>

and it's not valid code because of 2 "src" attributes.

With semicolon/object microsyntax:

<media-box video-player="src: video.mp4" subtitles="enabled: {{turnOnSubtitles}}; src: subtitles.txt">
</media-box>

and code is valid, because each directive have values inside isolated declaration.

@gdi2290
Member
gdi2290 commented Nov 1, 2014

@mhevery once said that angular was not a framework but an html compiler. What if angular used an html compiler for angular's html compiler? Users are fine working with JSX so why not allow us to use the updated html syntax which transpiling down to the text based syntax (for everyone else who doesn't like it). I'm just saying the api is starting to look bad going down this route so why not solve it the same way you solved di/annotations/types (going down to language level). I'm all about change and looking at a few of the new proposals compared to the original vision it's clear that the original idea was right (a lot more flexible). Remember the original vision for Angular? Angular 1.x allows you to use the future of the web (Web Components), today. I really like the vision for Angular 2.x since it's still holding the same values and beliefs. Others will understand more after using all the new tools and language extensions. There needs to be a clear path to 2.x and you could almost suggest a 1.5 (using es6) release to bridge the gap. Doing so would give everyone an opportunity to introduce new concepts to learn before atscript. On the other hand you have a whole year or so to teach people 2.x concepts to better prepare everyone for the future.

@ocombe
Contributor
ocombe commented Nov 1, 2014

I really like @jamm's semi colon/object syntax, it's clear and easy to understand for humans and parsers, you read it and you instantly know what it does (even if you have never done any angular before), yet it's not too verbose.
Also it's really easy to extend. We took the example of ng-repeat because it's familiar, but it only asks for one "special property" (item). With the media box example we can see that subtitles would need 2 special properties (or more): enabled and src.
I don't see any other syntax that would allow us to do that.

@stephanrauh

Please keep the syntax simple and self-explaining. The colon-and-semicolon-syntax looks eerie to me. Many programming languages have adopted the loops like "for x in y". So why do you want to get rid of the common standard in ng-repeat? I'm sure there are better ways to distinguish a loop from an ordinary string without using syntax that's hard to understand.

As for the ng: prefix: Please don't use the colon. JSF uses colons itself. As a consequence you'd make it impossible to use AngularJS in a JSF application.

@e-oz
e-oz commented Nov 1, 2014

@stephanrauh "ng:" was just for escaping (and looks sexy ;) but I think it's not a problem to use just "ng-".
But about "for x in y" - I don't agree we should write code and loops inside HTML, it should stay declarative.

@stephanrauh

But about "for x in y" - I don't agree we should write code and loops inside HTML, it should stay declarative.

Too late :). Mind you: what is ng-repeat if not a loop?

By the way, I'd suggest to make ng-repeat and ng-if proper tags. One of the AngularJS 2.0 design documents I read states these directive look a bit odd, because they reside in a tag but control the visibility of the tag. I agree. How do you like this proposal:

<ng-repeat var="employee" list="company">
   <tr>
       <td>{{employee.name}}</td>
  </tr>
</ng-repeat>
@dalcib
dalcib commented Nov 1, 2014

Why not just:

<element sameBindAttribute="[[expr]]" event="((doWork()))">

I like the @stephanrauh's proposal for ng-repeat

@ocombe
Contributor
ocombe commented Nov 1, 2014

The goal of this change is to keep the right side a valid expression @dalcib, this syntax would defeat this purpose.

@stephanrauh

I've been pondering about one-way-binding vs. two-way-binding and only-once-binding. AngularDart uses an annotation on the corresponding attribute of the Dart class. AtScript adds annotations to the AngularJS equation. Wouldn't it be better to use annotations instead of adding cryptic symbols to the attribute name or - as proposed above - to the value?

So instead of writing

<pui-datatable bind-value=".cars" bind-initialsort=":color">

we would write

<pui-datatable bind-value="cars" bind-initialsort="color">

using the following component (hint: I've copied the snippet from a component of my AngularPrimeDart project, so it's Dart code, not AtScript):

class PuiDatatableComponent {
  @NgTwoWay("value")
  List myList;

  @NgAttr("initialsort")
  String initialsort;
@EisenbergEffect

@stephanrauh That could be used to provide the default directionality for a property. However, there are use cases for needing to override that in the binding expression itself. My proposal above is to default properties to one-way but to allow an annotation like this to specify that the default is actually two way. Then to add binding options which would allow the override when deviation from the normal is required.

@EisenbergEffect

@dalcib Something like the [[]] syntax works for Polymer but this is because Polymer supports a much simpler expression syntax. However, at present, the Angular expression syntax allows for literal arrays and parenthetical expressions. As a result, it's not possible to tell whether or not the [] or () were intended to be part of the actual expression of just to identify it.

@akircher
akircher commented Nov 1, 2014

Thinking about this overnight I believe the bigger problem is that the current syntax only allows web components to be "consumers". They can set a property based on an already declared variable given to them, or they can consume an already created event.

However the current syntax doesn't allow web components to declare a new variable to scope or trigger a new event. So I propose we create two more generic prefixes: one that means to publish a variable and one that means to trigger an event. Just to get the conversation started say we name them **publish-*** and trigger-*.

publish-** is the complement/opposite of **bind-*_.
*_trigger-
* is the complement/opposite of on-*.

For ng-repeat this would result in:

bind-ng-repeat="items" publish-ng-repeat="item"

I think this may also solve one-way vs two way binding syntax by making it explicit

One way (view -> component): <input publish-value="name">
One way (component -> view): <input bind-value="name">
Two way: <input  bind-value="name" publish-value="name">
@thebigredgeek

+1 for everything @caitp is saying. I really think there needs to be more community involvement, personally, before flipping the entire framework upside down with huge breaking changes. Just my two cents

@e-oz
e-oz commented Nov 20, 2014

Thanks for link, @mhevery, didn't know that. Some intents are more understandable now. I agree it reasonable to use with ng-repeat, ng-switch and ng-if, because they "turns on/off" elements.

Sorry for being nervous - I, as all ng-fans, worry about these things not less than you :)

Have to say, I see slow movement to better syntax - current proposal is better, than initial. You (and we) just need little bit more genius ideas ;)

@DeborahK

@mhevery The great thing about Angular is that it is moving lots developers to the Web. They might not know about the new template tag.
Does the Angular team have any stats about its community and user base? How many are designers that find Angular so easy that they are dabbling in JavaScript? How many are students? How many are Rails developers? How many are Windows Forms/WPF developers dipping their toe into Web development for the first time? What percent are actually JavaScript gurus?
Might be interesting to know...
My Angular session at VSLive yesterday was standing room only. Up until recently, over 1/2 of the top 12 courses on Pluralsight were Angular. I'd like to see Angular continue to be approachable to all of these developers or future developers. :-)

@sheerun
sheerun commented Nov 20, 2014

I must say I love this new approach of discussing this problem.

Because of reasons I mentioned earlier many times, I firmly believe all directives in template should be prefixed in one or other way (mainly for dependency injection and ability to distinguish values that contain pseudo microsyntax). Also I think there's better way of writing templates than template="", as well as re-use short prefixes of pseudo microsyntax in attributes' keys.

I added new proposal in Angular's document :)

@vko-online
    <li template="{ base: 'ng-repeat', var: '#album', in: albums, index: '#i' }">

Looks bad, really

@caitp
Contributor
caitp commented Nov 20, 2014

@vko-online --- so the issue here (and this is something that we sort of have an issue with in angular 1.x) is making sure that things apply to the right element.

So, the rationale (as I understand it) is this:

  1. we see a template attribute, so what we're going to do is generate a <template> element, which will have attributes derived from items in the attribute string.
  2. the template sort of replaces the <li>, and the <li> itself is then used to populate the <template> (at the discretion of whatever directive).

So, in 1.x, we have this issue like, if you have a replace directive, we do attribute merging with the original host element, and mess things up (this is not quite the same issue, but it's sort of related), so I can see how we'd want to work around that.

One idea that I sort of like is this:

Given that we have a few magic attribute prefixes, bind-, on-, etc, we could also have variants of these prefixes, like so: template-bind- (or ^bind- or @bind- or t-bind- or x-bind- or whatever people like, it doesn't matter), so that you'd be able to separate "template" attributes from "element" attributes easily, while still using syntax that's baked into the web platform and has been for years.

How would something like that sound to you? I'm still working on hammering out a solid proposal which meets all of these constraints, but it would be good to hear how people would feel about that, versus the "magic template attribute" thing. The challenge with this is case sensitivity

@akircher

@caitp I really like template-* over template="" since it aligns better with the other syntax. You should check out my comments on the doc.

Unfortunately @mhevery did not like them because template-* allows for more than one template directive per element. I think this could be a helpful feature but this caused problems in 1.x. Although attribute order is "non-deterministic" the browsers I have looked at have all kept them, but this could mean a breaking change if browsers change this order in the future.

Maybe you can improve on my solution and solve these problems...

@caitp
Contributor
caitp commented Nov 20, 2014

Note that when I'm talking about template-*, I'm not referring to "directives" --- I'm referring to "the template is the target of whatever this attribute is, whatever that template may be". So it's slightly different.

Note that, if you have template="ngRepeat: #item; in: collection; trackby: item.id; ngIf: item.value, then you're still running into this "multiple template directives" thing --- sure, the ngIf wouldn't be very meaningful, but the point is that the magic syntax doesn't really prevent this.

One option is having a template attribute too which explicitly requests the instantiation of the template directive, maybe for short, just ng="repeat" or something. I think there are ways around this.

@akircher

@caitp I see, slightly different, but I like where you are going.

I have an unrelated clarifying question for the original problem of element.setAttribute('[foo]', 'exp') not working. Why is setAttribute() actually necessary? The only reason I can think of is for the compiler to recreate/copy a template element into a "new" DOM element.

Why can't you just grab the whole node as originally written by the author out of the template and insert it where you need it in the DOM. All the escaped/short-hand attributes will move along with it. And then you can manipulate the non-escaped properties/event handlers normally.

This seems like too simple of a solution. I am sure there is a good reason why this wouldn't work, but if someone would care to explain it to me I would appreciate it :)

@mprobst
Contributor
mprobst commented Nov 20, 2014

Coming to this a bit late, but regarding the micro-syntaxes I think a different approach would be possible. AngularJS 1.x takes pride that everything AngularJS is implementable as a directive itself, including ng-repeat, ng-if, etc. That's nice but also complicates things, i.e. compiling templates when new symbols are introduced by an ng-repeat.

An alternative would be to hard-wire the core structural elements of templating, much like programming languages hard-wire their syntax for for loops, function calls, variable declarations, etc. That's less powerful/dynamic for the directives themselves, but will overall give more power to AngularJS to understand templates and what's going on in them.

The number of constructs one would have to hard-wire is actually quite small - on the most basic level, you need:

  • repetition (ng-repeat)
  • conditionals (ng-if)
  • an inclusion mechanism (expansion of directives)

Plus everything that introduces new names into the template's lexical scope must be built in (e.g. ng-controller with as, but I think all of those things are wrapped into the directive controller's fields now, aren't they?).

I think this might deserve some exploration. Hard-wiring these primitives does not restrict what a directive can do overly, and it can simplify the entire system a lot.

@mhevery
Member
mhevery commented Nov 20, 2014

@caitp There are two issues:

  • if you write <div>{{foo}}</div> you need to convert it to <div [text|0]="foo|str"></div>. This can not be done in an efficient manner. We need to do this conversion so that when you view the DOM in the dev tools you can see the binding. What we don't want is to show the binding to you in yet another format.
  • Tools such as HAML will not be able to generate <div [prop]=exp>.

@mprobst We have considered that solution. The issues are:

  • There is no limit to the number of 'core' keywords. How about infinite repeater? Drag-able? custom selector? non-homogeneous repeater? and so on. It is really better to keep them open.
  • I think the current proposal strikes a good balance.
@popbee
popbee commented Nov 20, 2014

My 2 cents onto the attribute name syntax hunt:

Anybody considered using suffixes ?
Also what about using periods? Periods are valid XML.

 title.ng="contains {{ count }} items"
 title-ng="contains {{ count }} items"

Btw, using a period "." (not in the first position) appear to work fine with APIs like element.setAttribute("title.ng");

@EisenbergEffect

Do you know if that works with svg?

On Thu, Nov 20, 2014 at 11:23 AM, Bernard notifications@github.com wrote:

My 2 cents onto the attribute name syntax hunt:

Anybody considered using suffixes ?
Also what about using periods? Periods are valid XML.

title.ng="contains {{ count }} items"
title-ng="contains {{ count }} items"

Btw, using a period "." (not in the first position) appear to work fine
with APIs like element.setAttribute("title.ng");


Reply to this email directly or view it on GitHub
#133 (comment).

Rob Eisenberg,
President - Blue Spire
www.durandaljs.com

@mhevery
Member
mhevery commented Nov 20, 2014

@popbee period is an interesting idea. But it needs to work without any ng suffix. <div hidden.=true> Seems to me like the period get kind of lost. :-(

@popbee
popbee commented Nov 20, 2014

@EisenbergEffect It worked on SVG elements with the 3 browsers I have (chrome/firefox/recent IE). http://jsfiddle.net/ub96had5/1/

The period gives that "twisted feel" that something a bit special is happening to this attribute, yet I think people won't get scared starring at it (unlike [abc]="..." or (^click)="..." !). And since this is valid XML it can probably be generated from a lot of XML-building tools. If "-" is supported by the "tool", good chance "." is supported too. Mixing "-" and "." may also work visually: my-own-attribute.ng -- Of course, the period would need to have a special meaning. Like it is not really part of the attribute name.

@mhevery I agree with the period alone at the end is not good and just looks like a bug. After going through all the docs and all the posts, I missed the reason why we can't use "ng"?

@EisenbergEffect

That's interesting. What about something like this then:

  • property.bind="expression" or property.link="expression"
  • event.trigger="expression"
  • event.bubble="expression"
@mhevery
Member
mhevery commented Nov 20, 2014

not sure how property.bind="exp" is materially different from
bind-property="exp".

It's not that we cant use the ng- prefix, it is just that ng- prefix is
for core angular directives. A binding is a general problem which needs to
work with all directives (not just ng-) and with webcomponents which have
no prefix. For all practical purposes ng- is part of name ng-foo not
part of the binding system. So foo directive needs to be just as
bindable. Also library may declare my- prefix instead of ng- prefix. So
let
On Thu Nov 20 2014 at 9:20:35 AM Rob Eisenberg notifications@github.com
wrote:

That's interesting. What about something like this then:

  • property.bind="expression" or property.link="expression"
  • event.trigger="expression"
  • event.bubble="expression"


Reply to this email directly or view it on GitHub
#133 (comment).

@EisenbergEffect

There are a few differences:

  • By using the . you can more easily distinguish what is part of the attribute name vs. what is part of the binding syntax.
  • Related to the first point, you would be less likely to get odd naming schemes. Imagine that someone actually names an event on-tap. With the prefix and the dash you end up with on-on-tap="expression" as apposed to on-tap.trigger="expression"
  • In early discussions, my main argument against bind- was that it obscured the attribute name, particularly in an LTR grammar. It looks fine on it's own, but inside of a view with many expressions, it's harder to read. That's why I favored an operator suffix. I think it's easier to read and in some cases makes more grammatical sense. For example bind-if vs. if.bind or if.link.

It's just a thought. I think it would be interesting to see what a more substantial view looked like with this syntax.

@DeborahK

At a glance, I would prefer to see event.trigger = "expression" over (event) = "expression".
I still see the parens as either "evaluate the expression" such as (1+3) = 4 Or as a function parameter list. But I'm also fine with going with the "on" syntax here: onevent = "expression"

@Awk34
Awk34 commented Nov 20, 2014

@EisenbergEffect I like it. I especially agree with point two. Using the . notation makes it a lot clearer what's happening. At first I thought this might be confusing with the Object.Attribute sort of notation, but that's actually more of what is happening. It is representing what is being affected, and what is affecting it in a familiar context.

@popbee
popbee commented Nov 20, 2014

Yup, on second look, that period almost look like an <object>.<method> call. Also it somehow gives a "standard" visual location that describes what is the magic that is going to be applied:

 thingy.magic = "magic-specific microsyntax"

It's just a thought. I think it would be interesting to see what a more substantial view looked like with this syntax.

Same feeling here, this is not the entire solution, it is just another option to juggle with.

@Alxandr
Contributor
Alxandr commented Nov 20, 2014

@EisenbergEffect I'm also in favor of the .type suffix annotation. Except, please don't call it event.bubble. This could also enable property.bind-once quite easily (if that is something wanted in the end).

@EisenbergEffect

Perhaps event.delegate would be a better name. It's just an idea to play around with.

@akircher

@EisenbergEffect +1 for .type suffixes!

@vlazar
Contributor
vlazar commented Nov 20, 2014

Nice idea @popbee

Does anyone feels the need for the case like parsing template both by Angular and some other framework/library which probably also uses . but for it's own purpose?

If the attribute names would be unchanged after compilation by Angular, then it can be an issue for this other framework/library. That's why I was worried about prefixes (or suffixes now) for all Angular syntax (not only directives). See my comment #133 (comment)

Syntactically this looks really nice though @EisenbergEffect:

  • property.bind="expression"
  • event.handle="expression"
  • event.delegate="expression"

The use of . or () and [] overall feels like Angular is trying to take the cool syntax, so that when (and if) we need interoperability with some framework/library alter, then less popular library needs to adopt to the fact that . or () or [] is already in use by Angular. Kind of like jQuery became very popular, so $ was taken for jQuery. Sorry other libs. Or Underscore.js took _. I hope the idea is clear.

@vlazar
Contributor
vlazar commented Nov 20, 2014

BTW, just noticed this commit landed fc5b7ed

Are you guys still expecting community to continue exploring ideas here and making proposals in google docs document or is this syntax kind of final (at least for now)?

Please do submit your ideas and counterproposals in the doc Misko shared: https://docs.google.com/document/d/1HHy_zPLGqJj0bHMiWPzPCxn1pO5GlOYwmv-qGgl4f_s/edit#heading=h.94shrydkae0v

@mhevery
Member
mhevery commented Nov 20, 2014

Until we release v1 and have to worry about backwards compatibility
everything is changeable. We just don't want to be blocked, so we are going
with the best proposal so far.

On Thu Nov 20 2014 at 11:46:53 AM Vladislav Zarakovsky <
notifications@github.com> wrote:

BTW, just noticed this commit landed fc5b7ed
fc5b7ed

Are you guys still expecting community to continue exploring ideas here
and making proposals in google docs document or is this syntax kind of
final (at least for now)?

Please do submit your ideas and counterproposals in the doc Misko shared:
https://docs.google.com/document/d/1HHy_zPLGqJj0bHMiWPzPCxn1pO5GlOYwmv-qGgl4f_s/edit#heading=h.94shrydkae0v

Reply to this email directly or view it on GitHub
#133 (comment).

@popbee
popbee commented Nov 20, 2014

The use of the . appears possible in both CSS attribute selectors and JQuery DOM queries by escaping it with \2e. (more broad testing is required to assert this fully of course). Not the nicest, but it does work, I assume doing this type of stuff would be rare for the typical developer anyways.

var eventhandles = $('[event\\2ehandle]')

css:

[event\2ehandle] { 
    color:red; 
}

http://jsfiddle.net/ub96had5/2/

@sheerun
sheerun commented Nov 20, 2014

Just one note about counterproposals evaluation: I think @mhevery got very emotional when reviewing my counterproposal, and rejected it very soon. I think it's because he's reviewing counterproposal for his idea. Maybe it should for for 3rd party to reject proposals.

@e-oz
e-oz commented Nov 20, 2014

by escaping it with \2e.

Before this I liked dots-notation, but I really disagree to use so ugly \2e escaping.

  1. If ng-animation will still use CSS in 2.0, CSS with such selectors will look ugly;
  2. Any ugliness brings entropy, so I'm sure these small \2e will grow into something very ugly later.
@mhevery
Member
mhevery commented Nov 20, 2014

Hi Adam, I am sorry if I came across as emotional.

Your proposal was rejected on technical reasons because it does not work. I
have detailed the issues in the document.

You can fix your proposal and ask me to look at it again, but first I want
you to really understand my counterarguments as to why it will not work.

On Thu Nov 20 2014 at 3:26:44 PM Adam Stankiewicz notifications@github.com
wrote:

Just one note about counterproposals evaluation: I think @mhevery
https://github.com/mhevery got very emotional when reviewing my
counterproposal, and rejected it very soon. I think it's because he's
reviewing counterproposal for his idea. Maybe it should for for 3rd party
to reject proposals.


Reply to this email directly or view it on GitHub
#133 (comment).

@EisenbergEffect

@jamm I'm not sure you would write CSS for the dotted attributes, unless you wanted to actually style things that had expressions only. In other words, unless you wanted to make a developer tool that styled Angular-bound elements. Instead, you would still write your CSS for the normal attribute which would be set by the binding system itself.

@e-oz
e-oz commented Nov 21, 2014

@EisenbergEffect very simple example: ng-show currently contain expressions and is often used in ng-animation selectors.

@e-oz
e-oz commented Nov 21, 2014

spamming @matsko in case if I understood something wrong (happens often, hehe). Please, Matias, if you have 2 minutes, share your opinion about escaping in CSS selectors.

@kristianmandrup

OMG! This thread will never end... at least not before Xmas ;)

@EisenbergEffect

@jamm Is ng-show the only case where that matters? That directive could easily be implemented to update the attribute on its associated element, if that were the case. Are there other cases where targeting a decorator directive for css is common?

@nborelli

You can also escape the CSS selector as follows: [title.ng] { }
Here is the slightly modified jsFiddle: http://jsfiddle.net/nborelli/bv57ckk5/
Only slightly better though.

Still, I really like the dot syntax. It seems nice and clean to me and putting the name of the attribute you are binding first makes it easier to read.

property.bind="{{value}}"
event.handle="{{handler()}}"

I should add that it also seems to work fine with a colon character: http://jsfiddle.net/nborelli/bv57ckk5/1/. I like the dot better though.

@Alxandr
Contributor
Alxandr commented Nov 21, 2014

@nborelli As discussed multiple times in this thread already, colon does not work because if the document is interpreted as XML (XHTML), the colon will be seen as a namespace separator.

@nborelli

@Alxandr Yes, you are correct. I am new to the discussion and missed that. I need to read this huge thread more carefully. Still, I think the . escape in the CSS selector is a little better and like the dot syntax. As you stated earlier, it also allows for syntax like property.bind-once='{{value}}' although, I could also see binding behavior being part of the expression.

@Alxandr
Contributor
Alxandr commented Nov 21, 2014

@nborelli As also stated earlier in this thread, one of the points is that what's inside the attribute should only be a valid "expression" :)

@nborelli

@Alxandr Well, I can agree with that, but if we are going to have micro syntax for ng-repeat as discussed earlier, then why not allow something like this:

<element property.bind="expression" property.bind-options="{mode: 'once'}">
</element>

This would allow for additional binding behaviors to be be added at a later time and we have a syntax that looks like a JavaScript object.

With luck, I haven't missed something else in this thread, but I am not usually that lucky.

@nborelli

I am new to this, so I may be wrong, but would this work?

Simple binding:

<element property-bind="expression"></element>

Binding with options:

<element property-bind="{value: 'expression', mode: 'once'}"></element>

This way the single attribute could be used either way.

@popbee
popbee commented Nov 21, 2014

@nborelli I like the optional nature of the approach, although in this case, how do you differentiate between a generic expression and the "{value: 'expression', mode: 'once'}" (which is also an expression)? .. and probably it would rather be: (no quotes around the inner expression)

{value: expression, mode: 'once'}
@nborelli

@popbee Yes, I have been thinking about the same things. I don't know how locked down the expression syntax is at this point, so I don't know if something could be added to differentiate the two kinds of expressions. I think you may need the quotes around the inner expression though. If the inner expression itself had a comma in it, things could get pretty confusing. Can radical changes still happen in the expression syntax? For example:

<element property.bind="binding({value: 'expression', mode: 'once'})"></element>

Unfortunately, I still have the quotes around the inner expression though. Maybe some other micro syntax could help to get rid of them. I am sure someone smarter than me could think of a better expression syntax as it is really not my area of expertise. If this cannot be worked out, I guess the markup I proposed earlier might work, but it feels too heavy:

<element property.bind="expression" property.bind-options="{mode: 'once'}"></element>
@EisenbergEffect

I'm not sure if I proposed it here, but I had the idea of embedding binding options as part of the expression syntax in the same way that filters were in the past. Here's an example:

<input type="text" value.bind="expression & two_way & trigger:'blur'">

In the way that a filter is a function that operates on the output of an expression, the binding option is a function that operates on the binding itself to change its behavior from the default.

@nborelli

@EisenbergEffect Could the ampersand be interpreted as part of the expression? If this is not a concern, I like it. But I wonder if this would not be better as:

<input type="text" value.bind="expression & mode:'twoway' & trigger:'blur'">

Where the mode can be 'once', 'oneway', 'twoway', etc.

@EisenbergEffect

@nborelli I don't think the single & is being used by the expression syntax currently. I haven't looked in a bit though. You could certainly have mode:'twoway' That's basically what I imagined two_way would do under the hood. It's just a bit shorter syntax for a common case. Either way though...

@nborelli

@EisenbergEffect Could some other character besides the ampersand be used? Ampersand comes with some XML escaping issues, etc. How about the # character?

<input type="text" value.bind="expression # mode:'twoway' # trigger:'blur'">

I guess the whitespace could also be removed so it feels like hashtags 😄

<input type="text" value.bind="expression #mode:'twoway' #trigger:'blur'">
@e-oz
e-oz commented Nov 21, 2014

(optional) separators for expressions already proposed in doc Misko linked above.
I think I'm loosing difference when we discuss syntax and when semantics... Too many buzz about "{may favorite symbol} over dash", imho.

@akircher

@EisenbergEffect @nborelli
As @jamm said there is suggestion in the doc to extend the "alternating word" micro syntax from templates only to all bindings. This would allow for something like

<input type="text" value.bind="expression mode 'twoway' trigger 'blur'>

This would allow bindings to have additional options & filters. Of course under the proposal you could use any additional delimiters that you like : , ; etc.

If you like it make sure you +1 the comment. It has the promise to make the cut since @mhevery seems to like it.

@nborelli

+1 for extending the "alternating word" micro syntax from templates only to all bindings. As long as you can separate these alternating words from the expression when parsing.

@jamm I would be fine with a dash instead of the dot for the attribute name, but the dot seems more unique for attributes that already contain dashes.

<element cool-property.bind="expression mode:'twoway' trigger:'blur' ">

seems easier to tell that there is a binding than:

<element cool-property-bind="expression mode:'twoway' trigger:'blur' ">

But it is a really small difference IMO.

@popbee
popbee commented Nov 24, 2014

@jamm Buzz and huge thread is the price to pay here. Yes, it's difficult sometimes to follow because it is multi-faceted and long but it's worth every penny IMHO. It's quite refreshing that everybody here appears calm yet passionate for this to succeed. The syntax is essential to Angular's 2.0 acceptation & love. Keep the ideas flowing! 👍

@markmeeus

Why not use an inline-css-like approach and put all things angular in a single attribute.
The big pro is that you'll have ultimate freedom of the syntax you want to use inside of it.

    <p ng='cool-property:[item.coolValue, mode=once]; 
           onclick:[goGoGo]; 
           repeat:[item in items]' />

You can make the name of this attribute configurable so that when someone builds a web component with an 'ng' attribute, one can override angular's default to use another name.

    <nogo ng='the nogo component' ng-ng='ng:[item.nogood, mode=once]' />

This approach would need an api to interact with the elements bindings.

    element.removeBinding("cool-property");
    element.addBinding({"cool-property":"[item.coolValue, mode=twoWay]"});

This kind of abstraction of the bindings would then allow the (html attribute)syntax to be completely separated from the underlying mechanism.
You could then decide to support the 1.x syntax in this attribute (at least for a while):

    <img ng='!1 ng-src="{{image.url}}" 
              alt="{{image.alt}}"'/>

Migrating ng-attributes into ng="" seems easy to automate as well.

And once you are here, having an option (as a way to help migrating 1.x code) to rewrite the old syntax to then new one in the DOM should be a no-brainer.

@popbee
popbee commented Nov 27, 2014

@markmeeus This approach seems nice but also resemble some of the approaches described earlier. i.e.: <div ng:repeat="named: person; in: people">

I might be wrong, but at first sight it looks like most of the problem simply had "moved" into this single attribute. All the micro-syntax issues would remain. This has becomes some sort of "namespace". The only advantage I see is avoiding the clashing with attributes of the same name. It introduces some challenges: An additional "escaping layer" would be required. If I just look at your example with lots of double quotes, it does not seem parseable(?) Or is it?

element.addBinding({"cool-property":"[item.coolValue", "mode":"twoWay"]"});

Your syntax is CSS-like, but actually with the square brackets, it isn't like anything seen before and actually looks a bit like an array (but not quite). Like you said, you would require new APIs to deal with these special attributes. Angular Team can create some but sometimes HTML is generated on the server in all sorts of ways beyond the reach of the AngularTeam.

For the 1.x compatibility, it is an interesting idea, but since you had to change the markup it is not really... compatible! And since you are touching the markup, better to change it for 2.0 syntax directly! I would rather have a way to make a 1.x template work out of the box if possible. Angular Team did not give a path to migration yet, but I am confident that there will be. If not, the community will come up with something to greatly ease the pain.

@markmeeus

@popbee I wasn't exactly trying to define the syntax, just pitching the general idea of adding all angular things on an element in a single attribute.

Mainly because this way you can have complete control over the acual syntax/grammar. Which could potentially make the code much more readable.

I should have read my own code another time before posting it, the quotes were completely messed up.
I edited my original comment, so it makes more sense.

@popbee
popbee commented Nov 27, 2014

@markmeeus Ok. But whichever syntax we pick it will nonetheless force adding a layer "of escaping" which might not help for readability. Besides, having a large attribute value does not always work nicely if you want to split in multiple lines in the source code (for readability purposes).

@mhevery
Member
mhevery commented Nov 27, 2014

It seems to me that everyone has a pet syntax which is no better or worse than the one we have proposed.

What I would like to see more of is discussion about semantics. None of this discussion addresses any of the key points discussed in the document. These are:

  1. working with WebComponents,
  2. clearly identifying all of the expressions without knowing the semantics of active directives,
  3. clearly identify locations where the DOM structure can mutate,
  4. clearly identify where new variables are introduced,
  5. support for microsyntax.

Wether we chose <span bind-hidden="exp">, <span [hidden]="exp">, <span hidden.bind="exp"> or something else is secondary.

@kristianmandrup

After having seen all the recent talks on Polymer and web components... why would we ever want to have a document with various different types of "web components"? wouldn't it just be a bit of a mess and super confusing? Especially if each framework uses a different micro-syntax.

@mhevery
Member
mhevery commented Nov 27, 2014

WebComponents standards have nothing to say about data-binding. WebComponents deal with how Browsers create new elements which extend the behavior of the DOM. Databinding is a concern of a framework not that of the standards.

@akircher

@mhevery Overall I really like the Team's proposal. I only have some small questions/suggestions.

  1. I like the alternating word microsyntax. But I am a little worried that in the template attribute, the directive's name (e.g. ng-repeat) looks the same as a keyword, and might get "lost". Could angular 2 force template directives to appear in actual template elements and throw an error if the author puts them elsewhere? (authors could shim the template tag for IE until it supports it). e.g., <template ng-repeat="item in items"> works. <div ng-repeat="item in items"> throws.
  2. I would like the alternating word microsyntax to be available for the bind-* attributes, so even if not needed at first we still have the possibility of implementing something such as <input bind-value="firstName on blur type two-way">. This also works better paired with 1/ because then the first word on both bind and template syntax would be a variable to bind (in the current template syntax the first word is the directive's name).
  3. Syntax bikeshedding, but why were def=* and # used for declaring variables? Something like var-, let-, and $ seem more aligned with javascript.
  4. Although the annotation declarations for directives may have less "surprising" behavior than in 1.x, the current configuration looks a little intimidating and seems just as complicated as the DDO of 1.x. I don't know enough about the inter-workings to make specific recommendations, but hope we can move some of the complexity out of the annotation and into the template (or into conventions) instead. Here are some ideas:

4a) As I mentioned in the doc, I know a global registry is complicated but could we move the annotation's "directive" and "selector" property to the template by using html imports. Is it worth discussing pros/cons of this in a separate doc?

4b) Does the template property of the annotation really need new TemplateConfig()? Couldn't this just be done implicitly? Or the properties in templateConfig be put in the template itself?

4c) Per issue #249, I think I understand the distinction of component, lightDom, & shadow services, but this seems overly complicated. For the latter two, “Element Injectors”, why can’t a parent element just declare a directive (via one of its methods/properties) using the def-* or #, so that the children elements can then access them.

4d) I am sure there are more/better ideas to further simplify the annotation API

Happy Holidays!

Adam

@Alxandr
Contributor
Alxandr commented Nov 28, 2014

@akircher Just one comment on what you said. You probably shouldn't replacec # with $, because $stuff is actually a valid variable name, whereas #stuff is not.

@jonrimmer

I have added a new syntax proposal.

It isn't radically different to the team's own proposal, as the constraints make such a thing difficult, but it introduces two key ideas:

  1. An explicit "ng-" marker prefix for data bound elements in raw HTML:
<ng-input type="text" bind: value="expression" on: change="fn"></ng-input>

Depending on the browser's support for web components, Angular's parser/compiler will either register a new, matching element that proxies the bound element via shadow DOM, or replace it in the live DOM.

  1. "Attribute sections", demarcated by special attributes such as "bind", "on", etc. (which can take an optional ":" suffix). This allows special types of attribute, expressing bindings to properties, events, etc. without the need for prefixes on every attribute such as "ng-bind" and "ng-on", and inventing opaque new shorthand syntax for every new attribute type.

For example, one way bindings in either direction could easily be added as "bind-in" and "bind-out" attribute sections, without having to invent new prefixes and shorthand like "ng-bind-in-property" and "[+property]".

This all may be horrible/unimplementable, but I figured I'd throw it out there.

@kaleb
kaleb commented Dec 3, 2014

I'm not sure how this affects SVG, but what about attr:="val" and attr_="val" type syntax? Colons are allowed and don't necessarily mean namespace, it's just asked not to use colons and reserve them for namespaces.

@EisenbergEffect

Unfortunately := doesn't work in Internet Explorer.

@vko-online

why not ng-x-attr?

@JeroMiya

Incompatibility with html5 xml serialization (or, more broadly, any reasonable html preprocessing tool like htmlmin, etc...) is a deal breaker. Please add this as a constraint. One possible mitigation is an alternate xml-friendly syntax, but why, when there have been multiple xml-friendly proposals? Just pick a syntax that works for both xml serialization and html5 and go with it. Also, please note, xml serialization of html5 is not dead and is sometimes used as an intermediate template format for various html preprocessing and validation tools and editor exensions, many of which are internal. Regardless, it is inappropriate for the angular team to take a hard anti-xml stance.

Secondly, microsyntax. Nothing wrong with it. I don't care that IDEs will have a hard time supporting it. They never truly supported any of the binding expression syntax of this generation of frameworks either, so it's no loss. I would rather have a readable ng-repeat like comprehension syntax than one of the several awkward binding syntax proposals for ng-repeat I've seen so far. The comprehension syntax is easier to read, by far.

@mhevery
Member
mhevery commented Dec 12, 2014

Not sure how you came to the conclusion that we have anti-XML stance.
Supporting haml, XML is a constraint and should be supported.
On Fri, Dec 12, 2014 at 9:13 AM Sleepy Daddy Software™ <
notifications@github.com> wrote:

Incompatibility with html5 xml serialization (or, more broadly, any
reasonable html preprocessing tool like htmlmin, etc...) is a deal breaker.
Please add this as a constraint. One possible mitigation is an alternate
xml-friendly syntax, but why, when there have been multiple xml-friendly
proposals? Just pick a syntax that works for both xml serialization and
html5 and go with it. Also, please note, xml serialization of html5 is not
dead and is sometimes used as an intermediate template format for various
html preprocessing and validation tools and editor exensions, many of which
are internal. Regardless, it is inappropriate for the angular team to take
a hard anti-xml stance.

Secondly, microsyntax. Nothing wrong with it. I don't care that IDEs will
have a hard time supporting it. They never truly supported any of the
binding expression syntax of this generation of frameworks either, so it's
no loss. I would rather have a readable ng-repeat like comprehension syntax
than one of the several awkward binding syntax proposals for ng-repeat I've
seen so far. The comprehension syntax is easier to read, by far.


Reply to this email directly or view it on GitHub
#133 (comment).

@kpentchev

Just my two cents on the syntax discussion: one of the biggest advantages of AngularJS was the simple and HTML compliant syntax. Instead of extending the expression language or introducing an entirely new expresion language I would prefer learning a FEW new attribute prefixes, e.g.:

  • ng-sync-value="..." (two-way binding of the value property)
  • ng-bind-value="..." (one-way binding of the value property)
  • ng-set-value="..." (once off ...)
  • ng-on-click="..." (event binding)

While this syntax is somewhat more verbose it is defiitely simpler from my POW than an expression language. Plus it has the advantage of using key-words to describe the binding mode, while simple characters like '.' '::' etc don't mean anything actually and will be harder to adopt and to read.

@ewinslow

I figured I might as well join the fun. I wrote up a proposal of ideas inspired by ES6, which I believe passes all the latest criteria:

https://docs.google.com/document/d/1f0gRhFKSzcT20VwG34RNpvwJznXVFS9n7w_-v_2_QhM/edit#

Summary of ideas (which can be independently discussed/accepted/rejected):

  • No microsyntaxes. Just some extensions to the expression language.
  • ${expr} for string interpolation shorthand (instead of {{expr}}).
  • item of items for iteration
  • array/object destructuring for terseness
  • prop-* to set properties instead of bind-*
  • limited version of arrow function syntax for use in event handler expressions -- ($event) => ...
  • Limited use of spooky characters in HTML attribute
  • Some kind of ".suffix" reserved for special angular stuff like directive configuration or exporting controller variables.

Here's a little example snippet inspired by the "artists" example from Misko's current proposal:

<ul t-if="albums.length">
  <li t-for="[i, album] of albums.entries()">
    ${i}. Album: ${album.name}
    <ul t-if="album.tracks.length">
      <li t-for="[j, track] of album.tracks.entries()">
        ${i}-${j}: ${track.name}
        <button on-click="() => player.play(track.src)" prop-disabled="!track.src">
          play
        </button>
      </li>
    <ul>
  </li>
</ul>
<media-player .var="player"></media-player> ${player.time}
@codemata

Hello Everyone, just wanted to say - thank you dev ninjas at google for bringing AngularJS on the scene. I love the declarative approach to developing on the DOM.

I'm very new to AngularJS - actually just a week in... I've been having a hard time conforming with the api structure and syntax. When I a came across video today on AngularJS 2.0 - I was super excited about the changes. I feel current approach is sort of convoluted in how it's layered on top of the DOM but the new approach seems like your working with the DOM for a more native approach. Nice!

Anyways going back to the topic... during my search for a possible alpha/beta release of angular 2.0, I came across this discussion. As I mentioned earlier, Im far from an AngularJS pro but I feel my input as someone just learning Angular may be useful, especially in the scenario of a developer migrating to a new stack. I've also been a UX designer for the past few years that I can't help but question what my experience has been to this point.

After reviewing the comments (most not all) I came to the conclusion that the original proposition was probably the one I like the most (visually)- but as there are standards to conform to. I came to like the example above by @ewinslow more than the more recent proposed 'template' approach being considered.

as a developer, I like the idea of using reserved/key words to indicate logic in html syntax.

I've decided to modify @ewinslow proposition for what I - myself - consider to be more contextual and slightly less verbose.

<ul a-if="albums.length">
    <li a-for="(i of albums) albums.entries()">
        ${i}. Album: ${album.name}
        <ul a-if="album.tracks.length">
            <li a-for-if="[(j of tracks) album.tracks.entries() , album.track.show]">
                ${i}-${j}: ${track.name}
                <button a-event="(click) element.player.play(track.src)">
                   play
                </button> 
            </li>
        <ul>
        <ul a-else-if="album.track.length > 10"><li> more than ten tracks </li></ul>
        <ul a-else><li> no tracks </li></ul>
    </li>
</ul>
<ul a-else><li> no albums </li></ul>
<media a-element="player"> </media> ${player.time}  
<input type="checkbox" a-var="!albums.length">
@DeborahK

Because of Angular's current expressiveness and approachability, there has been an influx of people trying out Web development that have not before. This includes lots of devs from the Microsoft community that "hated" Web development, PHP devs, and Web designers that found they could understand Angular.
I believe that this approachability is due to Angular's current syntax, which is light on the syntax and minimizes symbols such as braces and parens. The more syntax that we introduce ... the less approachable and more error prone it will become.
As a Web developer since the "Internet bubble" in the '90's and a speaker/training, I see people struggle with technologies. Yet with the current version of Angular, I see them be successful with very little in the way of training/introduction. The more "syntax" we add here the less expressive and approachable it becomes.

@mvolkmann

I agree with Deborah!

On Wed, Dec 31, 2014 at 12:58 PM, Deborah Kurata notifications@github.com
wrote:

Because of Angular's current expressiveness and approachability, there has
been an influx of people trying out Web development that have not before.
This includes lots of devs from the Microsoft community that "hated" Web
development, PHP devs, and Web designers that found they could understand
Angular.
I believe that this approachability is due to Angular's current syntax,
which is light on the syntax and minimizes symbols such as braces and
parens. The more syntax that we introduce ... the less approachable and
more error prone it will become.
As a Web developer since the "Internet bubble" in the '90's and a
speaker/training, I see people struggle with technologies. Yet with the
current version of Angular, I see them be successful with very little in
the way of training/introduction. The more "syntax" we add here the less
expressive and approachable it becomes.


Reply to this email directly or view it on GitHub
#133 (comment).

R. Mark Volkmann
Object Computing, Inc.

@e-oz
e-oz commented Dec 31, 2014

I agree with @DeborahK but also I think programmers should be ready to learn new things. Whole life. And even if technology is not as easy to understand as PHP for newcomers, it still can be as powerful and popular in enterprise software world as Java. I'm sure team can find good balance :)

Happy New Year! 💫

@nilskp
nilskp commented Jan 3, 2015

Not sure if this is a recent change, prompted by this, but Jade seems to support both parentheses and brackets as attributes.
http://jade-lang.com/demo/

@pnazeran
  • I agree with DeborahK: expressive is better than syntactical
  • but syntax could mean shorter code too. So it is a compromise between typing and approachability

$scope variables may appear inside the brackets <> or outside.

  • Outside
    • the content of ${...} can be assumed a java-script expression as all binding would be one-way.
    • $ reminds most of the linux command prompt, where expressions are entered, so I prefer it to {{...}}
    • these are the general use cases I can think of:
      • Hello Visitor
      • Hello ${user_name}
      • Hello ${(a == b ? user_name : 'Visitor')}
      • Hello ${myFactory.getUserName()}
  • Inside: Attributes
    I want to second kpentchev's proposal for attribute assignments, but without the ng- at the beginning. Something like:
    • on-click="..." (event binding)
    • sync-value="..." (two-way binding of the value property)
    • bind-value="..." (one-way binding of the value property)
    • set-value="..." (once off ...)
    • value = "..." for string literals

As was pointed out, it is important that:

The syntax must clearly distinguish between 1) string literal; 2) expression (binding); 3) expression (events) 4) declaration of variables.

  • on-click: always an expression:
    • on-click = "click()"
    • on-click = "text.style = 'bold'"
  • sync-value: a variable name otherwise two-way binding won't work.
  • bind-value: a variable name or expression, so can be processed as expression
    • bind-value = "user_name"
    • bind-value = "myFactory.getUserName()"
  • set-value is just a special case of bind-value.

There may not be any ambiguity regarding string-literals, expressions, etc. as kpentchev's proposal clarifies that for attributes, and takes care of one-way and two-way

@circlingthesun

@EisenbergEffect's syntax for Aurelia looks really nice.

@gdi2290
Member
gdi2290 commented Jan 26, 2015

+1 for Aurelia

@PascalPrecht
Member

I think all syntaxes by Aurelia have already been proposed here by @EisenbergEffect. Please stop voting them up here.

@mhevery mhevery added the question label Jan 28, 2015
@martinmcwhorter

This may be late to the conversation, but I would like type annotations available in templates:

<form ng-type="vm.person: IPerson">
    <input type=text ng-model="vm.person.firstName" />
    <input type=text ng-model="vm.person.lastName" />
</form>

This could then be validated by an IDE or build time gulp/grunt task.

AtScript or TypeScript:

export interface IPerson {
    firstName: string;
    lastName: string;
}

The ng-type attribute could be removed from the template by the build task, as it would only be needed for development and build. Otherwise, it would get ignored in production.

Could this help IDEs with other issues with trying to understand angular's template system?

@tonypee
tonypee commented Feb 17, 2015

I think the reason people are voting up the Rob's dot syntax, is that it looks good. Semantically i think it works? (it seems as flexible as [] or ()) So, i would implore the angular 2 team (mhevery?) To please listen to people's suggestions. Frankly, I've just seen the video on the 'alpha' release, and the syntax looks visually terrible. I hope never to have to write with that syntax

@e-oz
e-oz commented Feb 17, 2015

@tonypee I see only 2 votes and compared with size of thread is nothing.

I wonder why ngnl presentation and current docs about templates still promoting []()# as parts of new syntax, when opening post in this thread contains

element.setAttribute('[foo]', 'exp') does not work

and, especially

SVG requires valid XML and []()# is not valid XML.

If these problems are solved somehow and we just don't know - please let us know.

@tonypee
tonypee commented Feb 17, 2015

@jamm the 'voting' followed positive multiple comments support a prefix/suffix style of defining binding. Originally, @EisenbergEffect suggested bind- on- etc. with 4 people supporting (@PascalPrecht, @SanderElias, @donaldpipowitch, @yamafactory). Recently the dot suffix (which @mhevery suggested is semantically the same) has positive support of:
@DeborahK, @Awk34 , Alxandr, @akircher, @nborelli, @circlingthesun, @gdi2290, and now me

It seems that readability & usability might be taking a backseat to perceived restrictions. I feel the comments suggest that people would *rather a more elegant syntax.

I'll be interested to see some more full examples of the () [] syntax, as i can understand change is hard. But, initially perception is important too - and the . syntax looks more elegant and readable

@gdi2290
Member
gdi2290 commented Feb 17, 2015

@tonypee Actually if we're voting or anything I'll withdraw mine. I'm in favor of the current syntax using (event)="exp" and [property]="val" after using angular2 to create a todo app it I found it's pretty clear. Keep in mind you can also use bind-, on-, and def with angular2's current syntax since ()[]# are considered shorthand and element.setAttribute('[foo]', 'exp') problem is solved by using their facade API for DOM aka using DOM.setAttribute(element, '[foo]', 'exp')

@Awk34
Awk34 commented Feb 17, 2015

@gdi2290 Thanks for the example code. I think I agree with you. As long as a new can use bind-property="val" as to make the syntax easier for beginners, and an experienced user can use the shorthand [property]="val" for more readable and terse code, I think it's a good solution.

@DavidSouther

I like the []()# syntax markers aesthetically, but I would like confirmation or refutation from @mhevery @IgorMinar et al that they will not break SVG or broader XML interoperability.

@petebacondarwin
Member

Read the summary of the syntax here: https://docs.google.com/document/d/1HHy_zPLGqJj0bHMiWPzPCxn1pO5GlOYwmv-qGgl4f_s/edit#heading=h.4w4k319ilnp8

There are two forms - shorthand (all these [...], (...) and so on) and the canonical form (bind-, on- and so on). The canonical forms are basically what you could use if you are using Angular 2 inside SVG or X(HT)ML documents.

@DavidSouther

@petebacondarwin That document has evolved quite a bit since I last looked at it :) Thanks for pointing out the updates.

@e-oz
e-oz commented Feb 17, 2015

thanks @petebacondarwin, finally it's resolved :)

@JeroMiya

@DavidSouther It is indeed true that the []()# attribute syntax is incompatible in an application/xhtml+xml document. Dots are, however, valid - e.g. on.click="foo".

I do not agree that the []()# attribute syntax is more readable than the prefix proposal. I think using sigils in general in place of keywords results in things that are terse but ultimately harder to comprehend. I feel the same way about sigil-heavy programming languages.

I also strongly recommend against having two different ways of writing templates. This is really confusing to new developers and results in inconsistent and hard-to-read templates as some developers write them one way, and others write them another. It reminds me of how some developers would use data-ng-* and others just ng-*, and the three different ways of doing dependency injection (one of which I had to ban, despite it being used in EVERY sample and talk on the internet). In this case I would have to ban the []()# syntax as our xml meta-templating tools wouldn't be able to parse them.

Given the lack of xml/SVG compatibility, I would say that the []()# syntax simply fails to meet the constraints, and should be eliminated altogether in favor of the prefix proposal.

@ldiego08
ldiego08 commented Mar 6, 2015

After reading the thread, these are my favorite suggestions:

  • Period notation for attributes.
  • JS object-like expressions for complex directives like ng-repeat.
  • Namespacing for prefixing attributes.
  • ${} enclosure for string interpolation.

Based on that, this is what I'm thinking of:

Template With Property Binding
<ng:template>
    <div ng:bind="{ title: expression1 }">
        <p>${expression2}</p>
    </div>
</ng:template>
Repeating, Event Binding, etc.
<ng:template>
    <ul>
        <li ng:for="{ each: 'item', in: items }">
            <span>${item.foo}</span>
            <button ng:on.click="deleteItem(item.id)">X</button>
        </li>
    </ul>
</ng:template>
Multi-line Lovers Compatible
<ng:template>
    <div ng:bind="{
            title: expression1,
            foo: expression2
         }">
        <ul>
            <li ng:for="{
                   each: 'item',
                   in: items
                }">
                <span>${item.foo}</span>
                <button ng:on.click="deleteItem(item.id)"
                        ng:on.blur="doSomething()">
                    X
                </button>
            </li>
    </div>
</ng:template>
@mhevery
Member
mhevery commented Mar 10, 2015

I know this is a sensitive topic to a lot of you. We have gotten a lot of good feedback from this discussion which was incorporated into our thinking, and so I think this thread has served its purpose, and will now be retired.

Please watch my presentation on this topic: https://www.youtube.com/watch?v=-dMBcqwvYA0 which tries to answer many of the questions.

@mhevery mhevery closed this Mar 10, 2015
@lukehutch

I'm late to the party here, but what about simply treating property binding as an event?:

<web-component [title]="exp1" (close)="exp2()">

would be

<web-component ng-on-bind="ngBind('title',exp1)" ng-on-close="exp2()">

where "ng-on-" would be a reserved prefix.

(As a bonus, it might actually be useful to be able to execute an arbitrary expression at bind time.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment