Search code examples
data-bindingdartdart-polymer

Making Superclass Fields Observable


I am trying to create a custom 'radial-selector' element using Polymer in Dart.

My current goal is to make a 'children' field for the RadialSelector class that is observable, so that I can do a template loop through the children and filter through those that are only of a certain element type.

It is not clear how I can make the 'children' field observable without creating another variable that does not conflict with it in terms of namespace. For example, I could just create a "childs" field, make it observable, and then set it equal to this.children. But this seems awkward and boilerplate.

Is there a way for my RadialSelector class, which extends the PolymerElement class, to make its superclass 'children' field observable? Currently when I just write @observable children, Dart (understandably) thinks I'm just creating a new 'children' variable.

Thank you and let me know if there is something I could make clearer! :)

Here is the Dart Code:

      import 'package:polymer/polymer.dart';
      import 'dart:html';

      @CustomTag('radial-selector')
      class RadialSelector extends PolymerElement {
        @published String img;
        @observable children;
        RadialSelector.created() : super.created() { 
          print("CONSTRUCTING RADIAL SELECTOR");
          print(children);
           }

        filterByType(Type t, List<Element> elems) {
          print("FILTER BY TYPE CALLED");
          List<Element> newElems = new List<Element>();

          for (var e in elems) {
            if (e.runtimeType == t)
              newElems.add(e);
          }
          return newElems;
          }
       }

Here is the corresponding html:

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

<polymer-element name="radial-selector">   
  <template>
    <style>
    :host {
    display: block;
    margin: 100px
    }

    </style>
         <script>console.log("HEY");</script>

    <div id="stem">

     <img src = "{{img}}" alt ="Radial stem"/>

     <template repeat = "{{child in children | filterByType(ImageElement)}}">   
         <polymer-ui-icon-button src = {{child.src}}></polymer-ui-icon-button> 
     </template>
    </div>
  </template>
  <script type="application/dart" src="radialselector.dart"></script>
</polymer-element>

Solution

  • There is no way you can do this for children.

    You could make an @observable getter/setter that redirects to the super class' children

    @observable
    NodeList get children => super.children;
    set children(NodeList val) => super.children = val; // won't work
    

    This has two flaws.

    • the children list is final (haven't tried but I'm sure you can't assign a new list.
    • it doesn't help you at all to have an @observable children.
      You would only be notified when another list gets assigned (which is not possible because the field is final)
      What you need is an observable list to get notified about list element changes (add/remove/replace).
      For this you would need to replace children by toObservable(children) which is not possible because you can't replace the children collection.

    What you can do is to use a MutationObserver. see Angular Dart - execute a function right after ng-repeat has finished to get notified about child node changes.

    You can add a new field

    @observable List myChildren;
    

    and use the mutation observer to update this field whenever the children change.

    void mutationCallback(...) {
      ...
      myChildren = children.toList();
    }
    

    This way each time your children change myChildren get's an entire new list assigned and the view gets notified about this change due to the @observable annotation.
    You don't need to make the list observable because there are no single add/remove/replace operations to observe.