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.
Related
I am trying to remove and add a template content using the a paper-toggle-button. However, I am not able to see the contents of the template even though the toggle is occurring.
The code used is shown below:
.dart
PolymerRegister( 'test-if-form' )
class TestIfForm extends PolymerElement {
TestIfForm.created( ) : super.created( );
#Property( observer: 'togglerChanged' )
//#property
bool toggler = false;
#reflectable
void toggleNormalChangedEvent( event, [_] ) {
toggler = !toggler;
print( 'toggler $toggler' );
}
#reflectable
void togglerChanged( newValue, oldValue ) {
print( 'new value $newValue' );
if ( newValue ) {
toggler = !toggler;
}
}
}
.html
<dom-module id = "test-if-form">
<template>
<div
class = "layout horizontal center-justified"
id = "normal-changed-toggler-container">
<span class = "margin-rt-5px">Normal</span>
<paper-toggle-button
required
on-change = "toggleNormalChangedEvent"
id = "togglerBtn">Changed
</paper-toggle-button>
<div>
<hr>
</div>
</div>
<br>
<template is = "dom-if"
if = "{{toggler}}"
restamp>
<div>I am now visible</div>
</template>
</template>
</dom-module>
The contents of the nested div element is never visible. It does not seem that the if is being called?
Thanks for your help.
toggler = !toggler;
should be
set('toggler', !toggler);
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.
I created a dialog and it renders. However, it can be hidden by its parent if the window is small. I tried the layered attribute on PaperDialog layered attribute it does not seem to help.
others.html
<!DOCTYPE html>
<link href='../../../../packages/polymer/polymer.html' rel='import' >
<link href='../../../../packages/bwu_datagrid/bwu_datagrid.html' rel='import' >
<link href='../../../../packages/paper_elements/paper_input.html' rel='import' >
<polymer-element name='others-form'>
<template>
<paper-input floatinglabel multiline
id = 'description'>
</paper-input>
</template>
<script type='application/dart' src='others_form.dart'></script>
</polymer-element>
others.dart
import 'package:polymer/polymer.dart';
import 'package:vacuum_persistent/persistent.dart' show PersistentMap;
import 'package:clean_data/clean_data.dart' show DataMap;
import 'package:epimss_shared/shared_event.dart' show eventBus,
PersistentMapEvent;
#CustomTag( 'others-form' )
class OthersForm extends PolymerElement
{
#observable String z = '3';
#observable DataMap<String, dynamic> selections;
String errorMsg;
OthersForm.created() : super.created();
#override
void attached()
{
super.attached();
selections = new DataMap<String, dynamic>.from({});
eventBus.on( PersistentMapEvent, ( event )
{
switch( event.topic )
{
case 'shared|description-form --> lab|routine-culture-rqst':
selections[ 'other' ] = event.pmap[ 'description' ];
break;
}
});
/*
selections.onChange.listen( (changeset)
{
if ((selections.length == 1 && !selections.containsKey( 'other' )) ||
selections.containsKey( 'other' ))
{
eventBus.signal(
new PersistentMapEvent (
new PersistentMap<String, String>.fromMap( selections ))
..topic = this.dataset[ 'topic' ]);
}
});
*
*/
}
}
ssss_form.html
<!DOCTYPE html>
<link href='../../../../packages/polymer/polymer.html' rel='import'>
<link href='../../../../packages/paper_elements/paper_icon_button.html' rel='import' >
<link href='../../../../packages/paper_elements/paper_shadow.html' rel='import'>
<link href='../../../../packages/paper_elements/paper_button.html' rel='import'>
<link href='../../../../packages/paper_elements/paper_dialog_transition.html' rel='import'>
<link href='../../../../packages/paper_elements/paper_dialog.html' rel='import'>
<link href='../../../../packages/html_components/input/select_item.html' rel='import'>
<link href='../../../../packages/html_components/input/select_checkbox_menu.html' rel='import'>
<link href='others_form.html' rel='import'>
<polymer-element name='ssss-form'>
<template>
<div layout horizontal>
<div layout vertical
id='specimen-div'
class='card'>
<h-select-checkbox-menu
label='Specimen'
on-selectionchanged='{{onSelectionChangedFiredSpecimen}}'>
<template repeat='{{key in specimens.keys}}'>
<h-select-item
label='{{key}}'
value='{{specimens[key]}}'>
</h-select-item>
</template>
</h-select-checkbox-menu>
</div>
<paper-shadow z='{{z}}'></paper-shadow>
</div>
<paper-dialog
id='other-dialog'
heading='Other'
transition='paper-dialog-transition-center'>
<others-form> </others-form>
<paper-button dismissive
label='More Info...' >
</paper-button>
<paper-button affirmative
label='Cancel'>
</paper-button>
<paper-button affirmative autofocus
label='Accept'>
</paper-button>
</paper-dialog>
</template>
<script type='application/dart' src='ssss_form.dart'></script>
</polymer-element>
ssss_form.dart
import '
package:polymer/polymer.dart';
import 'dart:html';
import 'package:paper_elements/paper_dialog.dart';
import 'package:html_components/html_components.dart' show SelectCheckboxMenuComponent;
import 'package:vacuum_persistent/persistent.dart' show PersistentMap;
import 'package:clean_data/clean_data.dart';
import 'package:epimss_shared/shared_transformers.dart' show CheckboxMenuItemsConverter;
#CustomTag( 'ssss-form' )
class SsssForm extends PolymerElement with CheckboxMenuItemsConverter
{
DataSet<DataMap> selections;
DataMap<String, dynamic> specimenSelections;
PersistentMap<String, Map> pmap;
#observable
Map<String, dynamic> specimens = toObservable(
{
'CSF': 'CSF',
'Other': 'Other'
});
#observable String specimen = '';
#observable String z = '3';
var sideForm;
SsssForm.created() : super.created();
void onSelectionChangedFiredSpecimen( Event event, var detail,
SelectCheckboxMenuComponent target)
{
var list = getItemModels( target.selectedItems );
specimenSelections.clear();
list.forEach( (item)
{
specimenSelections[item.label] = item.selected;
/// Checks if [item] selected is equal to 'Other' and if so creates a
/// a dialogue to make the selection
if ( item.label == 'Other')
{ toggleDialog( 'paper-dialog-transition-center' ); }
});
}
toggleDialog( transition ) => $['other-dialog'].toggle();
#override
void attached()
{
super.attached();
specimenSelections = new DataMap<String, dynamic>.from({});
selections = new DataSet<DataMap>.from( [specimenSelections] );
}
}
others.html is the contents of the dialog - the latter is hosted in the ssss_form.html file. When the 'Others' checkbox in the 'Specimen' dropdown is clicked, this triggers the dialog.
I also get the following when the application is run
Attributes on ssss-form were data bound prior to Polymer upgrading the element. This may result in incorrect binding types. (:1)
The "label" property is deprecated. (http://localhost:8080/polymer_bug.html:7467)
The "label" property is deprecated. (http://localhost:8080/polymer_bug.html:7467)
The "label" property is deprecated. (http://localhost:8080/polymer_bug.html:7467)
The "label" property is deprecated. (http://localhost:8080/polymer_bug.html:7467)
The "label" property is deprecated. (http://localhost:8080/polymer_bug.html:7467)
The "label" property is deprecated. (http://localhost:8080/polymer_bug.html:7467)
Please see attached graphic.
Thanks
I think you just need to set a higher value for the zIndex CSS property than every other HTML element on that page.
I have a problem with the performance of binding data to a table.
I started to use core-menu with paper-items.
paper-items are supposed to display a nice animation when clicked.
It works fine, but if I try to bind some data in the selected index changed handler, the animation frame rate is awful.
Is there anything I can do to improve the frame rate on paper-item?
Here is an example (binds 250 cells in a table).
clickcounter.html
<link rel="import" href="packages/polymer/polymer.html">
<link rel="import" href="packages/core_elements/core_menu.html">
<link rel="import" href="packages/core_elements/core_icons.html">
<link rel="import" href="packages/paper_elements/paper_item.html">
<polymer-element name="click-counter">
<template>
<style>
core-menu {
color: #01579b;
margin: 10px 0 0 0;
}
core-menu > paper-item {
transition: all 300ms ease-in-out;
}
core-menu > paper-item.core-selected {
background: #e1f5fe;
}
</style>
<div>
<core-menu selected="{{selIndex}}">
<paper-item icon="settings" label="Item 0">
<a ></a>
</paper-item>
<paper-item icon="settings" label="Item 1">
<a ></a>
</paper-item>
</core-menu>
</div>
<table>
<thead>
<tr>
<th>C1</th>
<th>C2</th>
<th>C3</th>
<th>C4</th>
<th>C5</th>
</tr>
</thead>
<tbody>
<tr template repeat="{{row in dataRows}}" >
<td>{{row.s1}}</td>
<td>{{row.s2)}}</td>
<td>{{row.s3}}</td>
<td>{{row.s4}}</td>
<td>{{row.s5}}</td>
</tr>
</tbody>
</table>
</template>
<script type="application/dart" src="clickcounter.dart"></script>
</polymer-element>
clickcounter.dart
import 'package:polymer/polymer.dart';
/**
* A Polymer click counter element.
*/
class TableItem {
String s1;
String s2;
String s3;
String s4;
String s5;
TableItem(this.s1, this.s2, this.s3, this.s4, this.s5);
}
#CustomTag('click-counter')
class ClickCounter extends PolymerElement {
#published var selIndex = 0;
#observable List<TableItem> dataRows = [];
ClickCounter.created() : super.created() {
makeData(selIndex);
}
selIndexChanged(var oldVal, var newVal) {
makeData(newVal);
}
void makeData(int newVal) {
List newList = new List();
for (int r = 0; r< 50; r++) {
int i = newVal * r;
String s = "${i}";
newList.add(new TableItem("${i}-1", "${i}-2", "${i}-3", "${i}-4", "${i}-5"));
}
dataRows = toObservable(newList);
}
}
Answering my question after Gunter's tips.
1 reassiging entire observable list
(as presented in question) - definitely the slowest
2 reassiging items in an observable list
improvement, but still unacceptable.
#observable List dataRows = toObservable([]);
void makeData(int newVal) {
List newList = new List();
for (int r = 0; r< 250; r++) {
int i = newVal * r;
String s = "${i}";
newList.add(new TableItem("${i}-1", "${i}-2", "${i}-3", "${i}-4", "${i}-5"));
}
dataRows.clear();
dataRows.addAll(newList);
}
3 reassiging items in an observable list with one time binding
much better, almost acceptable
Code changes as above, with one time binding:
<tr template repeat="{{row in dataRows}}" >
<td>[[row.s1]]</td>
<td>[[row.s2]]</td>
<td>[[row.s3]]</td>
<td>[[row.s4]]</td>
<td>[[row.s5]]</td>
</tr>
4 No list reassingment, observable elements
Definitely the fastest, works fine even with 1000 rows :)
class TableItemObs extends Object with Observable {
#observable String s1;
#observable String s2;
#observable String s3;
#observable String s4;
#observable String s5;
TableItemObs(this.s1, this.s2, this.s3, this.s4, this.s5);
}
selIndexChanged(var oldVal, var newVal) {
changeData(newVal);
}
void changeData(int newVal) {
for (int r = 0; r< 250; r++) {
int i = newVal * r;
dataRows[r].s1 = "${i}-1";
dataRows[r].s2 = "${i}-2";
dataRows[r].s3 = "${i}-3";
dataRows[r].s4 = "${i}-4";
dataRows[r].s5 = "${i}-5";
}
}
keep
<td>{{row.s1}}</td>
Update:
I tested another option:
3a reassiging items in an observable list with one time binding, with batches
A variation of option 3, but adding after clearing the list new items are added in batches.
Each function adds a part of the items.
Faster than option 3, but still below option 4.
selIndexChanged(var oldVal, var newVal) {
new Future(() => makeData1(newVal))
.then((_) => makeData2(newVal))
.then((_) => makeData3(newVal))
.then((_) => makeData4(newVal))
.then((_) => makeData5(newVal));
}
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