AngularJS 2.0: Sneak Preview on Data Binding

The AngularJS team presented a straw-man’s proposal for AngularJS 2.0 syntax one or two weeks ago. According to the proposal, AngularJS 2.0 is going to look substantially different from what we used to consider Angular, and nothing shows this better than the overhauled core of AngularJS: data binding. It’s a fresh new take on AngularJS, or, like Cagatay Civici tweeted, it’s almost like a new framework that just happens to have the same name.

He isn’t the only one to think so. Developers protested against the changes, and soon discovered several issues with the syntax. This article shows the original ideas nonetheless – I consider them interesting, even if they’re likely not to come.

Evolving W3C standards open new opportunities

So they decided to make a new start, dropping compatibility to AngularJS 1.x. And why not? I suppose the Angular team learned a lot about HTML and Javascript along the way, so they now know how to make things better. Who might have envisioned the flexibility of HTML concerning attribute names? Back in 2000, valid HTML attribute names were basically characters, digits, underscores and colons. By 2009, the W3C organization had added a lot of flexibility. Now, almost every Unicode character can be used in attribute names – this even includes Chinese and Devenagari characters (such as क्तु):

<div myAttribute[index] = ""
     ^[myAttribute]     = ""
     seño(ra|rita|r)    = ""
     क्तु                 = ""
     你好               = "">

AngularJS 2.0 makes a lot of use of this flexibility. The team invented a new system of data bindings that’s more logical than the previous system. It uses special characters to distinguish between different types of attributes. We learned from several Scala frameworks that ran havoc that using special characters tends to be a bad thing because special characters don’t do much in the way of explaining their meaning. I hope the AngularJS team has learnt the lesson and uses special characters carefully and sparsely. Changing the data binding syntax and dropping compatibility is a bold decision that may or may not work out. However, the current drafts look fascinating enough.

Update Nov 23, 2014

During the last couple of weeks the AngularJS team dropped the idea of using special characters. There were many obstacles: using special characters may be valid HTML, but that doesn’t mean browsers can deal with them. Even worse, many third-party tools don’t cope with special characters. However, it’s clear that the syntax will change. At the moment, there’s no agreement on the new syntax, so this article sticks with the straw man’s proposal. The details may change, but the ideas remain the same.

A short excursion

Before coming back to AngularJS in a minute, let’s put the flexibility of HTML attribute names to a test.

A quick glance at the BNF syntax definition gives you an idea of the huge allowed character set even if you never happened to learn BNF. The sheer length of the definition speaks for itself:

