Search code examples
dartpolymerdart-polymer

Changes do not get reflected when getting property by name


I have the following polymer element

import 'package:polymer/polymer.dart';
import 'dart:mirrors';
import 'dart:async';
import 'dart:math';

class Column extends Observable{
  @observable String name;
  @observable String displayName;
  Column(this.name,this.displayName);
}

class Model extends Observable{
  @observable String fname;
  @observable String lname;
  @observable int age;

  Model(this.fname,this.lname,this.age);

  operator [](String fieldName){
    var im = reflect(this);
    return im.getField(new Symbol(fieldName)).reflectee;
  }
}


@CustomTag('main-app')
class MainApp extends PolymerElement {
  @observable ObservableList<Object> data;
  @observable ObservableList<Column> columns;

  MainApp.created() : super.created(){
    columns = new ObservableList.from([new Column("fname","First Name"), new Column("lname","Last name"),new Column("age","Age")]);
    emulateDataChanging();
  }

  emulateDataChanging(){
    var r = new Random();
    var names = ["aaa","bbb","ccc","ddd","eee"];
    //add some models to data first
    data = new ObservableList();
    for(int i=0;i<5;i++){
      data.add(new Model(names[r.nextInt(4)],names[r.nextInt(4)],r.nextInt(99)));
    }

    //modify couple random props every second
    new Timer.periodic(new Duration(seconds:1),(t){

      Model m;
      m=(data[r.nextInt(data.length-1)] as Model);
      m.lname=names[r.nextInt(4)];
      m.deliverChanges();
      m=(data[r.nextInt(data.length-1)] as Model);
      m.fname=names[r.nextInt(4)];
      m.deliverChanges();
      m=(data[r.nextInt(data.length-1)] as Model);
      m.age =r.nextInt(4);
      m.deliverChanges();
      //add a random model
      //data.add(new Model(names[r.nextInt(4)],names[r.nextInt(4)],r.nextInt(99)));
    });
  }
}

and the html for the element

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

<polymer-element name="main-app">
  <template>
    <style>
      :host {
        display: block;
      }
    </style>


    <!--A table -->
    <table id="table" class="table table-hover table-mc-light-blue">
      <thead>
      <tr>
        <th template repeat='{{column in columns}}'>
          {{column.displayName}}
        </th>
      </tr>
      </thead>
      <tbody>
      <tr template repeat='{{row in data}}'>
        <td template repeat='{{ column in columns}}' data-title="{{column.displayName}}">
          {{row[column.name]}}
        </td>
      </tr>
      </tbody>
    </table>

    <!--A repeat by binding to the Model properties directly -->
      <template repeat="{{row in data}}">
        <p>{{row.fname}} {{row.lname}} {{row.age}}</p>
      </template>

  </template>
  <script type="application/dart" src="main_app.dart"></script>
</polymer-element>

i added the whole code so one can copy and past and run in seconds, as you can see in the template i have a table and rows are added using a nested repeat and the property of model gets accessed by the name of the column ,and a single repeat where properties get accessed directly.

however the problem when changing a model property, the changes do not get reflected to the table but they get reflected in the other repeat, i think if you run it you'll understand better.


Solution

  • You could try two things here in my opinion:

    1. Use Observable.dirtyCheck()
    2. Call deliverChanges() on the polymer element and not on the Model

    // EDIT

    I think your problem is that you create a new ObservableList() every time. Try to create the ObservableList only once e.g. in your created() contstructor and then change the elements of the list instead of the object.

    // EDIT 2 - Possible Solution

    Hi Again. Now that I understand your problem correctly I have a solution for you. I had the same problem as well. I solved it by writing a wrapper around the model:

    HTML

    <link rel="import" href="../../packages/polymer/polymer.html">
    
    <polymer-element name="model-wrapper" attributes="model property">
      <template>
        {{value}}
      </template>
      <script type="application/dart" src="model_wrapper.dart"></script>
    </polymer-element>
    

    DART

    import 'package:polymer/polymer.dart';
    import 'dart:mirrors';
    
    @CustomTag('model-wrapper')
    class ModelWrapper extends PolymerElement {
      @published var model;
    
      @published String property;
    
      @ComputedProperty('model[property]')
      get value => model[property] == null ? "" : model[property].toString();
    
      @observable
      set value(v) => model[property] = v; 
    
      ModelWrapper.created() : super.created(){
    
      }
    
      void ready() {
        model.changes.listen((List<ChangeRecord> changes) {
          changes.forEach((record) {
            if(record is PropertyChangeRecord) {
              if(MirrorSystem.getName(record.name) == property) {
                notifyPropertyChange(#value, record.oldValue, record.newValue);
              }
            }
          });
        });
      }
    }
    

    My original wrapper can be found here:

    https://github.com/roberthartung/webui/blob/master/lib/util/webui-property-wrapper.dart https://github.com/roberthartung/webui/blob/master/lib/util/webui-property-wrapper.html