I have built a Polymer.dart app that uses nested Polymer elements. The parent element takes an attribute and passes its value to the nested, child elment as an attribute. This works fine when "Run in Dartium" from within DartEditor, but the nested element fails to load after the app is "Pub Built" and "Run as JavaScript." There are no error messages during the build process, or any pointers of any other sort. I don't know how to debug this and the fact that it runs as expected without any warnings or errors in Dartium doesn't help.
The following is the code for the simplified version of my app that produces the same problem. my_view is the parent element and my_form is the nested element that is attached when my_view is loaded.
main.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sample</title>
<link rel="import" href="my_view.html">
<script type="application/dart">export 'package:polymer/init.dart';</script>
<script src="packages/browser/dart.js"></script>
</head>
<body>
<h1>The view polymer element should appear below:</h1>
<my-view viewAttribute="My_Value"></my-view>
</body>
</html>
my_view.html
<polymer-element name="my-view" attributes="viewAttribute">
<link rel="import" href="my_form.html">
<template>
<div style="width: 100%;"><h1>The form should appear below:</h1></div>
<div id="form_div" style="width: 100%;"></div>
</template>
<script type="application/dart" src="my_view.dart"></script>
</polymer-element>
my_view.dart
import 'package:polymer/polymer.dart';
import 'dart:html';
#CustomTag('my-view')
class MyView extends PolymerElement {
#published String viewAttribute;
DivElement _formSlot;
MyView.created() : super.created() {
_formSlot = $['form_div'];
}
void viewAttributeChanged() {
_formSlot..append(new Element.tag('form', 'my-form')..setAttribute("formAttribute", viewAttribute));
}
}
my_form.html
<polymer-element name="my-form" extends="form" attributes="formAttribute">
<template>
<div style="width: 100%;">Attribute value: {{formAttribute}}</div>
<div style="width: 100%;">
<label for="nameInput">name:</label>
<input id="nameInput" type="text" value="{{nameValue}}" />
</div>
<div style="width: 100%;">
<div id="button_div">
<input type="submit" on-click="{{submitForm}}" value="send" />
</div>
</div>
</template>
<script type="application/dart" src="my_form.dart"></script>
</polymer-element>
my_form.dart
import 'package:polymer/polymer.dart';
import 'dart:async';
import 'dart:html';
import 'dart:convert';
#CustomTag('my-form')
class MyForm extends FormElement with Polymer, Observable {
#published String formAttribute;
#observable String nameValue;
HttpRequest _request;
MyForm.created() : super.created();
void submitForm(Event e, var detail, Node target) {
e.preventDefault();
_request = new HttpRequest();
_request.onReadyStateChange.listen(_onData);
_request.open('POST', 'http://my.server.com/contact_form');
_request.send(JSON.encode({'name': nameValue, 'attribute': formAttribute}));
}
_onData(_) {
if (_request.readyState == HttpRequest.DONE) {
switch (_request.status) {
case 200:
// Data was posted successfully
break;
case 0:
// Post failed
break;
}
}
}
}
Any help, hints, well wishes, prayers would be greatly appreciated! Thanks!
I guess this is a dart2js bug.
For answering this question Dynamically create polymer element I built an example.
This code (like you showed in your question) generates the correct markup but the form element didn't work properly. I didn't examine this behaviour further because I myself never needed to extend a DOM element.
You could also try not to extend the <FORM> element but create a new Polymer element and just embed the <FORM> element.
Related
How can I bind to an event handler using <template is="dom-bind">?
index.dart
import 'dart:html';
import 'package:polymer/polymer.dart';
DomBind template;
main() async {
await initPolymer();
template = document.querySelector('#t');
final model = new MyModel();
template['model'] = model;
// Polymer 1.0 event handlers can't have `.` in declarative syntax
// and therefore need to be assigned to the template directly.
template['buttonTap'] = model.buttonTap;
}
class MyModel extends JsProxy {
// reflectable is Polymer 1.0.0-rc.2 (was #eventHandler in rc.1)
#reflectable
String value = 'something';
#reflectable
buttonTap([Event e, args]) {
print(template.$['mybutton'].id);
print('tap! $value');
}
}
index.dart
<!doctype html>
<html>
<head>
<link rel="import" href="packages/polymer/polymer.html">
<script src="packages/web_components/webcomponents-lite.min.js"></script>
<script src="packages/browser/dart.js"></script>
</head>
<body>
<template id="t" is="dom-bind">
<div>Model: <span>{{model}}</span></div>
<div>Say something: <input value="{{model.value::change}}"></div>
<div>You said: <span>{{model.value}}</span></div>
<button id="mybutton" on-tap="buttonTap">Tap me!</button>
</template>
<script type="application/dart" src="index.dart"></script>
</body>
</html>
I'd like to build a polymer element consisting of table whose row CSS varies depending on the certain data, I got some help here to implement something similar in dart but I'm exploring the option of using a polymer element. The dart implementation had:
void highlightRows(){
querySelectorAll('tr').where(
(tr)=> tr.children.any(
(td)=>td.text == 'Approved'
)
).forEach((approvedTr)=>approvedTr.classes.add('foo'));
}
Which only caters for rows that have "Approved" I'd like the polymer element to cater for rows that have "Denied" and "none". I'd appreciate some pointers.
EDIT
I updated the code with an example I just tried.
I use DartEditor dev channel and Polymer 0.10.0-pre.12
EDIT END
I didn't try the code but I think it should work.
You need some more Dart/HTML boilerplate to make a working Polymer element.
See https://www.dartlang.org/polymer-dart/ to get started.
In the Polymer element you need a field that holds a collection with a value for each row.
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Sample app</title>
<!-- import the test-element -->
<link rel="import" href="packages/polymer/polymer.html">
<link rel="import" href="test_element.html">
</head>
<body>
<h1>TestPolymer</h1>
<p>Sort observable</p>
<test-element></test-element>
</body>
</html>
test_element.html
<polymer-element name="test-element">
<template>
<style>
tr.foo {
background-color: #00ff00;
}
</style>
<table>
<tr template repeat="{{row in rows}}" class="{{ {'foo': row.contains('Approved')} }}">
<td template repeat="{{cell in row}}">{{cell}}</td>
</tr>
</table>
</template>
<script type="application/dart" src="test_element.dart"></script>
</polymer-element>
test_element.dart
library test_element;
import 'dart:async';
import 'package:polymer/polymer.dart';
#CustomTag('test-element')
class PeopleElement extends PolymerElement {
List<List<String>> rows = toObservable( // toObservable allows to update the HTML itself when the list content changes
[
['Row1-cell1', 'Row1-cell1', 'Row1-cell3', 'Approved'],
['Row2-cell1', 'Approved', 'Row2-cell3', 'Row2-cell4'],
['Approved', 'Row3-cell2', 'Row3-cell3', 'Row3-cell4'],
['Row4-cell1', 'Row4-cell2', 'Row4-cell3', 'Row4-cell4']
]);
PeopleElement.created() : super.created() {
print('PeopleElement');
}
}
[Moving this answer from the previous thread as an additional example of Gunter's answer]
Here is an example using a Polymer-Element to show the table (you can also use data-binding without creating a polymer element according to the doc, but i dunno if it works with dart):
<meta charset="utf-8">
<polymer-element name="myapp-request-table">
<style>
.Approved{
background-color: green;
}
.Denied{
background-color: red;
}
...
</style>
<table id="requests">
<template reapeat="{req in myRequestList}">
<tr class="{{req.statut}}"> <!-- row class depends on request.statut value-->
<td>{{req.title}}</td>
<td>{{req.statut}}</td>
<td>{{req.date}}</td>
</tr>
</template>
</table>
</polymer-element>
To use this html, you need to provide a compliant data context.
In this example, myRequestList is a List of a specific model object set in the polymer-element code like this:
#CustomTag('myapp-request-table')
class MyAppRequestTable extends PolymerElement{
#observable ObservableList<MyRequestClass> myRequestList = new ObservableList<MyRequestClass>(); //here is the data context
MyAppRequestTable.created() : super.created(){
myRequestList.addAll([new MyRequestClass('foo','denied',new DateTime.now()), new MyRequestClass('42','approved',new DateTime.now())]); //filling the data-context once the element is created
}
}
class MyRequestClass extends Observable {
#observable String title;
#observable String statut;
#observable DateTime date;
MyRequestClass(this.title,this.statut,this.date);
}
Then you juste have to import the custom element anywhere you want, and set its data context from outside.
I have a Dart WebComponent that obtains information periodically from a web service. I'd like to inject the web service into the component and have the component decide when to call the web service (this will allow me to prototype all the UI code using a mock web service with no HTTP call until the service is written).
The issue I'm having is that the web service object I'm sending to the WebComponent seems to be null until the page is rendered. I'm not exactly sure when the service reference is passed to the page, but it seems to happen after the page is constructed since the value is null in the WebComponent constructor, but I can see that the value is instantiated when the page itself is rendered. How can I be notified that the service object is now available to the page so I can call the web service?
Follow up question: Is passing the service reference into the WebComponent like I am below a bad practice? If so, what should I do instead to separate the mock implementation so I can inject the real web service later without changing any code.
Here's my base Dart page:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Dart Prototype</title>
<link rel="stylesheet" href="dart_prototype.css">
<link rel="import" href="location_container.html">
</head>
<body>
<h1>Dart Prototype</h1>
<div id="location_management_container">
<location-container location-service="{{applicationContext.locationService}}" count="{{startingCount}}"></location-container>
</div>
<script type="application/dart">
import 'package:web_ui/web_ui.dart';
import 'package:dart_prototype/dart_prototype_library.dart';
final ApplicationContext applicationContext = new ApplicationContext(
new WebService()
);
int startingCount = 5;
main() {
print('main initializing');
print(applicationContext);
print(applicationContext.locationService);
}
</script>
<script src="packages/browser/dart.js"></script>
</body>
</html>
Here's the code for location-container
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<element name="location-container">
<template>
<div>
<ul id="todo-list">
<li>hi there!</li>
<li>{{count}}</li>
<li>{{locationService}}</li>
</ul>
</div>
</template>
<script type="application/dart">
import 'package:web_ui/web_ui.dart';
import 'package:dart_prototype/dart_prototype_library.dart';
class LocationContainer extends WebComponent {
#observable
WebService locationService;
#observable
int count;
LocationContainer() {
print('in loc container constructor');
print(locationService);
print(count);
}
created() {
print('created!');
print(locationService);
print(count);
}
}
</script>
</element>
</body>
</html>
Here's the code for ApplicationContext and WebService
part of prototype_library;
class ApplicationContext {
final WebService locationService;
ApplicationContext(
this.locationService);
}
class WebService {
final Factory _objectFactory;
WebService(this._objectFactory);
Future call(String url) {
throw new UnimplementedError();
}
}
And here's the result of my print strings statements to the console
Invalid CSS property name: -webkit-touch-callout
main initializing
Instance of 'ApplicationContext'
Instance of 'WebService'
in loc container constructor
null
null
created!
null
null
And here's what my rendered page returns:
Dart Prototype
- hi there!
- 5
- Instance of 'WebService'
And the source of that page...
<!DOCTYPE html>
<!-- This file was auto-generated from web/dart_prototype.html. -->
<html><head><style>template { display: none; }</style>
<meta charset="utf-8">
<title>Dart Prototype</title>
<link rel="stylesheet" href="../dart_prototype.css">
</head>
<body>
<h1>Dart Prototype</h1>
<div id="location_management_container">
<span is="location-container"></span>
</div>
<script type="application/dart" src="dart_prototype.html_bootstrap.dart"></script><script src="../packages/browser/dart.js"></script>
</body></html>
I think my problem may be solved by using the inserted life cycle method instead of created.
Initially when I read the WebComponent life cycle method description, it said:
Invoked whenever a component is added to the DOM.
I'm still not sure if this is the right method to use, but I have the object I'm looking for - so I'm going to continue to experiment. Here's my updated WebComponent
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<element name="location-container">
<template>
<div>
<ul id="todo-list">
<li>hi there!</li>
<li>{{count}}</li>
<li>{{locationService}}</li>
</ul>
</div>
</template>
<script type="application/dart">
import 'package:web_ui/web_ui.dart';
import 'package:dart_prototype/dart_prototype_library.dart';
class LocationContainer extends WebComponent {
#observable
WebService locationService;
#observable
int count;
LocationContainer() {
print('in loc container constructor');
print(locationService);
print(count);
}
created() {
print('created!');
print(locationService);
print(count);
}
inserted() {
print('inserted!');
print(locationService);
print(count);
}
}
</script>
</element>
</body>
</html>
I'm having trouble binding a model to a table if I'm not using a custom element.
Custom element works fine, but when I try to bind the model manually without creating a custom component, no data is displayed. Any Ideas?
custom_table.html
<!DOCTYPE html>
<html>
<body>
<polymer-element name="custom-table" extends="div" apply-author-styles>
<template>
<table>
<tbody>
<tr template repeat="{{people}}">
<td>{{firstName}}</td>
<td>{{lastName}}</td>
</tr>
</tbody>
</table>
</template>
<script type="application/dart" src="custom_table.dart"></script>
</polymer-element>
</body>
</html>
custom_table.dart
import 'package:polymer/polymer.dart';
class Person {
String firstName;
String lastName;
Person(this.firstName, this.lastName);
}
#CustomTag('custom-table')
class CustomTable extends PolymerElement with ObservableMixin {
List people = [new Person('Bob', 'Smith'), new Person('Alice', 'Johnson')];
}
No custom element version:
polymer_test.html
<!DOCTYPE html>
<head>
<script src="packages/polymer/boot.js"></script>
</head>
<html>
<head>
<meta charset="utf-8">
<title>Polymer test</title>
<link rel="stylesheet" href="polymer_test.css">
</head>
<body>
<h1> Table test </h1>
<template id="tableTemplate" repeat>
<table>
<tbody>
<tr template repeat="{{ people }}">
<td>{{firstName}}</td>
<td>{{lastName}}</td>
<tr>
</tbody>
</table>
</template>
<script type="application/dart" src="polymer_test.dart"></script>
</body>
</html>
polymer_test.dart
import 'dart:html';
import 'package:polymer/polymer.dart';
import 'package:fancy_syntax/syntax.dart';
class Person {
String firstName;
String lastName;
Person(this.firstName, this.lastName);
}
class Message extends Object with ObservableMixin {
List<Person> people = toObservable([new Person('Bob', 'Smith'), new Person('Alice', 'Johnson')]);
}
main() {
var msgModel = new Message();
TemplateElement template = query('#tableTemplate');
template.bindingDelegate = new FancySyntax();
template.model = msgModel;
}
Does it help to add this to main?
void main() {
initPolymer([], () {
// put existing main code in here
});
}
polymer/boot.js should do this, but I think right now it isn't kicking in unless the page has at least one polymer-element (this is a bug).
alternatively, you could add a dummy <polymer-element> to the HTML. That will workaround the problem.
bind attribute was missing in my template definition. Instead, I wrongly used repeat.
It should be:
<template id="tableTemplate" bind>
Instead of:
<template id="tableTemplate" repeat>
Can you try
templateBind(query('#tableTemplate'))
..bindingDelegate = new FancySyntax()
..model = msgModel;
See http://api.dartlang.org/docs/releases/latest/template_binding.html#templateBind
For illustration reasons, I've created a class inheriting from WebComponent called FancyOption that changes to a background color specified by text in one child element upon clicking another child element.
import 'package:web_ui/web_ui.dart';
import 'dart:html';
class FancyOptionComponent extends WebComponent {
ButtonElement _button;
TextInputElement _textInput;
FancyOptionComponent() {
// obtain reference to button element
// obtain reference to text element
// failed attempt
//_button = this.query('.fancy-option-button');
// error: Bad state: host element has not been set. (no idea)
// make the background color of this web component the specified color
final changeColorFunc = (e) => this.style.backgroundColor = _textInput.value;
_button.onClick.listen(changeColorFunc);
}
}
FancyOption HTML:
<!DOCTYPE html>
<html>
<body>
<element name="x-fancy-option" constructor="FancyOptionComponent" extends="div">
<template>
<div>
<button class='fancy-option-button'>Click me!</button>
<input class='fancy-option-text' type='text'>
</div>
</template>
<script type="application/dart" src="fancyoption.dart"></script>
</element>
</body>
</html>
I have three of them on a page like this.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Sample app</title>
<link rel="stylesheet" href="myapp.css">
<link rel="components" href="fancyoption.html">
</head>
<body>
<h3>Type a color name into a fancy option textbox, push the button and
see what happens!</h3>
<div is="x-fancy-option" id="fancy-option1"></div>
<div is="x-fancy-option" id="fancy-option2"></div>
<div is="x-fancy-option" id="fancy-option3"></div>
<script type="application/dart" src="myapp.dart"></script>
<script src="packages/browser/dart.js"></script>
</body>
</html>
Just use getShadowRoot() and query against it:
import 'package:web_ui/web_ui.dart';
import 'dart:html';
class FancyOptionComponent extends WebComponent {
ButtonElement _button;
TextInputElement _textInput;
inserted() {
// obtain references
_button = getShadowRoot('x-fancy-option').query('.fancy-option-button');
_textInput = getShadowRoot('x-fancy-option').query('.fancy-option-text');
// make the background color of this web component the specified color
final changeColorFunc = (e) => this.style.backgroundColor = _textInput.value;
_button.onClick.listen(changeColorFunc);
}
}
Where x-fancy-option string is the name of the element.
Note: I changed your constructor to be inserted() method, which is a life cycle method.
I understand that _root is depracated. Answers recommending _root should use getShadowRoot() in place of _root.