Short answer
When there isn't an 1:1 mapping between an HTML attribute and a DOM property one must use the attribute binding syntax otherwise Angular 2 will report a "template parse error".
Examples:
[attr.my-custom-attribute]="myComponentValue"
[attr.colspan]="1 + 1"
In your case, an SVG element has the width and height DOM properties but they aren't what you expect. They're SVGAnimatedLength objects. Trying to set their value the old way won't do anything. That's why your template doesn't work as you expect and doesn't report an error. Switching to attribute binding syntax would fix this behaviour: [attr.width]="width" [attr.height]="height"
In depth explanation
There's a big conceptual difference between how attribute bindings work in Angular 1 and Angular 2.
In Angular 1, setting a custom attribute looks like this:
<div a-custom-attribute="I am a custom {{ 'attribute' }}">Text Content</div>
<div ng-attr-a-custom-attribute="I am a custom {{ 'attribute' }}">Text Content</div>
- this syntax allows you to bind to attributes that would otherwise be eagerly processed by browsers (e.g. an SVG element's circle[cx] attributes, the src attribute of a IMG element, etc)
In Angular 2, there's a different story:
Angular 2 introduced, as they put it, a new mental model: instead of binding to HTML attributes it binds to DOM properties. Understanding the distinction between an HTML attribute and a DOM property is crucial to getting a grasp of how Angular 2 binding works.
Binding to a DOM property may looks like this:
<img [src]="heroImageUrl">
<img bind-src="heroImageUrl">
<img src="{{ heroImageUrl }}">
- this may look a bit confusing, especially if someone has an AngularJS 1 background, but Angular 2 translates these interpolations into the corresponding property bindings before rendering the view (source). It's important to have in mind that, as Mark pointed out in the comment section, after an interpolation has been evaluated, its result is then converted to a string (source). This implies that this syntax is limited to only assigning string values.
Note that if the name fails to match a DOM property, Angular 2 reports an "unknown native property" error:
// Template parse errors:
// Can't bind to 'colspan' since it isn't a known native property
<tr><td colspan="{{1 + 1}}">Three-Four</td></tr>
// Template parse errors:
// Can't bind to 'madeUpProperty' since it isn't a known native property
<div [madeUpProperty]="My custom {{ 'madeUpProperty' }}"></div>
This means that one must use the attribute binding syntax when there is no DOM property to bind to.
Finally, I believe that as a good rule of thumb one should always use the property binding's syntax (e.g. [src]="heroImageUrl"
) in favor of interpolation (e.g. src="{{heroImageUrl}}"
) whenever he wants to modify the element's DOM property since the latter is limited to only passing string values. Another reason is that if someone has an AngularJS 1 background, this should reduce the confusion between setting an attribute and a DOM property.