So I have the following Dart/Polymer Class:
#CustomTag('my-select-element')
class MySelectElement extends PolymerElement {
#observable int currentIndex = 1;
MySelectElement.created() : super.created() {}
void changed() {
print(currentIndex);
}
}
and the matching html template:
<template name="my-select-element">
<select on-change="{{changed}}" selectedIndex="{{currentIndex}}">
<option>Index 0</option>
<option>Index 1</option>
<option>Index 2</option>
</select>
</template>
When I click an option of the select element, I get the right index - from the click before.
As w3schools event attributes tells me, this is correct behavior and I should use onselect instead of onchange to get the value after it has changed.
However, I can't find onselect or on-select or anything like it, and when I build the select element via dart alone, onChange.listen delivers the desired, if incorrect, result.
I know I could always retrieve the result after a button has been pressed, but in this scenario, I want to do stuff without waiting.
Am I just missing the right keyword?
This way I get the selectedIndex after the selection.
When I tried to bind to value as well the default value stopped working (so I commented it out).
This example shows to ways to be notified about changes:
event handler
change notification of observable property
<polymer-element name="my-select" >
<template>
<select selectedIndex="{{currentIndex}}" on-change="{{changedHandler}}">
<option>Index 0</option>
<option>Index 1</option>
<option>Index 2</option>
</select>
<!-- <div>value: {{value}}</div> -->
<div>currentIndex: {{currentIndex}}</div>
</template>
<script type='application/dart' src='my_select.dart'></script>
</polymer-element>
library x;
//import 'dart:async';
import 'dart:html';
import 'package:polymer/polymer.dart';
#CustomTag('my-select')
class MySelect extends PolymerElement {
#observable int currentIndex = 1;
#observable int value = 1;
MySelect.created() : super.created() {
print('MySelect');
}
void changedHandler(event, details, Element target) {
print('changed: ${currentIndex}');
}
void currentIndexChanged(old) {
print('currentIndexChange: ${currentIndex}');
}
}
I had to adapt the changeEventHandler method to get the JS version to work properly:
void changeEventHandler(Event e, var details, var target) {
/* Fix JS bug - ${currentIndex} still refers to the old value whereas in Chrome it already has the new value */
currentIndex = target.selectedIndex;
}
Related
I would like to
display a list with core-list-dart
show a modal (paper-action-dialog) when I click on an element of the list
the modal will hold form fields in order to update the list item selected. What I have for now is the modal displaying bad information like if you click on the second item of the list it's the information of the first item displayed in the modal
Here is the code:
<core-list-dart data="{{data}}">
<template>
<div>
<div on-click="{{showModal}}">{{model.name}}</div>
<paper-action-dialog heading="edit exercise: {{model.name}}" backdrop autoCloseDisabled>
<paper-input label="name" floatingLabel></paper-input>
<paper-button dismissive>Cancel</paper-button>
<paper-button on-click="{{updateExercise}}" data-ex-id="{{index}}" affirmative>Ok</paper-button>
</paper-action-dialog>
</div>
</template>
</core-list-dart>
and the dart code
#CustomTag('exercise-list')
class ExerciseList extends PolymerElement {
#observable ObservableList data;
ExerciseList.created() : super.created();
// lifecycle method
void ready() {
data = toObservable([new Person('Bob'), new Person('Tim')]);
}
showModal(event, detail, target){
shadowRoot.querySelector('paper-action-dialog').toggle();
}
updateExercise(event, detail, target){
String id = target.dataset['ex-id'];
print(id);
}
}
class Person extends Observable {
// mandatory field
#observable int index;
// mandatory field
#observable bool selected;
//model
#observable String name;
Person(this.name);
}
how can I bind the good information ?
Thank you !
Complete Code here
shadowRoot.querySelector('paper-action-dialog').toggle();
finds the first 'paper-action-dialog' within exercise-list (which is the first item as long as you don't scroll down and items are not virtualized) and shows it.
A solution could be to create an attribute on the dialog with the index bound to it.
<core-list-dart data="{{data}}">
<template>
<div index="{{index}}">
<div index="{{index}}" on-click="{{showModal}}">{{model.name}}</div>
<paper-action-dialog heading="edit exercise: {{model.name}}" backdrop autoCloseDisabled>
<paper-input label="name" floatingLabel></paper-input>
<paper-button dismissive>Cancel</paper-button>
<paper-button on-click="{{updateExercise}}" data-ex-id="{{index}}" affirmative>Ok</paper-button>
</paper-action-dialog>
</div>
</template>
</core-list-dart>
and change your code to
var index = target.attributes['index'];
shadowRoot.querySelector('div[index="$index"] paper-action-dialog').toggle();
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
I'm using the repeat template of Dart Polymer to show the items of list. Now, I would like to implement a search filter, where only the items are displayed, which relates to the typed search term. Consider the following code:
my-element.html
<polymer-element name="my-element">
<script type="application/dart" src="my-element.dart"></script>
<template>
<input type="text" value="{{ searchTerm }}">
<ul>
<template repeat="{{ item in items }}">
<template if="{{ matchesSearchFilter(item) }}">
<li>{{ item }}</li>
</template>
</template>
</ul>
</template>
</polymer-element>
my-element.dart
#CustomTag('my-element')
class MyElement extends PolymerElement {
#observable List<String> items = toObservable(["Monday", "Tuesday", "Wednesday"]);
#observable String searchTerm = '';
MyElement.created() : super.created();
matchesSearchFilter(String item) {
if (searchTerm.isEmpty) return true;
return item.toLowerCase().contains(searchTerm.toLowerCase());
}
}
For example, when I type "Mo" in the textbox, I would expect that the list of items updates automatically, such that only "Monday" is shown. However on typing any search term, the list remains the same and the search term is ignored.
So, how to implement such a feature correctly?
An second alternative is to add a filter :
<template repeat="{{ item in items | filter(searchTerm)}}">
<li>{{ item }}</li>
</template>
and
// One line
filter(term) => (items) => term.isEmpty ? items : items.where((item) => item.toLowerCase().contains(searchTerm.toLowerCase())).toList();
Items will be filtered before being used in the repeat loop.
Personally I prefer to use a filter instead of Günter's solution because :
- it's easy
- centralized
- quite understandable to add other filters with pipe operator
- your list is not changed (only filtered)
For example, imagine that you also want to sort your items. Then just add another filter :
<template repeat="{{ item in items | filter(searchTerm) | sort(sortField, sortAsc)}}">
<li>{{ item }}</li>
</template>
Your items will be filtered and then sorted (each time searchTerm, sortField or sortAsc change). Easy ;-)
And the last tips: the previous code will filter your list at each keystroke which is not very user friendly!
Instead you can use that kind of code:
// Note that now I filter items on searchFilter and not more on searchTerm
<template repeat="{{ item in items | filter(searchFilter) | sort(sortField, sortAsc)}}">
<li>{{ item }}</li>
</template>
And then in your class
#CustomTag('my-element')
class MyElement extends PolymerElement {
#observable List<String> items = toObservable(["Monday", "Tuesday", "Wednesday"]);
#observable String searchTerm = '';
#observable String searchFilter = '';
MyElement.created() : super.created();
filter(term) => (items) => term.isEmpty ? items : items.where((item) => item.toLowerCase().contains(searchTerm.toLowerCase())).toList();
// When the user keystroke, searchTerm is changed
// Launch a Timer (if the timer was not null then stop it: it means the user has not finished
// When the delay is elapsed then set searchFilter with searchTerm
// As your filter use 'filter(searchFilter)' then your items will be filtered
Timer _searchTimer;
searchTermChanged(oldValue) {
// If there's another keystroke during the delay then reset it
if (_searchTimer != null) _searchTimer.cancel();
new Timer(new Duration(milliSeconds: 300), () => searchFilter = searchTerm);
}
}
An alternative solution would be to make a list allItems containing all items and when searchTerm changed update items like:
#observable List<String> items = ['Monday', 'Tuesday', 'Wdnesday'];
void searchTermChanged(oldVal) {
items = items.where((String item) {
if (searchTerm.isEmpty) return true;
return item.toLowerCase().contains(searchTerm.toLowerCase());
}).toList(growable: false);
}
In this case you shouldn't need the toObservable as it is redundant to observe if items are added or removed. To observe whether the items list was replaced the #observable attribut should be enough.
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();
}
}
In Dart's Web UI, it was possible to pass arbitrary data to function in response to events, for example, the following snippet passes the value 2 to the increment(int incBy) method in response to the button's on-click event:
<!-- Web UI -->
<element name="x-click-counter">
<template>
<button on-click="increment(2)"> <!-- passing a value of 2 -->
Click me
</button>
</template>
</element>
<script>
import 'package:web_ui/web_ui.dart';
class CounterComponent extends WebComponent {
void increment(int incBy) { // accept the value of 2
count = count + incBy;
}
}
</script>
In Polymer (and Polymer.dart), the on-click event attribute requires a string version of the function name, rather than an actual function call. This is described on the polymer docs page as:
The value of an event handler attribute is the string name of a method
on the component. Unlike traditional syntax, you cannot put executable
code in the attribute.
Using polymer.dart, this looks like:
<polymer-element name="x-click-counter">
<template>
<button on-click="increment"> <!-- can't pass a value of 2, as you need to pass a string -->
Click Me
</button>
</template>
</polymer-element>
<script>
import 'package:polymer/polymer.dart';
#CustomTag("x-click-counter")
class CounterComponent extends PolymerElement with ObservableMixin {
#observable int count = 0;
void increment(Event event, var detail, var target) { // How do I pass 2 to this function?
count = count ++;
}
}
</script>
Question: How do I pass an arbitrary value to the increment function?
You can use html data- attributes to pass extra data, and then access them through the target parameter.
Re-writing the polymer example to add a data-incby field that takes the value increment the count by looks like this:
<polymer-element name="x-click-counter">
<template>
<button on-click="increment" data-incby="2"> <!-- now passing the value as a data attribute -->
Click Me
</button>
</template>
</polymer-element>
<script>
import 'package:polymer/polymer.dart';
#CustomTag("x-click-counter")
class CounterComponent extends PolymerElement with ObservableMixin {
#observable int count = 0;
void increment(Event event, var detail, var target) {
int incBy = int.parse(target.attributes['data-incby']); // extract the value 2
count = count + incBy;
}
}
</script>
Dart and Polymer.dart have changed since Chris' answer. Here is updated code for Dart v1.0:
<polymer-element name="x-click-counter">
<template>
<button on-click="{{increment}}" data-incby="2"> <!-- now passing the value as a data attribute -->
Click Me
</button>
<span>{{count}}</span>
</template>
</polymer-element>
<script type="application/dart">
import 'package:polymer/polymer.dart';
import 'dart:html';
#CustomTag("x-click-counter")
class CounterComponent extends PolymerElement {
#observable int count = 0;
CounterComponent.created() : super.created();
void increment(Event event, var detail, var target) {
int incBy = int.parse(target.attributes['data-incby']); // extract the value 2
count = count + incBy;
}
}
</script>
My solution for Polymer 0.11.0+5
element.html
<link rel="import" href="../packages/polymer/polymer.html">
<polymer-element name="dp-element">
<template>
<div class="row">
<ul>
<template repeat="{{ item in items }}">
<li on-click="{{load}}" data-incby="{{item}}">{{ item }}</li>
</template>
</ul>
</template>
<script type="application/dart">
import 'package:polymer/polymer.dart';
import 'view.dart';
import 'dart:html';
#CustomTag('dp-element')
class DpElement extends PolymerElement {
#observable List<String> items;
DpElement.created() : super.created(){
}
void load(Event event, var detail, var target) {
String incBy = target.attributes['data-incby'];
print(incBy);
}
}