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

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

Related

How to scroll to the last element of iron-list (dart polymer 1.0)?

I want to scroll to the last element of iron-list after each update of its model but calling IronList.scrollToIndex() shows only part of the list. More clearly, I have polymer element:
#PolymerRegister('main-app')
class MainApp extends PolymerElement {
#property
List<String> items = [];
MainApp.created() : super.created();
#reflectable
void onAdd([_, __]){
var fst = items.length;
var lst = new List.generate(30, (x) => (fst+x).toString());
addAll("items", lst);
($['list'] as IronList).scrollToIndex(items.length-1);
}
}
html:
<dom-module id="main-app">
<template>
<style>
#list {
height: 100px;
width: 100px;
}
</style>
<iron-list items="{{items}}" id="list">
<template>
<div>{{item}}</div>
</template>
</iron-list>
<paper-button raised on-tap="onAdd">add</paper-button>
</template>
</dom-module>
When I click on the button for the first time it gives me:
What's wrong here? How to update the view properly?
(package versions: polymer 1.0.0-rc.6, polymer_elements 1.0.0-rc.3, polymer_interop 1.0.0-rc.5)

Moving elements by dragging in Dart

I am trying to move an element using drag and drop. I want to be able to drag and element to a different location, and when I drop it, the element moves to the dropped location. Super basic, and nothing fancy. This is what I have so far:
html:
<input type='button' id='drag' class='draggable' value='drag me' draggable='true'>
Dart code:
Element drag = querySelector('.draggable');
drag.onDragEnd.listen((MouseEvent e) {
drag.style.left = '${e.client.x}px';
drag.style.top = '${e.client.y}px';
});
This doesn't quite do what I want it to do. The element is slightly off from where I drop it. I see examples in javascript with appendChild, clone(), parentNode, but none of the examples that I have seen can be reproduced in Dart. What is the best way to accomplish this? I don't want to use the DND package, since I am really trying to personally understand the concepts better.
index.html
<!doctype html>
<html>
<head>
<style>
#dropzone {
position: absolute;
top: 50px;
left: 50px;
width: 300px;
height: 150px;
border: solid 1px lightgreen;
}
#dropzone.droptarget {
background-color: lime;
}
</style>
</head>
<body>
<input type='button' id='drag' class='draggable' value='drag me'
draggable='true'>
<div id="dropzone"></div>
<script type="application/dart" src="index.dart"></script>
<script src="packages/browser/dart.js"></script>
</body>
</html>
index.dart
library _template.web;
import 'dart:html' as dom;
import 'dart:convert' show JSON;
main() async {
dom.Element drag = dom.querySelector('.draggable');
drag.onDragStart.listen((event) {
final startPos = (event.target as dom.Element).getBoundingClientRect();
final data = JSON.encode({
'id': (event.target as dom.Element).id,
'x': event.client.x - startPos.left,
'y': event.client.y - startPos.top
});
event.dataTransfer.setData('text', data);
});
dom.Element dropTarget = dom.querySelector('#dropzone');
dropTarget.onDragOver.listen((event) {
event.preventDefault();
dropTarget.classes.add('droptarget');
});
dropTarget.onDragLeave.listen((event) {
event.preventDefault();
dropTarget.classes.remove('droptarget');
});
dropTarget.onDrop.listen((event) {
event.preventDefault();
final data = JSON.decode(event.dataTransfer.getData('text'));
final drag = dom.document.getElementById(data['id']);
event.target.append(drag);
drag.style
..position = 'absolute'
..left = '${event.offset.x - data['x']}px'
..top = '${event.offset.y - data['y']}px';
dropTarget.classes.remove('droptarget');
});
}
The answer above is correct, and I didn't want to edit it for that reason. However, I wanted to also offer another answer that I derived from the above. It is a lot more basic compared to the above, and so easier to follow the basic concepts for beginners. As mentioned below, I don't think you can move elements unless they are within a droppable area.
index.html:
<!DOCTYPE html>
<html>
<head>
<style>
#dropzone {
position: absolute;
top: 100px;
left: 50px;
width: 300px;
height: 150px;
border: solid 1px;
color: lightgreen;
}</style>
</head>
<body>
<div id="dropzone">
<input type='button' id='drag' class='draggable' value='drag me'
draggable='true'>
</div>
<script type="application/dart" src="main.dart"></script>
</body>
</html>
main.dart:
import 'dart:html';
main() {
Element drag = querySelector('.draggable');
Element drop = querySelector('#dropzone');
drag.onDragStart.listen((MouseEvent e) {
var startPos = (e.target as Element).getBoundingClientRect();
String xPos = "${e.client.x - startPos.left}";
String yPos = "${e.client.y - startPos.top}";
e.dataTransfer.setData('x', xPos);
e.dataTransfer.setData('y', yPos);
});
drop.onDragOver.listen((MouseEvent e) {
e.preventDefault();
});
drop.onDrop.listen((MouseEvent e) {
e.stopPropagation();
String xPos = e.dataTransfer.getData('x');
String yPos = e.dataTransfer.getData('y');
int x = num.parse(xPos);
int y = num.parse(yPos);
drag.style.position = 'absolute';
drag.style
..left = '${e.offset.x - x}px'
..top = '${e.offset.y - y}px';
});
}
I had the same question and since the answers above did not meet my needs in:
Element drag-gable by itself(No drop zone)
Reusable
For a wrapper based solution, this package could be the answer:https://pub.dartlang.org/packages/dnd
Custom element based approach(Currently cursor styling is not working):
main(){
document.registerElement('draggable-element',
DraggableElement);
querySelector('body').append(new DraggableElement()..text='draggable');
}
class DraggableElement extends HtmlElement with Draggability{
DraggableElement.created():super.created(){
learn_custom_draggability();
}
factory DraggableElement(){
return new Element.tag('draggable-element');
}
}
class Draggability{
bool __custom_mouseDown = false;
//The Coordinates of the mouse click
//relative to the left top of the
//element.
Point<int> __custom_relative_mouse_position;
void learn_custom_draggability(){
if(this is! HtmlElement ){
throw ("Draggability mixin "
"is not compatible with"
' non-HtmlElement.');
}
var self = (this as HtmlElement);
self.onMouseDown.listen(mouseDownEventHandler);
self.onMouseUp.listen(mouseUpEventHandler);
//styling
self.style.position = 'absolute';
window.onMouseMove
.listen(mouseMoveEventHandler);
}
void mouseMoveEventHandler(MouseEvent e){
if(!__custom_mouseDown) return;
int xoffset = __custom_relative_mouse_position.x,
yoffset = __custom_relative_mouse_position.y;
var self = (this as HtmlElement);
int x = e.client.x-xoffset,
y = e.client.y-yoffset;
print(x);
if(y == 0) return;
self.style
..top = y.toString() +'px'
..left = x.toString()+'px';
}
void mouseDownEventHandler(MouseEvent e){
print('mouse down');
__custom_mouseDown = true;
var self = (this as HtmlElement);
self.style.cursor = 'grabbing';
__custom_relative_mouse_position =
e.offset;
}
void mouseUpEventHandler(MouseEvent e){
print('mouse up');
__custom_mouseDown = false;
var self = (this as HtmlElement);
self.style.cursor = 'default';
}
}
Edit:
Yay, Thank you Günter Zöchbauer for informing me about reflectable. It's so small and compiles fast.
A little off the topic but posting since mixins and the below pattern goes hand in hand.
import 'package:reflectable/reflectable.dart';
class Reflector extends Reflectable{
const Reflector():
super(
instanceInvokeCapability,
declarationsCapability
);
}
const reflector = const Reflector();
#reflector
class CustomBase extends HtmlElement{
CustomBase.created():super.created(){
learn();
}
learn(){
InstanceMirror selfMirror = reflector.reflect(this);
var methods = selfMirror.type.instanceMembers;
RegExp pattern = new RegExp('^learn_custom_.*bility\$');
for(var k in methods.keys){
if(pattern.firstMatch(k.toString()) != null){
selfMirror.invoke(k,[]);
}
}
}
}
Include: "reflectable: ^0.5.0" under dependencies and "- reflectable: entry_points: web/index.dart" etc under transformers
in the pubspec.yaml and extend a custom class like the above instead of a HtmlElement and selfMirror.invoke magically calls your initializer as long as their names match the given pattern. Useful when your classes have a quite few abilities.

