Polymer Dart $[] selector in an autobinding model - dart

Since polymer-body has been removed, we need to use an auto-binded template to use polymer binding features outside of a PolymerElement:
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sample app</title>
<script src="packages/web_components/platform.js"></script>
<script src="packages/web_components/dart_support.js"></script>
<link rel="import" href="packages/polymer/polymer.html">
<script src="packages/browser/dart.js"></script>
</head>
<body>
<template is="auto-binding-dart">
<div>Say something: <input value="{{value}}"></div>
<div>You said: {{value}}</div>
<button id="mybutton" on-tap="{{buttonTap}}">Tap me!</button>
</template>
<script type="application/dart">
import 'dart:html';
import 'package:polymer/polymer.dart';
import 'package:template_binding/template_binding.dart';
main() {
initPolymer().run(() {
Polymer.onReady.then((_) {
var template = document.querySelector('template');
templateBind(template).model = new MyModel();
});
});
}
class MyModel extends Observable {
//$['mybutton'] wont works there
#observable String value = 'something';
buttonTap() => print('tap!');
}
</script>
</body>
</html>
Unfortunately, the whole model now extends Observable, every binding seems to work, but the PolymerElement array selector $['foo'] cant be used anymore...
Is there any easy way to implement this $['id'] selector into an Observable model?

I would suggest to use a normal Polymer element instead of auto-binding-dart.
Then you don't have to care about differences and you need no 'main'.
I always start a Polymer project with an <app-element> Polymer element that acts as main() and container for the entire app.
I also would suggest to not use inline code.
As far as I know it has some limitations especially debugging is not supported (might be fixed already, I don't know because I never use it).
To make $ work you need a small and simple workaround;
import 'dart:html';
import 'package:polymer/polymer.dart';
import 'package:template_binding/template_binding.dart';
Map<String, dynamic> $; // we define our own '$'
main() {
initPolymer().run(() {
Polymer.onReady.then((_) {
var template = document.querySelector('template') as Polymer;
$ = template.$; // we assign template.$ to our own '$' so we can omit the 'template' part
templateBind(template).model = new MyModel();
});
});
}
class MyModel extends Observable {
//$['mybutton'] wont work there - it can't because this is outside of a method
#observable String value = 'something';
buttonTap() {
print($['mybutton'].id); // here it works
print('tap!');
}
}

Related

On-blur handler is triggered only on alternate events

I have a custom element which toggles hidden states of a span and an input element when clicked so that the user perceives that the span is replaced by the input each time he clicks it.
I want the input to toggle back to hidden state and span to visible state when the input loses focus.
However, once the input is revealed and given the focus, exiting the field (e.g., by clicking outside), the blur handler method is not triggered the first time. On repeating the process the second time, the blur is triggered. For the method to fire, one has to enter the field twice, each time.
How to avoid the need for entering into the input field twice to trigger the on-blur event?
I include my code.
index.html
<!DOCTYPE html>
<html>
<head lang="en">
<link rel="import" href="packages/polymer/polymer.html">
<link rel="import" href="toggle_el.html">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<toggle-el></toggle-el>
<script type="application/dart" src='main.dart'></script>
</body>
</html>
I am initializing polymer from the main.dart
import 'package:polymer/polymer.dart';
main() {
initPolymer();
}
Custom Element html file: toggle_el.html
<link rel="import" href="packages/polymer/polymer.html">
<polymer-element name="toggle-el">
<template>
<span hidden?="{{editMode}}"
on-click="{{switchEdit}}" id="name">{{name}}</span>
<input id="nmInput" hidden?="{{!editMode}}"
on-blur="{{switchEdit}}" type="text"
value="{{name}}"/>
</template>
<script type="application/dart" src="toggle_el.dart"></script>
</polymer-element>
Custom Element dart file: toggle_el.dart
import 'package:polymer/polymer.dart';
import 'dart:html';
#CustomTag('toggle-el')
class toggleElement extends PolymerElement {
#observable String name = 'Dartgnan';
#observable bool editMode = false;
toggleElement.created() : super.created();
#override ready();
void switchEdit(Event ev, var detail, Node sender) {
editMode = !editMode;
print('edit mode is now ${editMode ? 'on' : 'off'}');
if (editMode) {
InputElement el = $['nmInput'];
el.focus();
el.selectionStart = 0;
el.selectionEnd = 999;
}
}
}
Just change el.focus(); to new Future(() => el.focus());.
This way you delay focus a bit and allow the InputElement to get unhidden before focus() is called.

Auto convert html string to element

Say I have a polymer-element <polymer-element> <div id="foo"> {{html}} </div> </polymer-element>, where html is supposed to be a HTML formated string, like <p>blah</p>, what I want is, when html changes, the polymer-element also changes, and use html as its innerHtml, i.e. auto convert the string to an element and insert it as foo's child.
Can polymer/polymer_expression do this for me, or I have to do a querySelector(), then set html as innerHtml manually?
My solution is a custom element that extends a div and uses the DocumentFragment class to parse HTML strings into the DOM via data binding.
From my Github gist
<!-- Polymer Dart element that allows HTML to be inserted into the DOM via data binding -->
<link rel="import" href="packages/polymer/polymer.html">
<polymer-element name="html-display" extends="div">
<script type="application/dart">
import 'dart:html';
import 'package:polymer/polymer.dart';
#CustomTag('html-display')
class HtmlDisplay extends DivElement with Polymer, Observable {
#published String htmlContent;
// we need this stuff because we're extending <div> instead of PolymerElement
factory HtmlDisplay() => new Element.tag('div', 'html-display');
HtmlDisplay.created() : super.created() {
polymerCreated();
}
#override void attached() {
super.attached();
}
// respond to any change in the "htmlContent" attribute
void htmlContentChanged(oldValue) {
if (htmlContent == null) {
htmlContent = "";
}
// creating a DocumentFragment allows for HTML parsing
this.nodes..clear()..add(new DocumentFragment.html("$htmlContent"));
}
}
</script>
</polymer-element>
<!--
Once you've imported the Polymer element's HTML file, you can use it from another Polymer element like so:
<link rel="import" href="html_display.html">
<div is="html-display" htmlContent="{{htmlString}}"></div>
*htmlString* can be something like "I <em>love</em> Polymer Dart!"
-->
I use solution as described in https://stackoverflow.com/a/20869025/789338.
The key class is DocumentFragment.
The official way to do it is described in the doc: https://www.polymer-project.org/docs/polymer/databinding-advanced.html#boundhtml
The example on the doc:
<polymer-element name="my-element">
<template>
<div id="message_area"></div>
</template>
<script>
Polymer({
message: 'hi there',
ready: function() {
this.injectBoundHTML('<b>{{message}}</b>', this.$.message_area);
}
});
</script>
</polymer-element>

