Accessing Databindings by braces instead of dot (.) operator - dart

Have a template repeater that I am running, and showing data by the dot operator
<template as="dom-repeat" items="{{list}}" as="row" index-as="rowIndex"
<div>{{row.foo}}</div>
<div>{{row.bar}}</div>
</template
I was trying to get the item by braces
<div>row["foo"]</div>
<div>row['bar']</div>
but those dont work, but is there a way to make it work? I am trying to make this list dynamic based on some object metadata, generalizing it. That way i could run something like:
<div>{{row[getKey(rowIndex)]}}</div>
Edit:
I saw this: https://github.com/Polymer/polymer/issues/1504 which states that this is now allowed in Polymer 0.5, but there is an experimental branch in 1.0 called: Polymer Expressions at https://github.com/Polymer/polymer-expressions/tree/2.0-preview
which shows that it can be done.
The issue is that this is experimental therefore I dont want to add this to my production DartPolymer1.0 codebase.
I guess withh that said, are there workarounds for it? Im thinking I might try to do something with conversion of Objects to arrays for accessing and then just do a direct access.
Will post a follow-on answer if i can figure it out.

This is the answer I had come up with:
<style>
.table{
display:table;
}
.row{
display:table-row;
}
.cell{
display:table-cell;
}
.th{
display:table-cell;
font-weight: bold;
}
.table, .row, .cell, .th {
border: solid 1px black;
padding: 5px;
}
</style>
<div class="table">
<div id="headerRow" class="row">
<template is="dom-repeat" items="{{columnHeaders}}">
<template is="dom-repeat" items="{{convertMap(item)}}" as="prop">
<div class="th">{{prop.value}}</div>
</template>
</template>
</div>
<template id="myRepeat" is="dom-repeat" items="{{data}}" as="row">
<div class="row">
<template is="dom-repeat" items="{{getListByFilter(row)}}" as="prop">
<div class="cell">{{prop.value}}</div>
</template>
</div>
</template>
</div>
with the Dart code of:
#property
List<Map> columnHeaders = [];
#property
List<Map> data = [];
#reflectable
List<Map> convertMap(Map map, [__]){
List<Map> lst = map.keys.map((key)=> { "name": key, "value": map[key]}).toList();
return lst;
}
#reflectable
List<Map> getListByFilter(Map row) {
List<Map> headers = new List<Map>();
columnHeaders.forEach((Map ele) => headers.add(convertMap(ele)[0]));
List<Map> rowList = new List<Map>();
for (Map header in headers) {
print("in for loop");
Map node = new Map();
node["name"] = header["name"];
node["value"] = row[header["name"]];
rowList.add(node);
}
List keysToIgnore = rowList.map((Map element) => element["name"]).toList();
for (String key in row.keys) {
if (!keysToIgnore.contains(key)) {
Map node = new Map();
node["name"] = key;
node["value"] = row[key];
rowList.add(node);
}
}
return rowList.sublist(0, columnHeaders.length);
}
therefore when defining this element:
<myDataGrid data="{{testrows}}" columnheaders="{{headers}}"></myDataGrid>
where the dart objects are formed in the idea of:
List<Map> headers = [{"columnKey":"ColumnTextString"},....];
List<Map> testRows = [{"columnKey": data, "anotherColumnKey": dataB}];
where the columnKey has to be in the Row Objects.... and headers keys are a subset of the Row Objects'. Therefore, it will set an order of columns and use that as a referencing factor for the Row Data.
It will maintain that each row still has access to the full set of data for other means, but will only display what is currently set up in the headers.
This gets around the bracket operator fairly well.

Related

Vapor 3 Rendering leaf template with script inline