Error | Removing disallowed attribute on SelectElement

I am trying to use a button to add a new row in an html table when the button is clicked, and another button to remove the row/s with its checkbox checked. I am experience two problems:
Whenever the add-button is clicked I get Removing disallowed attribute SELECT on-change="{{ onChangeTypeFired }}"
If I select two checkboxes and clicked the delete row button it throws an indexIndexSizeError: Index or size was negative, or greater than the allowed value. The index provided (2) is greater than the number of rows in the table (2).
The files are shown below
.html
<!DOCTYPE html>
<link rel='import' href='../../../../packages/polymer/polymer.html' >
<link rel='import' href='../../../../packages/paper_elements/paper_checkbox.html' >
<link rel='import' href='../../../../packages/paper_elements/paper_button.html' >
<link rel='import' href='../../../../packages/core_elements/core_collapse.html' >
<link rel='import' href='../../../../packages/core_elements/communication_icons.html' >
<polymer-element name='phone-form'>
<template>
<div
class='parent-container'>
<paper-button id='add-btn' on-click='{{toggle}}'>
<core-icon id='toggle-btn-icon' icon=''></core-icon>
Phone
<core-icon id='validation-icon' class='margin-l5px' icon=''></core-icon>
</paper-button>
<table id='table' border='1' width='350px'>
<tbody><tr>
<td><input name='chk' type='checkbox'></td>
<td>
<select id='phoneType' class='width95percent'
selectedIndex='{{typeSelected}}'
on-change='{{onChangeTypeFired}}'>
<option template repeat='{{key in types.keys}}'
value='{{types[key]}}'>{{types[key]}}
</option>
</select>
</td>
</tr>
</tbody></table>
</div>
<div>
<paper-button raised id='add-row-btn' class='margin-8px'
on-click='{{addRow}}'>
Add Row
<core-icon id='add-row-btn-icon' icon='check-all'></core-icon>
</paper-button>
<paper-button raised id='delete-row-btn' class='margin-8px'
on-click='{{deleteRow}}'>
Delete Selected Row(s)
<core-icon id='delete-row-btn-icon' icon='check'></core-icon>
</paper-button>
</div>
</div>
</template>
<script type='application/dart' src='phone_form.dart'></script>
</polymer-element>
.dart
import 'package:polymer/polymer.dart';
import 'dart:html' as dom;
import 'package:paper_elements/paper_button.dart' show PaperButton;
import 'package:core_elements/core_collapse.dart';
//import 'package:epimss_shared/epimss_shared.dart';
//import 'package:epimss_shared/epimss_shared_client.dart' hide DataEvent;
#CustomTag('phone-form')
class PhoneForm extends PolymerElement {
#observable String errorMsg;
String topic;
PaperButton addBtn;
int typeSelected = 0;
CoreCollapse coreCollapse;
#observable
Map<String, String> types = <String, String> {
'': '',
'Car': 'Car',
'Direct': 'Direct',
'Fax': 'Fax',
'Home': 'Home',
'Mobile': 'Mobile',
'Video': 'Video',
'Work': 'Work'
};
PhoneForm.created() : super.created();
void toggleCoreCollapse() {
coreCollapse.toggle();
}
void onSelectTypeFired()
{
}
void onChangeTypeFired( dom.Event e, var detail, dom.SelectElement target)
{
print(target.value);
}
void addRow()
{
var table = $['table'];
var rowCount = table.rows.length;
var row = table.insertRow(rowCount);
var colCount = table.rows[0].cells.length;
for(var i = 0; i<colCount; i++ )
{
var newcell = row.insertCell(i);
newcell.innerHtml = table.rows[0].cells[i].innerHtml;
switch(newcell.childNodes[0].runtimeType.toString())
{
case 'text':
newcell.childNodes[0].value = '';
break;
case 'checkbox':
newcell.childNodes[0].checked = false;
break;
case 'select':
newcell.childNodes[0].selectedIndex = 0;
break;
}
}
}
void deleteRow()
{
var rowsToDelete = [];
try{
var table = $['table'];
var rowCount = table.rows.length;
for(var i = 0; i < rowCount; i++)
{
var row = table.rows[i];
var chkbox = row.cells[0].childNodes[0];
if(chkbox != null && chkbox.checked)
{
if(rowCount <= 1)
{
print('Cannot delete all the rows.');
break;
}
else
{
rowsToDelete.add(i);
}
}
}
rowsToDelete.forEach( (row)
{
table.deleteRow(row);
});
}
catch(e)
{ print(e); }
}
#override
void attached() {
super.attached();
topic = this.dataset['topic'];
coreCollapse = $['core-collapse'];
addBtn = $['add-btn'];
}
}
Thanks for your help.
It is probably caused by this line
newcell.innerHtml = table.rows[0].cells[i].innerHtml;
You need to specify which elements and tags are allowed to be added to the DOM from a string.
see https://stackoverflow.com/a/27334820/217408 for more details.
That doesn't apply when you generate the elements imperatively like new DivElement().
In your example it would probably easier and better to do something like
newcell.children.clear();
newcell.children.addAll(table.rows[0].cells[i].children.map((c) =>
c.clone(true)));
Caution: I'm not sure about how the code should exactly look like but I think you get the idea. If you can't work it out add a comment and I'll have a closer look.

