Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Angular 2 extrapolation in html element attributes?

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?

like image 821
lys1030 Avatar asked Mar 19 '16 08:03

lys1030


3 Answers

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.

like image 158
Cosmin Ababei Avatar answered Nov 17 '22 15:11

Cosmin Ababei


<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>
like image 34
Nikhil Shah Avatar answered Nov 17 '22 15:11

Nikhil Shah


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

like image 4
Pankaj Parkar Avatar answered Nov 17 '22 15:11

Pankaj Parkar