I am trying to render a template in Vapor 3 with Leaf. Most of my HTML is in my base.leaf. In the login.leaf template, I need to add a JS script. Trouble is when it gets to the function it breaks and renders the function. Can anyone tell me how to add these properly? Thanks for your help in advance. Here's what is giving me problems:
#set("content") {
<h1>#(title)</h1>
<div id="logo"><img src="images/global/journey_trax_logo.png" alt=""/></div>
<div id="sheet1" class="form_sheet">
<input type="text" class="text_input" id="name_field" placeholder="NAME">
<input type="password" class="text_input" id="password_field" placeholder="PASSWORD">
<div id="continue_btn1" class="text_btn" onClick="logIn();">Continue</div>
<p> </p>
</div>
<script>
var user_name = readCookie("user_name");
var user_password = readCookie("user_password");
document.getElementById("user_name").value = user_name;
document.getElementById("user_password").value = user_password;
function logIn() {
var baseURL = window.top.location.href;
if (baseURL.indexOf("#") != -1) {
baseURL = window.location.href.split("#")[0];
}
var url = baseURL + "login";
console.log("[logIn] post to URL: "+url);
console.log("[logIn] name: "+user_name+", password: "+user_password);
$.post( url,
{
name: user_name,
password: user_password
},
function(data) {
// Do something with the return data?
console.log("[logIn] callback data: "+data);
console.log("[logIn] handle succes or error");
//
parent.loadHTML('screens/home.html');
}
);
}
</script>
<style>
.text_input, select {
width: 100%;
margin-bottom: 30px;
}
.text_btn {
width: 100%;
margin-top: 20px;
cursor: pointer;
}
</style>
}
#embed("base")
Your fundamental problem is that the } characters in the Javascript are being captured by Leaf because it is inside the #set tag. You have two choices:
Leave it where it is escape all the instances of } inside your <script> tag to \}. As far as I can tell, it doesn't need { escaping in the same way; presumably, this is because there isn't a Leaf tag immediately preceding. This works reliably and you can use Leaf to generate your javascript server-side before sending it to the client.
Move the <script> and contents above (i.e. outside) the #set tag. If you do embed any Leaf tags inside the javascript, you'll need to start escaping any } characters belonging to the Javascript as option 1.

What is the Correct way of listening ObservableList adding and removing?

I have next code:
#CustomTag('my-element')
class MyElement extends PolymerElement {
MyElement.created() : super.created();
#published ObservableList persons = toObservable([]);
handleAdd() {
persons.add({'name': 'jhon', 'lastName': 'Doe'});
}
handleRemove() {
persons.remove({'name': 'jhon', 'lastName': 'Doe'});
}
}
and this is the HTML:
<polymer name="my-element">
<template>
<template repeate="{{p in persons}}">
{{p['name']}} {{p['lastName']}}
</template>
<button on-click="{{handleAdd}}">add person</button>
<button on-click="{{handleRemove}}">remove person</button>
<template>
</polymer>
when debugging, it is added and removed from the internal list of objects. However, it never shows the elements as added in HTML.
This code line has no effect
persons.remove({'name': 'jhon', 'lastName': 'Doe'});
because
print({'name': 'jhon', 'lastName': 'Doe'} == {'name': 'jhon', 'lastName': 'Doe'});
try at DartPad
prints false because for collections Dart compares identity not content.
There are helpers available. See How can I compare Lists for equality in Dart?
What you can do instead is
persons.removeWhere((p) => p['name'] == 'jhon' && p['lastName'] == 'Doe');
try at DartPad
This doesn't work because in Dart you can't access elements of a Map using dot-notation
<template repeate="{{p in persons}}">
{{p.name}} {{p.lastName}}
</template>
If you change this to
<template repeate="{{p in persons}}">
{{p['name']}} {{p['lastName']}}
</template>
it should work as intened.

TextArea Resizing in Dart during DOM Initialization

I'm trying to make a text area that resizes dynamically in Dart based on the height of its contents.
I have a textarea element defined in a polymer element in Dart like so:
<polymer-element name="page-content">
<template>
{{title}}
<ul>
<template repeat="{{element in elist | enumerate}}">
<li value={{element.index}}><textarea class="{{element.value.type}}" resize="none" on-keypress="{{resize}}" on-change="{{updateDatabase}}">{{element.value.content}}</textarea><div on-click="{{deleteElement}}">X</div></li>
</template>
</ul>
</template>
</polymer-element>
When any text is entered into the textarea, the resize method is called and properly resizes the text-area to adjust its height appropriately.
I am not sure how to call the resize method right when the element is loaded into the DOM. I have tried adding on-load="{{resize}}" to the textarea or even querying all the textareas and adjusting their sizes. Nothing seems to be working. My intutition tells me there should be an easy way to do this.
If it helps, my resize method in dart looks like this:
void resize(Event event, var detail, var target) {
Element element = event.target;
print(element.scrollHeight);
element.style.height = "1px";
element.style.height = "${element.scrollHeight}px";
}
This is an interesting question.
I think the best approach would be to wrap the textarea in some autosize-textarea and there add
<polymer-element name="autosize-textarea">
<template>
<textarea id="inner" class="{{element.value.type}}" resize="none"
on-keypress="{{resize}}">
{{element.value.content}}</textarea>
</template>
</polymer-element>
import 'dart:html';
import 'package:polymer/polymer.dart';
#CustomTag('autosize-textarea')
class AutosizeTextarea extends PolymerElement {
AutosizeTextarea.created() : super.created();
#published
Element element;
void attached() {
super.attached();
resize(null, null, null);
}
void resize(Event event, var detail, var target) {
Element textarea $['inner'];
print(textarea.scrollHeight);
textarea.style.height = "1px";
textarea.style.height = "${textarea.scrollHeight}px";
}
}
and the use it like
<link rel="import" href="autosize_textarea.html">
<polymer-element name="page-content">
<template>
{{title}}
<ul>
<template repeat="{{element in elist | enumerate}}">
<li value={{element.index}}>
<autosize-textarea on-change="{{updateDatabase}}" element="{{element}}></autosize-textarea>
<div on-click="{{deleteElement}}">X</div>
</li>
</template>
</ul>
</template>
</polymer-element>
I'm not sure if I understand your code correctly because you named the item created from template repeat element and also the element you got from event.target. I'm not sure if/how they are related.
Not tested but I think it should work.
The code that I worked out is very similar to what Günter Zöchbauer suggested. It queries the shadow DOM (something I did not think to do) to get all the text areas at once (instead of just one) and update their heights accordingly.
The method could be in attached() or after any place where the content is dynamically fetched from the database in my case.
void updatePageSizes() {
var items = shadowRoot.querySelectorAll("textarea");
var j = items.iterator;
while (j.moveNext()) {
j.current.style.height = "${element.scrollHeight}px";
j.current.style.backgroundColor = "blue";
}
}
Also, I figured out that, to my knowledge, it is not possible to call a function in Dart from each element added in an enumerated list in a polymer element. I guess this is just a limitation of Dart.