NameStartChar ::= “:” | [A-Z] | “_” | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] 
                | [#xF8-#x2FF]      | [#x370-#x37D]   | [#x37F-#x1FFF] 
                | [#x200C-#x200D]   | [#x2070-#x218F] | [#x2C00-#x2FEF] 
                | [#x3001-#xD7FF]   | [#xF900-#xFDCF] | [#xFDF0-#xFFFD]
                | [#x10000-#xEFFFF]
NameChar ::=      NameStartChar     | “-” | “.” | [0-9] | #xB7 
                | [#x0300-#x036F]   | [#x203F-#x2040]

This little AngularJS application let’s you experiment with what’s possible and what’s not. Before you ask: it’s good “old”1 AngularJS 1.3.

<!DOCTYPE html>
<html>
  <head>
    <script
      src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0/angular.min.js"></script>
    <script type="text/javascript">
    angular.module('attributeApp', [])
    .controller('AttributeReaderController', ['$scope', function($scope) {
      $scope.attributeList = [];
    
      init = function($scope) {
        var tag = document.getElementById("example");
        var attributes = tag.attributes;
        for (var i=0; i < attributes.length; i++) {
            var current = attributes[i];
            $scope.attributeList.push({"name":current.name, "value":current.value});
        }
        return $scope.attributeList;
      };
      
      init($scope);
    }]
    );
  </script>
  </head>
  <body ng-app="attributeApp" ng-controller="AttributeReaderController">
    <div id="example" 
         myAttribute="1"
         myAttribute[index]="2"
         ^[myAttribute]="3"
         seño(ra|rita|r)="4"
         क्तु="5"
         你好="6"
         myÆlement="7">
    </div>
    List of attributes in the div tag:
    <table>
      <thead>
        <td>name</td> <td>value</td>
      </thead>
      <tr ng-repeat="attr in attributeList">
        <td>{{attr.name}}</td> <td>{{attr.value}}</td>
      </tr>
    </table>
  </body>
</html>

Running the program yields more or less the output you’re probably expecting by now:

Even the Devenagari and Chinese characters can be used as attribute names.

But…

There’s a catch. Attribute names starting with “[” or “(” may be valid HTML, but that doesn’t mean your browser accepts them. Most browsers contain a bug preventing to set such an attribute via Javascript:

var test = element.getAttribute('[myattribute]'); // works
element.setAttribute('[myattribute]', 'value');   // doesn't work with most browsers

There are more issues with the syntax. I’ll discuss them after presenting the (no longer valid) straw-man’s proposal.

Attributes and properties

AngularJS 2.0 uses special characters to distinguish between different classes of attributes. At this point we have to be very careful about our language: the AngularJS team make a difference between attributes, properties and events.

Attributes are what you write in the HTML code:

<pui-tab title="What a nice tab!" closeable="true" closed="false">

In this example “title”, “closeable” and “closed” are attributes of the tag “pui-tag”. Attributes are always represented as a String, and that’s one of the reasons why the AngularJS team isn’t happy with treating every property of the tag as an attribute. The other reason is there are HTML attributes that influence the rendering engine by just being there – no matter which value they have. “disabled” is an example of such an attribute.

<input disabled="true">  <!-- This input field is disabled. -->
<input disabled>         <!-- This input field is disabled. -->
<input disabled="false"> <!-- Event this input field is disabled! -->
<input >                 <!-- This input field is editable. -->
<input disabled="{{controller.isInputDisabled}}"> <!-- Always disabled! -->

The third example comes as a surprise. The last example is a common trap catching AngularJS 1.x programmers every once in a while. AngularJS 2.0 sidesteps the problem by using the properties of the underlying DOM object. The attributes are used to initialize the DOM object. After that changes to the component are achieved by writing the DOM object’s properties. Conversely any changes of the component are visible in the DOM object’s properties, but not (or not always) in the attribute list. To make things more explicit, AngularJS introduces a special syntax to access the DOM object property:

<input [disabled]="controller.isInputDisabled">

The brackets around the property name indicate we’re talking not about an attribute, but about a property. This is a boolean property, so now the expression does what the programmer wants it to do: if the variable “controller.isInputDisabled” is false, the field is editable. Properties don’t have to be converted to strings. They can use the real data type.

AngularJS directives that are now longer needed

Using the property names directly makes several AngularJS directives superfluous:

  • ng-hide and ng-show are replaced by [hidden]
  • ng-href and ng-disabled simply become [href] and [disabled], respectively.
  • ng-class is replaced either by [class] or [class-list].
  • ng-bind-html becomes [inner-html].
  • ng-bind becomes [text-content].

This simplification eliminates the need to learn different names for AngularJS directives and the corresponding DOM attributes.

Events

Events used to be registered by special attributes, like ng-click to register a listener to a mouse click. Basically, event listeners are methods of the DOM object. Events are usually fired by the browser (mouse movements, keyboard events, swipes, page resizes, just to name a few). If you allow for user-defined events, such a predefined list of event listeners is too restricted. So AngularJS 2.0 drops the ng-* attributes in favor of round brackets surrounding the event name:

<button (click)="actionListener()">

AngularJS 2.0 adds an interesting twist by changing the standard behavior when a child element of the button is clicked. In standard HTML, the event “bubbles” up the DOM tree until one of the parents or grandparents of the clicked element has an event listener. If you don’t like bubbling, you have to prevent it actively. In AngularJS 2.0, bubbling is deactivated by default. If you need bubbling, add another special character to the event name:

<button (click)="^actionListener()">
   <img src="..." />
</button>

Attribute names and Javascript identifiers

CamelCaseDid you look carefully at the output of our character set test program? HTML converts every English letter to lowercase. The funny thing is, this doesn’t apply to German Umlauts. It even fails to work with exotic characters that are sometimes used in the English language, such as Æ.

Most Javascript variable and function names use camelcase between word boundaries. AngularJS 2.0 attribute names use a minus sign at the case boundary:

<button (^my-custom-event)="startCalculation()">

In this case, my-custom-event refers to the event named myCustomEvent. This convention isn’t entirely new: for instance, ng-repeat is implemented in a class called NgRepeat2 in AngularDart3. AngularJS make this convention a rule.

IDs

DOM elements are often referenced by ids, which leads to cumbersome code. AngularJS 2.0 introduces a new kind of ids which can be referenced in the Javascript code without further ado:

<img src="something.png" #the-image>
<button (^click)="theImage.hide()">hide image</button>

ng-if, ng-repeat and ng-switch

These directive are considered a bit odd by the AngularJS team. In fact, they are: they control the visibility of the element they reside in. It would be clearer to write

<ng-if condition="expression">
  <div>...</div>
</ng-if>

instead of

<div ng-if="condition">...</div>

These directives seems to be discussed heavily among the AngularJS team members, so they may change. In fact, the <ng-if> tag is my proposal, which has been inspired by a remark in the AngularJS 2.0 design documents. In particular, ng-repeat might use the special characters in the attribute name to denote the parameter. For instance,

<div ng-repeat="todo in todos">{{todo.task}}</div>

might become:

<div ng-repeat[todo]="todos">${todo.task}</div>

However, as far as I can see, the discussion hasn’t settled yet. In any case the syntax of ng-repeat is going to change. The team says the current syntax can easily confused with a regular string, so it can’t be generalized to other directives. Currently ng-repeat has a unique syntax that’s used nowhere else. You can’t use it for your own directives easily.

Bye bye mustaches!

AngularJS 2.0 is going to replace the mustache syntax “{{attribute}}” by a syntax that better fits in an Ecmascript 6 or JSP environment. Interpolated strings are now written as “${attribute}”. I suppose that will cause me some headache in AngularFaces: I can’t follow this move because “${…}” is already a valid, albeit discouraged syntax in JSF applications. By the way, I’m not sure AngularJS 2.0 replaces every mustache by “${…}”. The design documents don’t say it, but they left me with the expression that mustaches are used for typed expressions, while “${…}” is used for strings.

bind-one, bind-always and two-way

AngularDart uses annotations in the controller to indicate how closely the tag attribute is related to the property it’s bound to. You can define it as “bind-once” (the attribute is used to initialize the property), “bind” (every change of the attribute results in a change of the property) and “two-way” (every change of the property changes the attribute, too). AngularJS 2.0 will provide similar options. Most likely they will encode it in the attribute name or the attribute value.

Issues with the straw-man’s syntax proposal

As you can imagine, developers who’ve already invested in AngularJS aren’t happy with the syntax changes. Many voices heard on Twitter and elsewhere consider it a bad move.

Every changes raises resistance. Maybe developers could be convinced over time. However, there are more substantial problems. I already mentioned setAttribute() doesn’t cope with special characters. Third party tools don’t cope with it, too. HTML shorteners and IDEs run into problems.

Plus, the special characters aren’t self-explanatory. Remember, many Scala frameworks ran into the same problem. You have to learn what “(” and “[” means. They aren’t too much in the way of aide-mémoire. So chances are the final syntax is going to be slightly more verbose, such as “bind-property” instead of “[property]” and “on-event” instead of “(event)”. However, there are also issues with that solution, so it remains exciting to follow the discussion on GitHub.

Wrapping it up

It’s not easy to write a wrap-up of this article. The AngularJS 2.0 proposal raised a vivid discussion. The dust has yet to settle. I don’t think the radical proposal will make it. In any case AngularJS 2.0 may look substantially different from Angular 1.x. The change is so profound there won’t be a smooth migration path. Personally, I think the more logical and more powerful data binding syntax and the optionally typed AtScript language justify this. Whether the Javascript community accepts the bold move is another question altogether. I hope they will. You’ve seen some of the benefits in the article: several directive become superfluous (they can be replaced by the corresponding DOM properties), tag properties can use the proper type instead of having to be converted to strings and everybody who loves autocompletion in the IDE prefers typed languages. Of course, it depends on the tool chain. Among other things someone has to write AtScript plugins for Eclipse, Netbeans and IntelliJ to make AtScript shine.


Dig deeper

AtScript slides
The AngularJS design docs
discussion about AngularJS 2.0 data binding on GitHub
AtScript primer
Hackernews on AtScript
InfoQ: AngularJS 2.0 Details Emerge
Michael Bromley’s report of ng-europe 2014 and AngularJS 2.0
Videos of the ng-europe conference (not yet available at the time of writing)
A short introduction to TypeScript
Comparison of Typescript, Facebook Flow and AtScript


  1. released a week before I wrote this article
  2. more precisely: NgRepeatDirective
  3. I didn’t look it up in AngularJS.

4 thoughts on “AngularJS 2.0: Sneak Preview on Data Binding

  1. xiexin

    I have a problem, with angular 2, I found in the data binding expression, it doesn’t support Chinese char. like this in the complete template, `{{好}}`, The ‘好’ is an Chinese char, It can’t work, and there’s is a error, “Template parse errors: Parser Error: Unexpected token Lexer Error: Unexpected character {{好}}……”, how can I make it work? I really need this.

    Reply
    1. Stephan Rauh Post author

      I suppose that’s because Angular is American software. The HTML standard may support Chinese characters, but that doesn’t necessarily mean that the Angular compiler supports foreign characters. The template between the mustache braces follows the syntax rules of a variable, and probably Angular only allows English letters and digits (and a few special characters) for variable names. BTW, this usually discriminates most European languages, too, because most European languages use a superset of the English character set. But of course, the situation is much more annoying for, say, Chinese, Greek or Russian programmers.

      In any case, I suggest you open a ticket on the GitHub repository of Angular. As to my experience, the Angular team listens to the community.

      Reply
        1. Stephan Rauh Post author

          As you’ve seen, your issue has quickly been closed because it’s a duplicate of a really old issue, but even so, it’s worth reporting things. This way the Angular team gets aware that your problem isn’t as exotic as they think.

          Reply

Leave a Reply

Your email address will not be published.