2

I read Polymer API developer guide, but unfortunately it has examples only for JavaScript developers. Some examples I try to port into Dart, but in this case I get fail. Please, go to https://www.polymer-project.org/0.5/docs/polymer/polymer.html#global (Section Supporting global variables). Here is a clip from the documentation:

elements.html

<polymer-element name="app-globals">
  <script>
    (function() {
      var values = {};

      Polymer({
       ready: function() {
         this.values = values;
         for (var i = 0; i < this.attributes.length; ++i) {
           var attr = this.attributes[i];
           values[attr.nodeName] = attr.value;
         }
       }
      });
    })();
  </script>
</polymer-element>

<polymer-element name="my-component">
  <template>
    <app-globals id="globals"></app-globals>
    <div id="firstname">{{$.globals.values.firstName}}</div>
    <div id="lastname">{{$.globals.values.lastName}}</div>
  </template>
  <script>
    Polymer({
      ready: function() {
        console.log('Last name: ' + this.$.globals.values.lastName);
      }
    });
  </script>
</polymer-element>

index.html

<app-globals id="globals" firstname="Addy" lastname="Osmani"></app-globals>

Questions:

  • How to implement this code in Dart?
  • Reading the code of different Q&A concerning Dart Polymer usage I come across with @observable annotation, toObserve() function and class CustomElement extends PolymerElement with Observable {..}. I know that they somehow related with data bindings, but I have no idea exactly how.
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
Timur Fayzrakhmanov
  • 17,967
  • 20
  • 64
  • 95

2 Answers2

3

app_gobals.html

<link rel="import" href="packages/polymer/polymer.html">

<polymer-element name="app-globals">
  <template>
    <style>
      :host {
        display: none;
      }
    </style>
  </template>
  <script type="application/dart" src="app_globals.dart"></script>
</polymer-element>

app_gobals.dart

import 'package:polymer/polymer.dart';
import 'dart:async' show Timer;

@CustomTag('app-globals')
class AppGlobals extends PolymerElement {
  static final ObservableMap _staticValues = toObservable({});

  Map get values => _staticValues;

  AppGlobals.created() : super.created();

  ready() {
    attributes.keys.forEach((k) {
      values[k] = attributes[k];
    });

    // just to demonstrate that value changes are reflected
    // in the view
    new Timer.periodic(new Duration(seconds: 2),
        (_) => values['periodic'] = new DateTime.now());
  }
}

app_element.html (your my-component)

<link rel="import" href="packages/polymer/polymer.html">
<link rel="import" href="app_globals.html">
<polymer-element name="app-element">
  <template>
    <style>
      :host {
        display: block;
      }
    </style>
    <app-globals id="globals"></app-globals>
    <div>{{$["globals"].values["firstname"]}}</div>
    <div>{{$["globals"].values["lastname"]}}</div>
    <div>{{$["globals"].values["periodic"]}}</div>
  </template>
  <script type="application/dart" src="app_element.dart"></script>
</polymer-element>

app_element.dart

import 'package:polymer/polymer.dart';

@CustomTag('app-element')
class AppElement extends PolymerElement {
  AppElement.created() : super.created();

  ready() {
    print('Last name: ${$["globals"].values["lastName"]}');
  }
}

@observable indicates that Polymer should be notified when the value changes so it can update the view. If you have a collection this is not enough because Polymer only gets notified when the field changes (another collection or null is assigned). toObservable(list|map) ensures that Polymer gets notified when elements in the collection are changed (removed/added/replaced). PolymerElement includes Observable there fore there is nothing special to do on class level. When you extend a DOM element this looks a bit different see https://stackoverflow.com/a/20383102/217408.

Update
This are a lot of questions. I use static final ObservableMap _staticValues = toObservable({}); to ensure all values are stored in one place no matter how many <app-globals> elements your application contains. Statics are stored in the class not in the instance therefore it doesn't matter how many instances exist. @ComputedProperty(expression) var someField; watches expression for value changes and notifies Polymer to update bindings to someField. @observable is the simpler version but works only for fields. @published is like @observable but in addition allows bindings to the field from outside the element. @PublishedProperty() is the same as @published but this annotation form allows to pass arguments. @PublishedProperty(reflect: true) is like @published but in addition updates the actual DOM attribute to make the bound value available not only for other Polymer elements to bind to but also for CSS or other Frameworks which have no knowledge how to bind to Polymer fields.

Community
  • 1
  • 1
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Gunter, could you a explain why using `MapObservable get values => toObservable({})` it doesn't work. Besides that I'm really confused in whic cases use `@ComputedProperty()`, `@observable`, `@published`. As well as I have no idea what does `notifyPropertyChange(#values, ...)` mean and how to use it. Even after 10 times reading you post again and again I hardly understand what's going on here. Maybe you give some examples to disperse the fog? I can create new answer if it neccessary. – Timur Fayzrakhmanov Apr 26 '15 at 15:37
  • 1
    I added the answered to your questions to my answer because it's to long for a comment. Please try to make follow-up questions more concrete. It's hard to know what exactly I should explain better. – Günter Zöchbauer Apr 26 '15 at 15:48
  • 1
    Glad to hear :) One additional note. You might be tempted to always use `@PublishedProperty(reflect: true)` to have full power but try to avoid it because it's much slower than without `reflect: true`. When you for example use this in an element where you create hundreds of instances using for example ` – Günter Zöchbauer Apr 26 '15 at 16:56
  • Thank you for advice! If to be honest I haven't yet reached this level)) – Timur Fayzrakhmanov Apr 26 '15 at 17:11
  • 1
    Ok, but I think it helps to understand why developers are bothered with so many different options. – Günter Zöchbauer Apr 26 '15 at 17:13
1

I found another solution that works for me:
Just define a class for your globals. Use a factory constructor so that every representation of the class is the same instance. The class must extend Object with Observable:
globals.dart:

library globals;
import 'package:polymer/polymer.dart';

class Globals extends Object with Observable {

  static Globals oneAndOnlyInstance;
  @observable String text; // a "global" variable
  @observable int integer; // another "global" variable

  factory Globals() {
    if (oneAndOnlyInstance==null) {
      oneAndOnlyInstance=new Globals.init();
    } 
    return oneAndOnlyInstance;    
  }

  Globals.init();

}

Now you can use this class inside of all your custom elements. Only need to import globals.dart and create an object of it:
inside: main_app.dart:

import 'dart:html';

import 'package:polymer/polymer.dart';   
import 'package:abundan/classes/globals.dart';

/// A Polymer `<main-app>` element.
@CustomTag('main-app')
class MainApp extends PolymerElement {      
  Globals globals=new Globals();    

  /// Constructor used to create instance of MainApp.
  MainApp.created() : super.created();

attached() {
  super.attached();
  globals.text="HELLO!";
}

and inside of main_app.html:

{{globals.text}}

This works for me and it seems to be the easiest way to have something like globals with polymer custom elements.