Google Chart is shown NgDirective, but not NgComponent

I'm having issues with showing Google Graphs in an AngularDart component, but not in an AngularDart directive. For the code below the graph is nicely displayed for the directive, where the div node has had two divs added as children to contain the graph and SVG. But for the compoenent, the graph is not shown, but in the DOM the same divs appear as children. Any ideas of how to debug this or solutions for it?
Running the code below prints out to console both Succesfully printed plot for Component graph and Succesfully printed plot for Directive graph.
The simplest code to showcase it is:
main.dart:
import 'dart:html' as dom;
import 'package:js/js.dart' as js;
import 'package:angular/angular.dart';
void printPlot(dom.Element element, String title) {
var gviz = js.context.google.visualization;
var gTableData = new js.Proxy(gviz.DataTable);
gTableData.addColumn('number', 'Size');
gTableData.addColumn('number', 'Counts');
gTableData.addRow(js.array([1, 2]));
gTableData.addRow(js.array([3, 4]));
var options = js.map({
'title': title
});
// Create and draw the visualization.
var chart = new js.Proxy(gviz.ScatterChart, element);
chart.draw(gTableData, options);
print("Succesfully printed plot for $title");
}
#NgComponent(selector: '[my-component]')
class MyComponent {
final dom.Element element;
MyComponent(this.element) {
js.context.google.load('visualization', '1', js.map({
'packages': ['corechart'],
'callback': (() => printPlot(element, 'Component graph'))
}));
}
}
#NgDirective(selector: '[my-directive]')
class MyDirective {
final dom.Element element;
MyDirective(this.element) {
js.context.google.load('visualization', '1', js.map({
'packages': ['corechart'],
'callback': (() => printPlot(element, 'Directive graph'))
}));
}
}
#NgController(selector: '[controller]',
publishAs: 'ctrl')
class Controller {
Controller() {
}
}
class DashboardModule extends Module {
DashboardModule() {
type(Controller);
type(MyComponent);
type(MyDirective);
}
}
void main() {
ngBootstrap(module: new DashboardModule());
}
index.html
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body controller>
<div my-component></div>
<div my-directive></div>
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="application/dart" src="main.dart"></script>
<script type="text/javascript" src="packages/browser/dart.js"></script>
</body>
</html>
This is very probably due to the shadowDOM introduced by the NgComponent.
When the JavaScript library doesn't support shadowDOM (which it probably doesn't) it just won't find your DOM elements and therefore can't act on them.
The AngularDart team is working on someone similar to NgComponent but without shadowDOM (which is currently not possible). But this is in experimental state only (as far as I know) and no release date available.

Dynamically add a web component to a div

