Skip to content

Instantly share code, notes, and snippets.

@sjmiles
Last active August 29, 2015 14:23
Show Gist options
  • Save sjmiles/1cc4a0e56c4e4fd09ca2 to your computer and use it in GitHub Desktop.
Save sjmiles/1cc4a0e56c4e4fd09ca2 to your computer and use it in GitHub Desktop.
[DRAFT] Curmudgeon's Corner: Attributes, Properties, and Bears, Oh My!

@sjmiles, 6/17/2015

... shooing away some flying monkeys ...

Attributes Use Dash-Case

First, when we use attributes in Polymer we always use dash-case for the name. In other words, we never write <x-foo coolSetting>, instead we always write <x-foo cool-setting>. This is true regardless of how we are going to use cool-setting, whether for styling, data binding, or whatever.

We do this because attributes in HTML are case-insensitive.

This means that whether you write:

<x-foo COOLsetting=”3”>
<x-foo coolSetting=”3”>
<x-foo coolSETTING=”3”>

The HTML parser lower-cases the attribute names before Polymer gets to them. All Polymer can ever see is:

<x-foo coolsetting=”3”>

Remember this only applies to the attribute name. You can put whatever you want in the attribute value and the parser will leave it alone (putting quotes around the value can help you remember this rule).

<x-foo coolproperty=”coolProperty”> <-- the parser leaves this as is

Again, none of the above has anything to do with whether you are binding or not, these are just the rules of HTML.

Properties Use Camel-Case

JavaScript properties on the other hand are case-sensitive. You can have this.coolProperty and this.COOLproperty and they are unique properties. As a general rule, we use camel-case for property names.

Attributes: Dash, Properties: Camel

Sometimes in Polymer we use attributes to refer to properties, and we have a problem with the difference in case sensitivity. So we invented a rule: use dash-case for attribute names, camel-case for property names.

For example, the iron-ajax element supports a handleAs property. To set handleAs using markup, you have to write <iron-ajax handle-as="json"> for example.

Once again, these rules are all about the HTML parser, and nothing really to do with the particulars of data-binding or property flags.

Attributes Are Weird, Properties FTW

Attributes are name/value pairs on elements (DOM). Properties are name/value pairs on Objects (JavaScript).

Properties are awesome because they are references, which means they can point to anything: Arrays, Functions, Strings, Number, Objects, any type of data.

Attributes are weird because they are only Strings. If you try to set an attribute to an Object, your attribute value will be [Object object]. Properties are also faster than attributes because they are a basic part of the language.

Because of these advantages, Polymer really wants to stick with properties. We use properties for all the things, and mostly use attributes only when we want to encode something in markup (like HTML in a template).

If you aren't binding, your attribute is just an attribute

If you are not using {{ }} (or it's lame cousin [[ ]]) then your attribute is not special and Polymer will not mess with it. You never need to use $= syntax and the attribute will be sitting there in DOM if you look for it.

Example:

<template>
  <div title="I'm a tooltip"></div>
  <iron-ajax handle-as="text" ...></iron-ajax>
</template>

This div has a regular title attribute. Yay, Polymer doesn't mess with it. This iron-ajax has a regular handle-as attribute. Polymer doesn't mess with that one either (although the iron-ajax itself can use the information).

If you are binding, your attribute is a request to Polymer to do things to properties

If you are using {{ }}, then your attribute is special. Polymer sees it as a request to bind things together, which Polymer does using properties. After Polymer reads your request and sets up the binding, it throws away the attribute.

<template>
  <iron-ajax url="{{url}}" ...></iron-ajax>
</template>

In this case, we are asking Polymer to bind the url property of our element to the url property of the iron-ajax. After Polymer sets up the binding, it discards this attribute, because it's only an instruction for Polymer's setup engine.

But elements can use attributes?

Elements can use the value of attributes you set on them, even if they are not bindings. So again, if we write:

<template>
  <iron-ajax handle-as="text" ...></iron-ajax>
</template>

The iron-ajax will be set to text mode (and the iron-ajax.handlAs property will be set to text). This is not a binding, it's just an assignment by attribute

What if I really need an attribute?

Once in awhile we need to use both bindings and attributes. This comes up in particular when styling things.

For example,

<style>
  [magenta] {
    color: magenta;
  }
</style>
<template>
  <div magenta$="{{magenta}}">
</template>

In this case we don't actually care about any magenta property on the div, but we really want to control the attribute because it's used in the stylesheet. This is where $= comes in. If we write our binding using $= we are instructing Polymer to setup a binding to the attribute itself, not the property like it normally would.

Bindings to attributes are slower, avoid them whenever possible.

Sometimes elements keep properties and attributes in sync

All lot of native elements will automatically keep properties and attributes in sync.

For example, given

<div id="foo">

The div.id property will have the property foo. Now, if you set the div.id property, div.id = "bar" you will see

<div id="bar">

Polymer can do that too

Polymer will always try to convert attributes into properties (for known properties), but if you want it to go the other way and convert properties into attributes you have to ask for this specifically via the reflectToAttribute flag.

Given:

Polymer({
  is: 'x-foo'
});

If I do x-foo.foo = 'hello', my x-foo still looks like this <x-foo></x-foo> (no attributes). However, if I have

Polymer({
  is: 'x-foo',
  properties: {
    foo: {
      reflectToAttribute: true
    }
  }
});

Now if I do x-foo.foo = 'hello', my x-foo will reflect that value into an attribute like so: <x-foo foo="hello"></x-foo>.

The reason we don't do this by default is that is because (1) it's a relatively slow operation and (2) we have to convert your value to a String (see Attributes are Weird section above).

Polymer Uses Type Exclusively for Attribute Handling

Polymer lets you put type information on property declarations.

Polymer({
  is: 'x-foo',
  properties: {
    foo: {
      type: Object,
      reflectToAttribute: true
    }
  }
});

The only thing Polymer itself does with this information, is use it to decode information in and out of attributes. Because I've told Polymer that foo is supposed to be an Object, then I can write:

<x-foo foo='{"fruit": "apple"}'></x-foo>

and Polymer will decode that JSON into a real object to store in foo. Without the type annotation, Polymer would just make foo equal to the String '{"fruit": "apple"}'.

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