template repeat in template repeat - dart

Is it possible, to repeat templates inside a template ? I`d like to have something like this:
<polymer-element name="polymer-mylement">
<template>
<div class="col-xs-12 col-sm-12 col-md-12">
{{ myobject.stringone }}
<br>
{{ myobject.stringtwo }}
<br>
<table>
<tbody>
<tr template repeat="{{ a in myobject.as}}" >
<td>{{ a.type }}</td>
<tr template repeat="{{ b in a.bs }}">
<li>
{{ b.type }}
</li>
</tr>
</tr>
</tbody>
</table>
</div>
</template>
<script type="application/dart" src="polymer_myelement.dart"></script>
</polymer-element>
polymer_mylement.dart
import 'dart:html';
import 'dart:core';
import 'package:polymer/polymer.dart';
import 'dart:typed_data';
import 'protobuf/***/myobject.pb.dart';
import 'package:custom_element/custom_element.dart';
import 'package:intl/intl.dart';
class MyObject {
String string1;
String string2;
List<a> as;
MyObject(this.string1, this.string2, this.as);
}
class a {
String type;
List<b> bs;
a(this.type, this.bs);
}
class b {
String type;
b(this.type);
}
#CustomTag('polymer-myelement')
class MyObjectElement extends PolymerElement with ObservableMixin {
bool get applyAuthorStyles => true;
#observable MyObject myobject;
}
main() {
void onDataLoaded(ByteBuffer response) {
Uint8List li = new Uint8List.view(response);
MyObject myobject = new MyObject.fromBuffer(li);
List<a> as = new List();
List<b> bs = new List();
for(final a in myobject.as) {
for (final b in a.bs){
bs.add(new b(b.type));
}
as.add(new a(a.type.toString(), bs));
}
MyObject myobject = new MyObject(myobject.string1, myobject.string2, as);
var myElem = createElement('polymer-myelement');
MyObjectElement moele = myElem.xtag;
moele.myobject = myobject;
query('.container').children.add(myElem);
}
query('#menu-mylement').onClick.listen((MouseEvent event){
query('.container').children.clear();
try {
var url = "*****";
var request = new HttpRequest();
request.open('GET', url);
request.responseType = "arraybuffer";
request.onLoad.listen((event) => onDataLoaded(event.target.response)
//print('Request complete ${event.target.response}')
);
request.send();
}
catch (e) {
print(e);
}
});
}
As you can see i have a MyObject which contains a List. Listelements of "a" have another List.
In generell, in my main() method i fetch some protobuf data from our WebService.
In my onDataLoaded i translate the binary string back to an readable format.
Now i create my MyObject and fill it with ListElements via some Loops through my Response.
In my template, i would like to render the result.
It works till the second template repeat. I see string 1 and string2 from MyObject and also see the types from the List. But he doesnt show me the second repeat for my List ?
Is it possible or am i doing something mandatory wrong ?

See my comment above. It may just be that the browser's refusing to render the results of that second template because it's a tr inside a tr. Here's a JavaScript version of your code that seems to be working fine after switching to a td.

Related

two polymer elements (paper-input) referencing the same object on different instance variable does not work

I have two polymer elements (paper-input) referencing the same object on different instance variable.
When I change the text on one paper-input element, another paper-element does not change, although the referenced object has changed.
Following details:
>>> Browser
>>> main_app.html
<dom-module id="main-app">
<template>
<h3>Lista de Produtos</h3>
<paper-fab icon="add" on-tap="adicionarProduto"></paper-fab>
<template is="dom-repeat" items="{{produtos}}" as="item">
<div class="layout vertical">
<paper-material elevation="1">
<div class="layout horizontal">
<paper-input label="Produto" value="{{item.descricao}}"></paper-input>
</div>
</paper-material>
<template is="dom-repeat" items="{{item.componentes}}" as="item_comp">
<div class="layout vertical">
<div class="layout horizontal">
<paper-input label="Produto Componente" value="{{item_comp.descricao}}"></paper-input>
</div>
</div>
</template>
</div>
</template>
</template>
</dom-module>
>>> main_app.dart
#PolymerRegister('main-app')
class MainApp extends PolymerElement {
#property
List<Produto> produtos = new List();
MainApp.created() : super.created();
void ready() {
Produto p = new Produto()..descricao = 'teste';
add('produtos', p);
Produto p2 = new Produto()..descricao = 'teste 2';
add('produtos', p2);
Produto p3 = new Produto()..descricao = 'teste3';
add('produtos', p3);
add('produtos.2.componentes', p);
add('produtos.2.componentes', p2);
}
#reflectable
void adicionarProduto(e, d) {
//...
}
}
class Produto extends JsProxy {
#property
String descricao;
#property
List<Produto> componentes;
Produto() {
componentes = new List();
}
}
Is this a bug or something else needs to be done?
It's not a bug, because Polymer won't observe internal property changes, so when you edit the description, the value is updated but the other input does not get notified and it doesn't update its value.
One way to solve your problem would be to add an on-change handler to the input elements. In the listener code call this.notifyPath(itemPath).
You need to generate the correct path e.g 'produtos.3.descricao' so you need the index of the element. For that you can use the indexForElement method of the dom-repeat
//inside the change event listener (js code)
//You need to give your dom-repeat and id
var index = this.$.myDomRepeatTemplateId.indexForElement(ev.target)
#property/#Property() is only for Polymer elements. Use #reflectable for model class members.
class Produto extends JsProxy {
#reflectable
String descricao;
#reflectable
List<Produto> componentes;
Produto() {
componentes = new List();
}
}
Building on #HugoZapata s answer I created a working example https://github.com/bwu-dart-playground/polymer1/tree/master/nested_repeat_complex_data
The recursivity in your model makes this quite a challenge. Maybe there is a simpler way, but this is what I was able to make work.

Cloned table row cell widget on-change event does not fire when event is changed

This is a follow-up question to Error | Removing disallowed attribute on SelectElement
I created a table as shown in the figure below:
on-change events are attached to the select dropdowns. For the first row, the event is fired as expected with the dropdown content changes. However, when a new row is added by cloning, the event-listener does not fire. I need some help as to how best to get the attached event to fire.
.html
<link rel='import' href='../../../../packages/polymer/polymer.html' >
<link rel='import' href='../../../../packages/paper_elements/paper_checkbox.html' >
<link rel='import' href='../../../../packages/paper_elements/paper_button.html' >
<link rel='import' href='../../../../packages/core_elements/core_collapse.html' >
<link rel='import' href='../../../../packages/core_elements/communication_icons.html' >
<polymer-element name='phone-form'>
<template>
<table id='table-1'>
<thead>
<tr>
<th width='20px'></th>
<th width='67px'>Type</th>
<th width='130px'>Provider</th>
</tr>
</thead>
</table>
<table id='table'>
<tbody>
<tr>
<td><input name='chk' type='checkbox'></td>
<td>
<select id='phone-type'
selectedIndex='{{typeSelected}}'
on-change='{{onChangeTypeFired}}'>
<option template repeat='{{key in types.keys}}'
value='{{types[key]}}'>{{types[key]}}
</option>
</select>
</td>
<td>
<select id='phone-provider'
selectedIndex='{{providerSelected}}'
on-change='{{onChangeProviderFired}}'>
<option template repeat='{{key in providers.keys}}'
value='{{providers[key]}}'>{{providers[key]}}
</option>
</select>
</td>
</tr>
</tbody>
</table>
<div>
<paper-button raised id='add-row-btn' class='margin-8px'
on-click='{{addRow}}'>
Add Row
<core-icon id='add-row-btn-icon' icon=''></core-icon>
</paper-button>
<paper-button raised id='delete-row-btn' class='margin-8px'
on-click='{{deleteRow}}'>
Delete Row
<core-icon id='delete-row-btn-icon' icon='fa:minus-circle'></core-icon>
</paper-button>
</div>
</template>
<script type='application/dart' src='phone_form.dart'></script>
</polymer-element>
.dart
import 'package:polymer/polymer.dart';
import 'dart:html' as dom;
//import 'package:paper_elements/paper_toggle_button.dart' show PaperToggleButton;
import 'package:paper_elements/paper_button.dart' show PaperButton;
//import 'package:epimss_shared/epimss_shared.dart';
//import 'package:epimss_shared/epimss_shared_client.dart' hide DataEvent;
#CustomTag('phone-form')
class PhoneForm extends PolymerElement {
#observable String icon = '';
#observable String errorMsg;
String topic;
PaperButton addBtn;
#observable int typeSelected = 0;
#observable int providerSelected = 0;
#observable int relationsSelected = 0;
Map<String, String> types = const <String, String> {
'': '',
'Car': 'Car',
'Direct': 'Direct',
'Fax': 'Fax',
};
Map<String, String> providers = const <String, String> {
'': '',
'AT & T': 'AT & T',
'Cable & Wireless': 'Cable & Wireless',
};
PhoneForm.created() : super.created();
void onSelectTypeFired()
{
}
void onChangeTypeFired( dom.Event e, var detail, dom.SelectElement target)
{
print(target.value);
}
void onChangeProviderFired( dom.Event e, var detail, dom.SelectElement target)
{
print(target.value);
}
void onChangeRelationsFired( dom.Event e, var detail, dom.SelectElement target)
{
print(target.value);
}
void addRow()
{
var table = $['table'];
var rowCount = table.rows.length;
var row = table.insertRow(rowCount);
var colCount = table.rows[0].cells.length;
for(var i = 0; i < colCount; i++ )
{
var newcell = row.insertCell(i);
newcell.children.clear();
newcell.children.addAll(table.rows[0].cells[i].children.map((c) =>
c.clone(true)));
switch(newcell.childNodes[0].runtimeType.toString())
{
case 'text':
newcell.childNodes[0].value = '';
break;
case 'checkbox':
newcell.childNodes[0].checked = false;
break;
case 'select':
newcell.childNodes[0].selectedIndex = 0;
break;
}
}
}
void deleteRow()
{
var rowsToDelete = [];
try{
var table = $['table'];
var rowCount = table.rows.length;
for(var i = 0; i < rowCount; i++)
{
var row = table.rows[i];
var chkbox = row.cells[0].childNodes[0];
if(chkbox != null && chkbox.checked)
{
if(rowCount <= 1)
{
print('Cannot delete all the rows.');
break;
}
else
{
rowsToDelete.add(i);
}
}
}
rowsToDelete.forEach( (row)
{
table.deleteRow(row);
});
}
catch(e)
{ print(e); }
}
#override
void attached() {
super.attached();
topic = this.dataset['topic'];
addBtn = $['add-btn'];
}
}
Thanks
Clone just doesn't copy event handlers (see for example https://groups.google.com/a/dartlang.org/forum/#!topic/misc/-z_8sVp_uPY)
You could use <template repeat> to generate the rows and cells as mentioned in your previous question. Another way is to use injectBoundHtml (https://www.polymer-project.org/docs/polymer/databinding-advanced.html#boundhtml).
With injectBoundHtml you can't read the HTML from the DOM (innerHtml, or similar) because what you get here is already with bindings interpreted by Polymer. When you store the HTML in a string in Dart code you can add it multiple times and it will interpreted every time when inserted.
If you prefer to have the content in the HTML file you could use a template instead
injectBoundHtml((this.querySelector('#my_template') as TemplateElement)
.innerHtml);
(haven't actually tried)

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.
You could try two things here in my opinion:
Use Observable.dirtyCheck()
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
The problem is how you access the data in the Model
{{row[column.name]}}
the [] operator access is not observable and column.name doesn't change so the expression is never re-evaluated.
Just because the fields are #observable doesn't make the reference using the [] operator access #observable too.
My attempt - introduce a dummy field:
class Model extends Observable {
#observable String fname;
#observable String lname;
#observable int age;
#observable String get dummy => '$fname$lname$age';
getValue(String colName, String dummy) {
return this[colName];
}
Model(this.fname, this.lname, this.age) {
changes.listen((List<ChangeRecord> c) {
c.forEach((PropertyChangeRecord r) {
if(r.name != #dummy) {
notifyPropertyChange(#dummy, null, dummy);
}
});
});
}
operator [](String fieldName){
var im = reflect(this);
return im.getField(new Symbol(fieldName)).reflectee;
}
}
and bind the field value like
{{row.getValue(column.name, row.dummy)}}
This way the Polymer binding expression contains a field that changes row.dummy and this makes Polymer to re-evaluate the expression.

dart-polymer update polymer dom elements

I am using this Templates to create a list of notice entries which works fine the first time.
NoticeList nle = querySelector('#noticeList');
nle.notices = notices;
But the second time I call this code gets executed the site doesn't change at all.
Am i missing something?
Thank you
<polymer-element name="notice-list">
<template>
<ul id = "noticeEntrys">
<template repeat="{{notice in notices}}">
<li>
<notice-element notice={{notice}}></notice-element>
</li>
</template>
</ul>
</template>
<script type="application/dart" src="notice_list.dart"></script>
</polymer-element>
<polymer-element name="notice-element">
<template>
<div class="notice">
<textarea rows="8" readonly>{{notice.getText()}}</textarea>
<div class="controlls">
<button type="button" name="delete" on-click={{delete}}>Delete</button>
<button type="button" name="change" on-click={{change}}>Change</button>
</div>
</div>
</template>
<script type="application/dart" src="notice_element.dart"></script>
</polymer-element>
#CustomTag('notice-list')
class NoticeList extends PolymerElement {
NoticeList.created() : super.created() {
}
#published List<Notice> notices;
}
#CustomTag('notice-element')
class NoticeElement extends PolymerElement {
NoticeElement.created() : super.created() {
}
#published Notice notice;
void delete(Event e, var detail, Node target) {
Datamanager.removeNotice(notice);
Controller.updateListe();
}
void change(Event e, var detail, Node target) {
Controller.updateActiveElement(notice);
}
void setNotice(Notice n) {
notice = n;
}
}
Edit: I update the code the same as i set the list in the first time
I get the new data via a webservice and the new data is correct
static void noticesLoadedPolymerList(List<Notice> notices) {
NoticeList nle = querySelector('#noticeList');
nle.setNotices(notices);
}
Edit2: I added a simple integer to display the listsize
#observable int listSize;
The value changes if i assign the new list but the displayed content doesn't.
If you set notices in NoticeList to a new List instance it should recognize the change.
If you assign notices in your first attempt and only modify in you second attempt you have to make your notices list observable var notices = toObservable([new Notice('notice1'), new Notice('notice2'), ...]).
You haven't provided code of your Notice class.
It may be necessary to make your Notice class observable like:
class Notice extends Object with Observable {
#observable String text;
}
This way Polymer recognizes if only a property of a notice instance changes (without changing (add/remove) the notices list.
Finally hat time to solve the problem.
My problem was that i would start polymer like this
initPolymer();
when i should have started it like this
initPolymer().run(() {
//initialization and other stuff
}
More about this you can find here https://code.google.com/p/dart/issues/detail?id=15379

Build Polymer App with multiple elements using one single Dart script

Do you know how to use multiple "polymer-element" with only one Dart script ?
I succeeded but I am not sure it is the best solution.
My example is a Dart/Polymer implementation of this following example with Dart/WebUI:
https://www.dartlang.org/articles/web-ui/#loops
It displays a list of fruits in a polymer-element ("example-template1"), and where we can research a specific fruit in another polymer-element ("example-template2"). The second polymer-element must update the first with data binding.
To do that, I declared as follow my polymer elements. One parent polymer-element named "example-script" (because we can only use one script declaration in a polymer templates file), and children that extend from this parent :
<polymer-element name="example-script">
<script type="application/dart" src="tute.dart"></script>
</polymer-element>
<polymer-element name="example-template1" extends="example-script">
<template>
<div>
<p>Search fruit</p>
<input type="text" class="form-control" value="{{ research }}" on-input="{{ get_results }}">
</div>
</template>
</polymer-element>
<polymer-element name="example-template2" extends="example-script">
<template>
<div><ul>
<template repeat="{{ fruit in fruits }}">
<li>{{ fruit }}</li>
</template>
</ul></div>
</template>
</polymer-element>
My Dart script is as follow:
import 'package:polymer/polymer.dart';
import 'dart:html';
#CustomTag('example-script')
class FruitsScript extends PolymerElement {
static List<String> fruitsList = new List();
#observable static List<String> fruits = toObservable(fruitsList);
#observable static String research = '';
FruitsScript.created() : super.created();
}
#CustomTag('example-template1')
class FruitsResearch extends FruitsScript {
FruitsResearch.created() : super.created();
String get research {
return FruitsScript.research;
}
String set research(String search) {
FruitsScript.research = search;
}
void get_results(Event e, var detail, Node target) {
FruitsScript.fruits.clear();
if (FruitsScript.research.length > 0) {
var lResearch = FruitsScript.research.toLowerCase();
var results = FruitsScript.fruitsList.where((v) => v.toLowerCase().contains(lResearch));
FruitsScript.fruits.addAll(results);
}
else {
FruitsScript.fruits.addAll(FruitsScript.fruitsList);
}
}
}
#CustomTag('example-template2')
class FruitsDisplay extends FruitsScript {
FruitsDisplay.created() : super.created() {
List<String> fruits = [ 'Apple', 'Apricot', 'Avocado'];
FruitsScript.fruitsList.clear();
FruitsScript.fruitsList.addAll(fruits);
FruitsScript.fruitsList.sort();
FruitsScript.fruits.clear();
FruitsScript.fruits.addAll(FruitsScript.fruitsList);
}
List<String> get fruits {
return FruitsScript.fruits;
}
}
I declare one parent Class "FruitsScript" to initialize the observable variables. Then, I create two other classes for my two polymer elements "example-template1/2". Hence I can access to my observable variables. But for that, I also need to declare them as static, and to make setters an getters in my subclasses, otherwise my polymer elements can not share the observable variables.
This method works, but do you think there is a better way to make this?
How do you add those elements to your page?
As far as I understand what you want to to, inheritance (extend) is not the appropriate solution.
I have not tried to run this code just edited your code here in the textfield.
If this is what you want to do but something doesn't work post a comment.
I didn't want to put to much time in it as long as it's not clear what exactly you want to achieve.
In your web page you lay out your elements like this:
<example-template1></example-template1>
<example-template2></example-template2>
<example-script></example-script>
Your elements
<polymer-element name="example-script">
<script type="application/dart" src="tute.dart"></script>
</polymer-element>
<polymer-element name="example-template1">
<template>
<div>
<p>Search fruit</p>
<input type="text" class="form-control" value="{{ model.research }}" on-input="{{ get_results }}">
</div>
</template>
</polymer-element>
<polymer-element name="example-template2">
<template>
<div><ul>
<template repeat="{{ fruit in model.fruits }}">
<li>{{ fruit }}</li>
</template>
</ul></div>
</template>
</polymer-element>
Your Dart script is as follow:
import 'package:polymer/polymer.dart';
import 'dart:html';
class Model extends Object with Observable {
#observable List<String> fruits = toObservable(new List<String>());
#observable String research = '';
}
#CustomTag('example-script')
class FruitsScript extends PolymerElement {
#published Model model = new Model();
FruitsScript.created() : super.created();
void attached() {
super.attached();
(document.querySelector('example-template1') as FruitsResearch).model = model;
(document.querySelector('example-template2') as FruitsDisplay).model = model;
}
}
#CustomTag('example-template1')
class FruitsResearch extends PolymerElement {
FruitsResearch.created() : super.created();
#published Model model;
void get_results(Event e, var detail, Node target) {
if (this.research.length > 0) {
var lResearch = this.model.research.toLowerCase();
var results = this.model.fruits.where((v) => v.toLowerCase().contains(lResearch));
this.model.fruits = toObservable(results);
}
}
}
#CustomTag('example-template2')
class FruitsDisplay extends PolymerElement {
#published Model model = new Model();
FruitsDisplay.created() : super.created() {
this.model.fruits = toObservable([ 'Apple', 'Apricot', 'Avocado']);
this.model.fruits.sort();
}
}

Resources