I want to bind some data to non-custom html element attributes. However, the {{ }} in attributes is not extrapolated. I've looked at other related posts as "Angularjs templateUrl fails to bind attributes inside ng-repeat", which is a angular 1 solution for custom directives.
For example, I have:
size = 500;
I want the following SVG element to work properly:
<svg xmlns="http://www.w3.org/2000/svg/" width="{{size}}" height="{{size}}">
<rect width="{{size}}" height="{{size}}" fill="#DCB35C"/>
</svg>
How should I do this in Angular 2?
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"
There's a big conceptual difference between how attribute bindings work in Angular 1 and Angular 2.
<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)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.
<svg xmlns="http://www.w3.org/2000/svg/" [attr.width.px]="size" [attr.height.px]="size">
<rect [attr.width.px]="width" [attr.height.px]="height" fill="#DCB35C" />
</svg>
You should bind width
& height
using attribute binding by having attr
prefix before binding like [attr.*]
.
Markup
<svg xmlns="http://www.w3.org/2000/svg/" [attr.width]="size" [attr.height]="size">
<rect [attr.width]="width" [attr.height]="height" fill="#DCB35C" />
</svg>
Class
import {Component} from 'angular2/core';
import {bootstrap} from 'angular2/platform/browser';
@Component({
selector: 'demo-app',
templateUrl: 'src/app.html',
pipes: []
})
export class App {
width: number = 100;
height: number=100;
size: number=100;
constructor() { }
}
bootstrap(App);
Demo Plunkr
As requested appending value of size by having some constant string value, You just need to put that on attribute
like [attr.width]="size + '5'"
<svg xmlns="http://www.w3.org/2000/svg/" [attr.width]="size" [attr.height]="size">
<rect [attr.width]="width + '5'" [attr.height]="height + '5'" fill="#DCB35C" />
</svg>
Updated Plunkr
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With