I started with the generating click-counter example. I made the click-counter into a library which I then imported into my main file. The click-counter component can be added manually by putting the appropriate HTML into the web page before running the program. However, I've been unable to find a way to dynamically add the click-counter web component to a div. My attempts have either ended in "Aw, snap!" errors or simply with nothing happening.
The click-counter (xclickcounter.dart):
library clickcounter;
import 'package:web_ui/web_ui.dart';
class CounterComponent extends WebComponent {
int count = 0;
void increment() {
count++;
}
}
The main HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Sample app</title>
<link rel="stylesheet" href="test1.css">
<!-- import the click-counter -->
<link rel="components" href="xclickcounter.html">
</head>
<body>
<h1>Test1</h1>
<p>Hello world from Dart!</p>
<div id="sample_container_id">
<div is="x-click-counter" id="click_counter" count="{{startingCount}}"></div>
</div>
<script type="application/dart" src="test1.dart"></script>
<script src="packages/browser/dart.js"></script>
</body>
</html>
main file:
import 'dart:html';
import 'package:web_ui/web_ui.dart';
import 'xclickcounter.dart';
// initial value for click-counter
int startingCount = 5;
void main() {
// no error for adding an empty button
var button = new ButtonElement();
query('#sample_container_id').append(button);
// doesn't work (gives "Aw, snap!" in Dartium)
query('#sample_container_id').append(new CounterComponent());
// Nothing happens with this code. Nothing appears.
// But I promise this same thing was giving Aw, Snap
// for a very similar program
final newComponentHtml = '<div is="x-click-counter"></div>';
query('#sample_container_id').appendHtml(newComponentHtml);
}
I tried added an empty constructor to click-counter but it still crashes.
I had the same issue.
See the example (not mine) at https://github.com/dart-lang/web-ui/blob/master/test/data/input/component_created_in_code_test.html and let me know if it works for you.
TL;DR:
void main() {
var element = query('#sample_container_id');
appendComponentToElement(element, new CounterComponent() );
}
void appendComponentToElement(Element element, WebComponent component) {
component.host = new DivElement();
var lifecycleCaller = new ComponentItem(component)..create();
element.append(component.host);
lifecycleCaller.insert();
}
There's more info at my web-ui#dartlang.org post: https://groups.google.com/a/dartlang.org/d/topic/web-ui/hACXh69UqG4/discussion
Hope that helps.

How do I dynamically load HTML and insert into my web page with Dart?

How do I dynamically load a snippet of HTML and insert it into my web page? I am using Dart.
Glad you asked! Using Dart for this task isn't much different than JavaScript, except you get typing, code completion, and a slick editing experience.
First, create the snippet.html:
<p>This is the snippet</p>
Next, create the application. Notice the use of XMLHttpRequest to request the snippet. Also, use new Element.html(string) to create a block of HTML from a string.
import 'dart:html';
void main() {
var div = querySelector('#insert-here');
HttpRequest.getString("snippet.html").then((resp) {
div.append(new Element.html(resp));
});
}
Finally, here's the host HTML page:
<!DOCTYPE html>
<html>
<head>
<title>dynamicdiv</title>
</head>
<body>
<h1>dynamicdiv</h1>
<div id="insert-here"></div>
<script type="application/dart" src="dynamicdiv.dart"></script>
<script src="packages/browser/dart.js"></script>
</body>
</html>
main.dart:
import 'dart:html';
DivElement div = querySelector('div');
main() async {
String template = await HttpRequest.getString("template.html");
div.setInnerHtml(template, treeSanitizer: NodeTreeSanitizer.trusted);
}
template.html:
<h1>Hello world.</h1>
Check my bird... <em>it flies</em> !
<img src="https://www.dartlang.org/logos/dart-bird.svg">
For the full example, that runs out of the box, see:
https://gist.github.com/kasperpeulen/536b021ac1cf397d4e6d
Note that you need 1.12 to get NodeTreeSanitizer.trusted working.
You can try this example.
https://jsfiddle.net/kofwe39d/ (JS compiled from Dart source code.)
web/main.dart
import 'dart:async';
import 'dart:html';
import 'package:virtual_dom/components/component.dart';
import 'package:virtual_dom/features/state.dart';
import 'package:virtual_dom/helpers/h.dart';
import 'package:virtual_dom/helpers/mount.dart';
import 'package:virtual_dom/helpers/styles.dart';
import 'package:virtual_dom/helpers/vhtml.dart';
void main() {
final app = document.getElementById('app')!;
mount(app, _App());
}
class _App extends Component {
#override
Object render() {
final timer = State.get('timer', () => 3);
final setTimer = State.set<int>('timer');
if (timer > 0) {
Timer(Duration(seconds: 1), () {
setTimer(timer - 1);
});
}
final html = timer > 0
? ''
: '''
Hello, <strong>World!</strong>
''';
final style = styles({'padding': '6px'});
return h('div', {
'style': style
}, [
if (timer > 0) '$timer sec',
h('p', 'Your html:'),
vHtml('div', html),
]);
}
}
web/index.html
<!DOCTYPE html>
<html style="height: 100%;">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Example application</title>
<link rel="stylesheet" href="normalize.css">
<link rel="stylesheet" href="styles.css">
<script defer src="main.dart.js"></script>
</head>
<body style="height: 100%; font-family: Verdana,sans-serif; font-size:15px; line-height:1.5">
<div id="app" style="height: 100%;"></div>
</body>
</html>

Resources