How do I structure a "Controller" in Dart's web_ui? - dart

I have the following code
xviewcontainer.html
<!DOCTYPE html>
<html>
<head>
<title>xviewcontainer</title>
<link rel="components" href="xsearch.html">
<link rel="components" href="xcard.html">
</head>
<body>
<element name="x-view-container" constructor="ViewContainerComponent" extends="div">
<template>
<template instantiate="if view == 'SEARCH_VIEW'">
<x-search></x-search>
</template>
<template instantiate="if view == 'CARD_VIEW'">
<x-card></x-card>
</template>
</template>
</element>
<script type="application/dart" src="xviewcontainer.dart"></script>
<!-- for this next line to work, your pubspec.yaml file must have a dependency on 'browser' -->
<script src="packages/browser/dart.js"></script>
</body>
</html>
xviewcontainer.dart
import 'package:web_ui/web_ui.dart';
class ViewContainerComponent extends WebComponent {
String view = 'SEARCH_VIEW';
}
I have the event handling code within some other currently rendered sub-component of x-search. How do I get a reference to the containing x-view-container instance? I wish to change the .view property so that x-view-container will render x-card instead of the currently rendered x-search. I would be specifically interested in how to do so from my event handlers relative position, how to do it in a absolute fashion, as well as how to do so in any other manner.
void openCardView(){
WHAT_DO_I_PUT_HERE.view = 'CARD_VIEW';
}

You can query for the element you have on the DOM with query() method. Simplest example is query('x-view-container'). Or assign a class or an id on it and query against that. Then access the xtag property to get the actual web component instance.
Here's an example:
import 'package:web_ui/watcher.dart' as watchers;
main() {
// I'm assuming that the HTML tag is somewhere on the page.
query('x-view-container').xtag.view = 'CARD_VIEW';
watchers.dispatch(); // You may need to call this, or use #observable stuff.
}

Related

Single page application (SPA) using web components with Dart not JS