How to bind repeating nested templates to complex models

Eventually I want to bind templates to a tree model, but to understand how binding works beyond object properties (simple or chained), lists, and maps, I've created a two-level set of repeating templates bound to a corresponding Dart model. Each item in the outer list contains an inner list.
o1
a
b
o2
c
d
When I select an inner item (for example 'd'), the click handler highlights the item and appends an Inner object 'e' to the 'o2' Outer object's list. Inspecting the model in the debugger shows that 'e' has been added to the model, but it is not being added as an 'li' element to the HTML list. How do I revise my code so that the inner template detects the change?
HTML
<polymer-element name="nested-templates">
<template>
<style>
:host { display: block; height: 100%; }
ul { margin: 0; padding: 0; }
li { font-size: 0.85rem; padding-left: 0.75rem; }
li:hover { background: lightgrey; cursor: pointer; }
li.selected { color: red; }
</style>
<div>
<template repeat="{{o in outer}}">
<strong>{{o.name}}</strong>
<ul>
<template repeat="{{i in o.inner}}">
<li id="{{i.name}}" on-click="{{innerClickHandler}}">{{i.name}}</li>
</template>
</ul>
</template>
</div>
</template>
<script type="application/dart" src="nested_templates.dart"></script>
Dart
import 'dart:html';
import 'package:polymer/polymer.dart';
#CustomTag('nested-templates')
class NestedTemplates extends PolymerElement {
#observable List<Outer> outer = toObservable([
new Outer('o1', [ new Inner('a'), new Inner('b')]),
new Outer('o2', [ new Inner('c'), new Inner('d')])
], deep: true);
void innerClickHandler(Event e, Map detail, HtmlElement target) {
target.classes.add('selected');
outer[1].inner.add(new Inner('e'));
}
NestedTemplates.created() : super.created();
}
class Inner extends Observable {
String name;
Inner(this.name);
}
class Outer extends Observable {
String name;
List<Inner> inner;
Outer(this.name, this.inner);
}
pubspec.yaml
dependencies:
...
polymer: 0.10.1+1
polymer_expressions: 0.11.0
...
dependency_overrides:
polymer_expressions: '0.11.0'
You need toObservable() for your inner too
new Outer('o1', toObservable([ new Inner('a'), new Inner('b')])),

In Dart-polymer I have been unable to find a way to keep a text-box scrolled to the bottom

I have found multiple ways to do this in various browsers and languages but nothing I can find has worked for Dart-polymer in Chrome.
Otherwise everything is very simple and standard:
<template>
<style>
textarea {
width: 825px;
}
</style>
<div>
<textarea id="ta" rows="10" on-mouseover="{{on_mouse_over}}">
{{message}}
</textarea>
</div>
</template>
Thanks!
I guess this is what you want:
#CustomTag('my-text')
class MyText extends PolymerElement {
#observable String message = "";
MyText.created() : super.created();
void messageChanged(old) {
var ta = $['ta'];
ta.scrollTop = ta.scrollHeight;
}
void attached() {
super.attached();
new Timer.periodic(new Duration(seconds: 1), (_) => message += 'some text ${new DateTime.now()}\n');
}
}
I added id="ta" to the <textarea> to make $['ta'] work

Resources