Thursday, August 7, 2014

What's the Difference Between Attribute and Property?


I'll be the first to admit that I am far too obsessed with the new annotations in Polymer.dart. No matter what they do, they are not that important to the overall function of Polymer elements—they just make certain things nicer. And yet, I have been investigating (mostly poorly) these things for a week now.

And it continues tonight.

I have been so deep in trying to understand how these annotations work, that I have not really given much consideration to best practices. That is the whole point of this Patterns in Polymer research, so I will take at least a night to give it some thought. I start with, what is the difference between a property and an attribute?

I realize now that this is not an original thought. The mailing list is replete with concerns that the word “attribute” is not used in at least some of these annotations, but I had not understood the annotations well enough to join in the consternation. I now know that the @PublishedProperty annotation is used for… attributes. And I think that @ComputedProperty is used for properties. This certainly seems confusing, so I first want to double-check this understanding.

First up, is @ComputedProperty an attribute like @PublishedProperty? I get the feeling that the “published” part of @PublishedProperty is meant to make it an element attribute, but let's be sure. In my <x-pizza> element, I already have a special_name attribute that is updated when certain conditions are met. It is an attribute by virtue of the @PublishedProperty annotation in the backing class:
@CustomTag('x-pizza')
class XPizza extends PolymerElement {
  // ...
  @PublishedProperty(reflect: true)
  String get special_name => readValue(#special_name);
  set special_name(String newValue) => writeValue(#special_name, newValue);
  // ...
}
The reflect option indicates that changes will be reflected in the attributes in the DOM. This works just fine. One of the conditions updates the specialty pizza name from “Plain” if the first half has pepperoni on it:



So is @ComputedProperty an attribute? I declare the num_toppings computed attribute as the sum of toppings on various sides of the pizza:
@CustomTag('x-pizza')
class XPizza extends PolymerElement {
  // ...
  @ComputedProperty("firstHalf.model.length + secondHalf.model.length + whole.model.length")
  int get num_toppings => readValue(#num_toppings);

  @PublishedProperty(reflect: true)
  String get special_name => readValue(#special_name);
  set special_name(String newValue) => writeValue(#special_name, newValue);
  // ...
}
If I set a listener on this element:
  // ...
  XPizza.created(): super.created() {
    changes.listen((changes){
      print(changes);
    });
  }
  // ...
Then I see this value change as I add toppings:
[#<PropertyChangeRecord Symbol("num_toppings") from: 3 to: 4>]
But, if I add this as an attribute on the element:
      <h1>Ye Olde Dart Pizza Shoppe</h1>
      <x-pizza special_name="Plain" num_toppings=""></x-pizza>
Then I do not see values being updated. So @ComputedProperty is not an attribute:



Which, in my mind, begs the question, how you make a computed property an attribute? Putting reflect: true in the annotation does not work (it only accepts a single argument with no optional parameters. Stacking the @PublishedProperty annotation on top of @ComputedProperty seems to ignore @PublishedProperty:
  @PublishedProperty(reflect: true)
  @ComputedProperty("firstHalf.model.length + secondHalf.model.length + whole.model.length")
  int get num_toppings => readValue(#num_toppings);
The computed changes are still seen, but the attribute is never updated. I try futzing with the readValue() symbol and redefining the num_toppings getter, but to no avail. If there is an easy way to “publish” a @ComputedProperty, I cannot find it. Tonight.


Day #145

1 comment:

  1. try adding a setter for the computed property, but with the caveat that for this setter to work, your @ComputedProperty must use a expression that uses a single attribute/var, that can have it's value set. Like you can set the value of x, so

    @ComputedProperty('x)
    int get uberX => readValue(#uberX);
    set uberX(val) => writeValue(#uberX, val)

    will work, but you cant do the same if the expression is, say, adding two numbers, like x+y

    @ComputedProperty('x+y)
    int get z => readValue(#z);

    will work, but if you try

    @ComputedProperty('x+y)
    int get z => readValue(#z);
    set z(val) => writeValue(#z, val);

    won't work.

    The thing I like is that you can, if one or more of the values in the expression are themselves Observable, when the observable is updated, the referenced @ComputedProperty in the template will also be updated. I don't think we could have done this before @ComputedProperty.

    Someone please correct me if I'm wrong.

    ReplyDelete