I'm trying to do a single page application (SPA) using web components as in the linked example. But with Dart instead of javascript. I'd like to put the whole <core-scaffold> in a Polymer Element. The layout is working as expected, but when I get to the part "Simplifying the markup using a data model" I can't understand how to bind my dart model equivalent to the javascript model in the example. Should this attribute <template is="auto-binding"> be in the main template of the Polymer element, or where does that go? Any pointers from you experts?
Edit: OK, I'm adding some code to this question.
In darteditor i started a new project with the option using polymer library selected. In the created main-html-file all I did was remove the "counter"-attribute in <click-counter>. I added these dependencies paper_elements: 0.5.0, polymer: any I replaced everything in clickcounter.dart with:
import 'package:polymer/polymer.dart';
import 'dart:html';
#CustomTag('click-counter')
class ClickCounter extends PolymerElement {
#observable var route = 0;
#observable List<dynamic> pages = [
{'name': 'Single', 'hash': 'one'},
{'name': 'page', 'hash': 'two'},
{'name': 'app', 'hash': 'three'}
];
ClickCounter.created() : super.created() {
}
}
And I replaced everything in clickcounter.html with:
<link rel="import" href="packages/polymer/polymer.html">
<link rel="import" href="packages/core_elements/core_scaffold.html">
<link rel="import" href="packages/core_elements/core_header_panel.html">
<link rel="import" href="packages/core_elements/core_toolbar.html">
<link rel="import" href="packages/core_elements/core_menu.html">
<link rel="import" href="packages/core_elements/core_item.html">
<link rel="import" href="packages/core_elements/core_transition.html">
<link rel="import" href="packages/core_elements/core_animated_pages.html">
<link rel="import" href="packages/core_elements/core_animated_pages/transitions/core_transition_pages.html">
<link rel="import" href="packages/paper_elements/paper_item.html">
<polymer-element name="click-counter">
<template>
<style>
</style>
<core-scaffold>
<core-header-panel navigation flex mode="waterfall">
<core-toolbar>Application</core-toolbar>
<core-menu theme="core-light-theme" valueattr="hash" selected="{{route}}">
<template repeat="{{page in pages}}">
<paper-item icon="settings" label="{{page['name']}}" hash="{{page['hash']}}">
<a _href="#{{page['hash']}}"></a>
</paper-item>
</template>
</core-menu>
</core-header-panel>
<div tool>Title</div>
<core-animated-pages selected="{{route}}" transitions="slide-from-right">
<template repeat="{{page in pages}}">
<section hash="{{page['hash']}}" layout vertical center-center>
<div>{{page['name']}}</div>
</section>
</template>
</core-animated-pages>
</core-scaffold>
</template>
<script type="application/dart" src="clickcounter.dart"></script>
</polymer-element>
I run in Dartium, and see a fine layout with the three menu options. The main area has the word "single" at startup, as the first menu option is selected. When I click the second menu option I get this error:
Exception caught during observer callback: TypeError: Cannot read property 'classList' of undefined
at core-animated-pages.Polymer.applySelection (http://localhost:8081/spa_test.html:939:15)
at core-animated-pages.Polymer.selectedItemChanged (http://localhost:8081/spa_test.html:4389:14)
at core-animated-pages.g.invokeMethod (http://localhost:8081/packages/polymer/src/js/polymer/polymer.js:13:25932)
at core-animated-pages.g.notifyPropertyChanges (http://localhost:8081/packages/polymer/src/js/polymer/polymer.js:13:24037)
at Object.x.report_ (http://localhost:8081/packages/polymer/src/js/polymer/polymer.js:12:18274)
at Object.S.check_ (http://localhost:8081/packages/polymer/src/js/polymer/polymer.js:12:22612)
at c (http://localhost:8081/packages/polymer/src/js/polymer/polymer.js:12:12181) polymer.concat.js:4861
If you put everything in a Polymer element you use this Polymer element instead of <template is="auto-binding"> not in addition to. The model is the class of the Polymer element that contains <core-scaffold>.
If you add more code that shows what you try to accomplish it is easier to make concrete suggestions.
Update
I changed
#observable var route = 'one';
to get rid of the exception
and added
routeChanged(oldVal, newVal) {
print(newVal);
}
to see what values are assigned (prints 'one', 'two', 'three').
and also added
<link rel="import" href="packages/paper_elements/paper_item.html">

Polymer Dart #initMethod not executing

I was fiddling with the "new" instructions for polymer dart 0.10.0-pre.10 only to realize I had package 0.9.5 installed (on an updated Dart Editor). And could only get code to run using main() => dostuff(); Adding component1 as per instructions just broke whatever worked.
I set pubspec.yaml polymer dependency to >= 0.9.9 and it auto pub gets the version 0.10.0-pre.10. Then I made changes as suggested and moved dostuff() to a custom element class (extends PolymerElement) and put #initMethod above it. It does not run.
And as I got it to run before I was unable to find a way to bind new items from a JSON file (which I successfuly got through http) to the polymer element.
mylist.dart
import 'package:polymer/polymer.dart';
import 'dart:html';
#CustomTag('my-list')
class MyListElement extends PolymerElement {
#observable List mylist = ['one', 'two', 'three'];
#initMethod
static dostuff() {
print("initMethod");
// get json and pass to mylist
}
}
mylist.html
<polymer-element name="my-list">
<template>
<ul>
<template repeat="{{item in mylist}}">
<li>{{item}}</li>
</template>
</ul>
</template>
<script type="application/dart;component=1" src="mylist.dart"></script>
</polymer-element>
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sample app</title>
<link rel="import" href="packages/polymer/polymer.html">
<link rel="import" href="mylist.html">
<script src="packages/browser/dart.js"></script>
</head>
<body>
<h1>MyList</h1>
<div id="container1">
<json-list id="my-list1"></json-list>
</div>
</body>
</html>
This code (especially #initMethod) inside the element doesn't make sense.
#CustomTag('my-list') does this already. You need #initMethod() only when you want a method executed like main() that is outside of an Polymer element.
You can put this code inside the constructor of MyListElement or better inside polymerCreated before the super call.
import 'package:polymer/polymer.dart';
import 'dart:html';
#CustomTag('my-list')
class MyListElement extends PolymerElement {
#observable List mylist = ['one', 'two', 'three'];
#override
polymerCreated() {
print("initMethod");
// get json and pass to mylist
super.polymerCreated();
}
}
You didn't get Polymer 0.10.0-pre.10 because it is a pre-release which is indicated by the - after the patch version (not by pre).
Pub by default ignores pre-releases. You have to enforce them by a version constraint like '>=0.10.0-'

App inside an html element

I'm trying to find a clean way to use twice a SVG element I've created. I want to learn using all technologies related to dart so I'm experimenting with Polymer. I've created a custom element where I then load another custom element twice.
This is the index.html
<link rel="import" href="da-imageTool.html">
</head>
<body>
<da-imageTool ></da-imageTool>
<script type="application/dart">export 'package:polymer/init.dart';</script>
<script src="packages/browser/dart.js"></script>
<script src="packages/browser/interop.js"></script>
</body>
da-imageTool.html
<polymer-element name="da-imageTool">
<template>
<div class="images" id="mainDiv"></div>
<div class="images" id="secondDiv"></div>
</template>
<script src="da-imageTool.dart" type="application/dart"></script>
</polymer-element>
da-imageTool.dart
#CustomTag('da-imageTool')
class ImageTool extends PolymerElement{
DivElement div1;
DivElement div2;
ImageEditor ime;
//ImageEditor ime2;
//ImageEditor _selectedEditor;
ImageTool.created(): super.created(){
int a = 1;
}
}
int a = 1 is just a line where I set a breakpoint to find out why all this does not work.
I don't understand why but the element isn't created. Here is the error I get:
Exception: The null object does not have a getter '_observe#0x29474386'.
NoSuchMethodError : method not found: '_observe#0x29474386'
Receiver: null
Arguments: []
HtmlElement&Polymer.observeProperties (package:polymer/src/instance.dart:514:34)
HtmlElement&Polymer.prepareElement (package:polymer/src/instance.dart:153:22)
HtmlElement&Polymer.polymerCreated (package:polymer/src/instance.dart:139:21)
PolymerElement.PolymerElement.created (package:polymer/src/instance.dart:1088:19)
ImageTool.ImageTool.created (http://localhost:3030/FigureSVG/web/da-imageTool.dart:17:24)
The script tags go into the head with Polymer
not sure about the interop.js script.
<head>
<link rel="import" href="da-imageTool.html">
<script type="application/dart">export 'package:polymer/init.dart';</script>
<script src="packages/browser/dart.js"></script>
</head>
<body>
<da-imageTool ></da-imageTool>
<script src="packages/browser/interop.js"></script>
</body>
I did several tests and right now my code is dead simple. The correction that made it working are:
Move the script tags
Rename the custom element all lowercase.
I remember having read about the compulsory hypen, but I've not seen anything about lowercase :( A warning might have been useful.

Custom Element - null reference from query() in constructor

I'm working on my first Dart app, having completed the Game of Darts tutorials. I am trying to create a semantically named top-menu element that will eventually display a list of navigation menu tabs at the top of my page. My Dart app is able to recognize my custom element and calls the associated constructor.
However, I am getting a null reference when trying to query for the UL element within my custom element. I need the UL reference in order to dynamically load my LI elements into the menu.
Question 1:
Should the element be visible in the DOM at the point where the constructor is running?
Question 2:
If it is not yet visible, is there a Dart event I can use to trigger loading of the LI elements after the custom element has been completely loaded into the DOM?
Thanks in advance! For reference, here is the source of my custom element:
topmenu-element.html
<!DOCTYPE html>
<html>
<body>
<element name="top-menu" constructor="TopMenu" extends="div">
<template>
<div>
Top Menu
<ul id="top-menu-list"></ul>
</div>
</template>
<script type="application/dart" src="topmenu-element.dart"></script>
</element>
</body>
</html>
topmenu-element.dart
import 'package:web_ui/web_ui.dart';
import 'dart:html';
class TopMenu extends WebComponent {
List menuItems = ['Session', 'Authentication Services', 'Vehicle Services', 'Subscriber Services', 'Data Services'];
void populateMenu() {
UListElement menuList = query('#top-menu-list');
LIElement newMenuItem = new LIElement();
newMenuItem.text = menuItems[0];
menuList.children.add(newMenuItem);
}
TopMenu() {
// populateMenu();
}
}
I can't speak specifically about the DOM visibility in a constructor with the query method as I'm truthfully not certain. However there are perhaps better methods which you can use, which are called at various stages in the elements lifecycle.
That said, can I ask why you need to use this particular method to add the children. It is probably much easier to do it with the template repeat like so:
<!DOCTYPE html>
<html>
<body>
<element name="top-menu" constructor="TopMenu" extends="div">
<template>
<div>
Top Menu
<ul id="top-menu-list">
<li template repeat="item in menuItems">{{item}}</li>
</ul>
</div>
</template>
<script type="application/dart" src="topmenu-element.dart"></script>
</element>
</body>
</html>
Then there's no need to put any of your menu display code in your constructor.

When is the removed lifecycle method called for Dart web components?

Latest edit:
This is an open issue in web-ui: https://github.com/dart-lang/web-ui/issues/245
Previously:
I'm trying to figure out how to get removed() from the web component lifecycle methods to be called. Looking through the generated code for my below example, I see there's a call to autogenerated.dispatch(); after replaceElement() which I hoped would be what calls removed(), but I don't see my print statement output.
Maybe related: I glanced through the spec trying to understand what the output of build.dart is doing for the lifecycle methods. Perhaps the spec is out of date? I still don't see composeChildren() listed in the instantiation section of the spec (which is mentioned in this web-ui issue comment) even though composeChildren() gets called in the autogenerated code from build.dart.
The reason behind this question is my interest in a Dart webapp able to load and unload web components within a single parent html file programmatically (via the instantiation instructions in the spec), instead of having to declare web components in the html. I'm running with web_ui-0.2.11. Thanks!
webcomponent:
<!DOCTYPE html>
<html><body>
<element name="x-lifecycle-test" constructor="LifecycleTest" extends="div">
<template> {{foo}} </template>
<script type="application/dart">
import 'dart:html';
import 'package:web_ui/web_ui.dart';
var foo = "testing lifecycle methods";
class LifecycleTest extends WebComponent{
inserted() => print("inserted");
removed() => print("removed");
}
</script>
</element>
</body></html>
Parent html file:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"><title>Lifecycle</title>
<link rel="components" href="lifecycle_test.html">
</head>
<body>
<div>
<button on-click="replaceElement()">replace element</button>
</div>
<div id='holder'>
<x-lifecycle-test></x-lifecycle-test>
</div>
<script type="application/dart">
import 'dart:html';
void replaceElement() {
query('#holder').replaceWith(new DivElement()
..id = 'holder'
..text = 'replaced');
}
main() {}
</script>
<script type='text/javascript' src="https://dart.googlecode.com/svn/branches/bleeding_edge/dart/client/dart.js"></script>
</body>
</html>
Adding an answer here for completeness: web components are used in Dart using Polymer. When an instance of a custom element is removed from the DOM, the leftView life cycle method triggers. You can read more about this at https://www.dartlang.org/docs/tutorials/polymer-intro/#life-cycle-methods.

Resources