core-style and classes does not seem to work

I am trying to understand core-style. I noticed that in all examples I have seen so far, only elements eg Button are referenced in a core-style - there is no class reference (eg .blue). I tried to place a class reference in the core-style but it does not render. Please see the example below
.html
<link href='../../../../packages/polymer/polymer.html' rel='import' >
<link href='../../../../packages/core_elements/core_style.html' rel='import' >
<polymer-element name='blue-theme'>
<template>
<core-style id='blue-theme'>
:host {
background-color: red;
.lb-container1 {
background-color: {{lb50}};
padding-top: 5px;
padding-bottom: 5px;
width: {{width}}
}
}
</core-style>
</template>
<script type='application/dart' src='blue_theme.dart'></script>
</polymer-element>
.dart
import 'package:polymer/polymer.dart';
import 'package:epimss_shared/epimss_shared_client.dart' hide DataEvent;
#CustomTag( 'blue-theme' )
class BlueTheme extends PolymerElement
{
String topic = '';
#observable String lb50 = LightBlue['50'];
#observable String lb100 = LightBlue['100'];
#observable String lb200 = LightBlue['200'];
BlueTheme.created() : super.created();
#published
String get width => readValue( #width );
set width(String value) => writeValue( #width, value );
#override
void attached()
{
super.attached();
topic = this.dataset['topic'];
}
}
The above code does not render.
Thanks
You only created a <core-style> producer (provides reusable styles). What you also need is a <core-style> consumer.
<core-style ref="blue-theme"></core-style>
I haven't used it myself but I think just adding this line should solve the problem. You can have the producer and consumer in different elements. That is rather the point of the element. Define a style once and reuse it just by referencing.
This tutorial looks good so far https://pascalprecht.github.io/2014/08/01/sharing-styles-across-web-components-with-polymer-and-core-style/

How to remove a child component with a delete button in the child itself

I have an email component (email-tag.html) that consist of a label, a select and a delete button element.
The email-tag.html component is hosted in its parent email-view-tag.html. email-view-tag contains an add-email-button that adds the email-tag element to the DOM each time it is clicked.
I need help in removing an added email-tag component when its delete-button is clicked. It is the compnoent that contains the delete-button that should be removed.
The two components are shown below:
email-tag.html
<!DOCTYPE html>
<polymer-element name='email-tag'>
<template>
<style>
.main-flex-container
{
display:flex;
flex-flow:row wrap;
align-content:flex-start;
}
.col
{
display:flex;
flex-flow:column;
align-content:flex-start;
flex-grow:1;
}
</style>
<div id='email' class='main-flex-container'>
<section id='col1' class='col'>
<input id=emailTxt
type='text'
list='_emails'
value='{{webContact.homeEmail}}'>
<datalist id='_emails'>
<template repeat='{{email in emails}}'>
<option value='{{email}}'>{{email}}</option>
</template>
</datalist>
</section>
<section id='col2' class='col'>
<button id='delete-email-btn' type='button' on-click='{{deletePhone}}'>Delete</button>
</section>
</div>
</template>
<script type="application/dart">
import 'package:polymer/polymer.dart' show CustomTag, PolymerElement;
import 'dart:html' show Event, Node;
#CustomTag( 'email-tag' )
class EmailElement extends PolymerElement
{
//#observable
EmailElement.created() : super.created();
List<String> emails = [ '', 'Home', 'Personal', 'Private', 'Work', ];
void deletePhone( Event e, var detail, Node target)
{
//shadowRoot.querySelector('#new-phone' ).remove();
//print( 'Current row deleted' );
}
}
</script>
</polymer-element>
email-view-tag.html
<!DOCTYPE html>
<link rel="import" href="email-tag.html">
<polymer-element name='email-view-tag'>
<template>
<style>
.main-flex-container
{
display:flex;
flex-flow:row wrap;
align-content:flex-start;
}
.col
{
display:flex;
flex-flow:column;
align-content:flex-start;
flex-grow:1;
}
</style>
<div id='email-view' class='main-flex-container'>
<section id='row0' >
<button id='add-email-btn' type='button' on-click='{{addPhone}}'>Add Phone</button>
</section >
<section id='rows' class='col'>
<!-- <epimss-phone-header-tag id='col1' class='col'></epimss-phone-header-tag> -->
</section>
</div>
</template>
<script type="application/dart">
import 'package:polymer/polymer.dart' show CustomTag, PolymerElement;
import 'dart:html' show Event, Node, Element;
#CustomTag( 'email-view-tag' )
class EmailViewElement extends PolymerElement
{
//#observable
EmailViewElement.created() : super.created();
void addPhone( Event e, var detail, Node target )
{
$[ 'rows' ].children.add( new Element.tag( 'email-tag' ) );
}
#override
void attached() {
super.attached();
$[ 'add-email-btn' ].click();
}
}
</script>
</polymer-element>
The application does execute normally and clicking the add button does add the email component. The delete button does not work - it is here I am asking for help.
Thanks
The child component, <email-tag> should not be in the business of deleting itself. Instead, it should delegate that responsibility to the the parent component, email-view-tag, by dispatching a custom event.
Here is the code for dispatching a custom event from deletePhone:
void deletePhone( Event e, var detail, Node target){
dispatchEvent(new CustomEvent('notneeded'));
}
Then, in the parent, <custom-view>, change your code for adding <email-tag>s like so:
void addPhone( Event e, var detail, Node target ) {
$['rows'].children.add( new Element.tag('email-tag'));
$['rows'].on["notneeded"].listen((Event e) {
(e.target as Element).remove();
});
}
Also, I would change the name of deletePhone, since the method no longer deletes the record but merely informs the parent that it is not needed. Call it 'notNeeded' or something similar.
EDIT
#ShailenTuli is right about encapsulation should not be broken.
But also JS Polymer elements access the parent in their layout elements because it's still convenient in some scenarios.
This works now in PolymerDart too.
(this.parentNode as ShadowRoot).host
ORIGINAL
You can fire an event and make the email-view-tag listen to this tag and the event handler can remove the event target from it's childs.
I had a similar question a while ago:
How to access parent model from polymer component
This was actually the question I wanted refer to
How can I access the host of a custom element
but the first one may be of some use too.
PolymerJS FAQ - When is the best time to access an element’s parent node?
attached() currently still named enteredView() in Dart, but will be renamed probably soon.

Resources