How to overwrite part of <template> element in custom element inheritance? - shadow-dom

I have parent Custom Element, BasicSwitcher, which has its shadow dom html template:
const template_basic_switcher = document.createElement('template');
template_basic_switcher.innerHTML = `
<style>
#import url("https://use.fontawesome.com/releases/v5.6.3/css/all.css")
</style>
<div id="controls-container">
<span>ON</span>
<span>OFF</span>
</div>
`;
Now I have another Custom Element, ModeSwitcher, inheriting from BasicSwitcher. It has completely different switches.
Is there a way to overwrite just the controls-container part of the template, while still utilizing other part? the element doesn't seem to support any kind of inheritance.

1) Create class for you Base Custom Element with a defaut render() method.
class BasicSwitcher extends HTMLElement {
render() {
this.shadowRoot.innerHTML = baseTemplate
}
}
2) Extend the above class for your inherited Custum Element, and redefine the render() method and use the reference to the new template.
class ModeSwicher extends BasicSwitch () {
render() {
this.shadowRoot.innerHTML = otherTemplate
}
}
Below is a working snippet:
class BasicSwitcher extends HTMLElement {
constructor() {
super()
this.attachShadow( { mode: 'open' } )
this.render()
}
render() {
this.shadowRoot.innerHTML = BS.innerHTML
}
}
customElements.define( 'basic-switch', BasicSwitcher )
class ModeSwitcher extends BasicSwitcher {
render() {
this.shadowRoot.innerHTML = MS.innerHTML
}
}
customElements.define( 'mode-switch', ModeSwitcher )
<template id=BS>
<style> :host { display: inline-block ; border: 1px solid gray } </style>
<span>Basic Switch</span>
</template>
<template id=MS>
<style> :host { display: inline-block ; border: 1px dotted blue ; color: red } </style>
<span>Mode Switch</span>
</template>
<basic-switch></basic-switch>
<mode-switch></mode-switch>

Related

how to style the vaadin-grid header?

i am trying to apply styles to the header vaadin grid component but nothing comes out what could be the problem
I have components in page
<script-loader-grid></script-loader-grid>
and inside it component I try implement styles
<style>
${styles}
:host {
height: 150px;
}
[part="header-cell"] {
background: red;
}
</style>
<vaadin-grid id="test" .items ="${test_data}">
<vaadin-grid-column path="company" header="Company" ы></vaadin-grid-column>
<vaadin-grid-column path="script" header="Script"></vaadin-grid-column>
<vaadin-grid-column path="stage" header="Stage"></vaadin-grid-column>
<vaadin-grid-column path="atributes" header="Atributes"></vaadin-grid-column>
</vaadin-grid>
`
I resolve my problem this way, just using registerStyles
import { registerStyles } from '#vaadin/vaadin-themable-mixin/register-styles.js'
import {css} from '#vaadin/vaadin-themable-mixin/register-styles'
class scriptLoaderGrid extends connect(store)(LitElement) {
static get properties() {
return {
opened: {type: Boolean},
}
}
constructor() {
super()
registerStyles('vaadin-grid', css`
:host [part~="header-cell"] ::slotted(vaadin-grid-cell-content), [part~="footer-cell"] ::slotted(vaadin-grid-cell-content), [part~="reorder-ghost"] {
background:red
}
`)
}
......
<vaadin-grid .items ="${test_data}" theme="special-field">
<vaadin-grid-column path="company" header="Company"></vaadin-grid-column>
.....
</vaadin-grid>

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.

How to change the color of an polymer_element icon using Dart and polymer 1.0.0-rc1

I have the following files:
.html
<dom-module id = "required-icon">
<template>
<style is="custom-style">
.big {
--iron-icon-height: 12px;
--iron-icon-width: 12px;
margin-right :3px;
}
</style>
<iron-icon
class = "big"
id = "z"
icon = "star"></iron-icon>
<paper-tooltip
for = "z"
position = "right"
animation-delay = "0">
<b>Required</b>
</paper-tooltip>
</template>
</dom-module>
.dart
#PolymerRegister( "required-icon" )
class RequiredIcon extends PolymerElement {
RequiredIcon.created( ) : super.created( );
ready()
{
}
}
How can I change the color of the 'star' icon in the statement | icon = 'star' both using the tag and from the .dart class.
Thanks
.big {
color: red;
fill: red; /* for SVG icons */
}
$$('iron-icon.big').style..color = 'red'..fill = 'red';
See also
- https://github.com/bwu-dart/bwu_fontawesome_iconset_svg/blob/master/example/example01.html#L20
- https://github.com/PolymerElements/iron-iconset/issues/11

In Polymer.js children of a template have a reference to the template, how can this be done in Polymer.dart

When I have a reference to an element that was produced by a <template>, in Polymer.js such elements have an attribute templateInstance that provides a references to its template like it's used here:
https://github.com/PolymerLabs/polymer-selector/blob/master/polymer-selector.html#L286
Polymer >= 1.0.0
#reflectable
void someClickHandler(dom.Event event, [_]) {
// for native events (like on-click)
var model = new DomRepeatModel.fromEvent(event);
// or for custom events (like on-tap, works also for native events)
var model = new DomRepeatModel.fromEvent(convertToJs(event));
var value = model.jsElement['items'];
// or
var value = model.jsElement[$['mylist'].attributes['as']];
// if you used the `as="somename"`
// in your <core-list> or <template is="dom-repeat">
}
There is an open issue related to custom events: https://github.com/dart-lang/polymer-dart/issues/624
Polymer <= 0.16.0
EDIT
The example below needs only this 3 lines, the other code is just for demonstration purposes
import 'package:template_binding/template_binding.dart' as tb;
tb.TemplateInstance ti = tb.nodeBind(e.target).templateInstance;
var value = ti.model.value as Inner;
EDIT END
This functionality was added recently (see https://code.google.com/p/dart/issues/detail?id=17462)
I created an example to test how it works:
index.html
<!DOCTYPE html>
<html>
<head>
<title>nested-repeat</title>
<!-- <script src="packages/web_components/platform.js"></script>
not necessary anymore with Polymer >= 0.14.0 -->
<script src="packages/web_components/dart_support.js"></script>
<link rel="import" href="nested_templates.html">
</head>
<body>
<nested-templates></nested-templates>
<script type="application/dart">export 'package:polymer/init.dart';</script>
</body>
</html>
nested_templates.html
<link rel="import" href="packages/polymer/polymer.html">
<polymer-element name="nested-templates">
<template>
<style>
:host { display: block; height: 100%; }
ul { margin: 0; padding: 0; }
li { font-size: 0.85rem; padding-left: 0.75rem; }
li:hover { background: lightgrey; cursor: pointer; }
li.selected { color: red; }
</style>
<div>
<template repeat="{{o in outer}}">
<strong>{{o.name}}</strong>
<ul>
<template repeat="{{i in o.inner}}">
<li id="{{i.name}}" on-click="{{innerClickHandler}}" template-value='{{i}}'>{{i.name}}</li>
</template>
</ul>
</template>
</div>
</template>
<script type="application/dart" src="nested_templates.dart"></script>
</polymer-element>
nested_templates.dart
import 'dart:html' as dom;
import 'package:polymer/polymer.dart';
import 'package:template_binding/template_binding.dart' as tb;
#CustomTag('nested-templates')
class NestedTemplates extends PolymerElement {
NestedTemplates.created() : super.created();
#observable List<Outer> outer = toObservable([new Outer('o1', toObservable(
[new Inner('a'), new Inner('b')])), new Outer('o2', toObservable([new Inner(
'c'), new Inner('d')]))], deep: true);
void innerClickHandler(dom.Event e) {
shadowRoot.querySelectorAll('li.selected').forEach((e) => (e as
dom.HtmlElement).classes.remove('selected'));
(e.target as dom.HtmlElement).classes.add('selected');
tb.TemplateInstance ti = tb.nodeBind(e.target).templateInstance; // get access to the TemplateInstance of the element
// TemplateInstance provides access to the model and the actual value
var value = ti.model.value as Inner;
print('name: ${value.name}'); // works
print('equals: ${value == (e.target as dom.HtmlElement).attributes['template-value']}'); // prints "false"
print(
'${(e.target as dom.HtmlElement).attributes['template-value']}'); // prints "Instance of 'Inner'"
// shows that the attribute only has the result of 'toString()' but not the actual value of type 'Inner'
print(
'${(e.target as dom.HtmlElement).attributes['template-value'].runtimeType}'); // prints "String"
}
}
class Inner extends Observable {
#observable String name;
Inner(this.name);
}
class Outer extends Observable {
#observable String name;
List<Inner> inner;
Outer(this.name, this.inner);
}

How to bind repeating nested templates to complex models

Eventually I want to bind templates to a tree model, but to understand how binding works beyond object properties (simple or chained), lists, and maps, I've created a two-level set of repeating templates bound to a corresponding Dart model. Each item in the outer list contains an inner list.
o1
a
b
o2
c
d
When I select an inner item (for example 'd'), the click handler highlights the item and appends an Inner object 'e' to the 'o2' Outer object's list. Inspecting the model in the debugger shows that 'e' has been added to the model, but it is not being added as an 'li' element to the HTML list. How do I revise my code so that the inner template detects the change?
HTML
<polymer-element name="nested-templates">
<template>
<style>
:host { display: block; height: 100%; }
ul { margin: 0; padding: 0; }
li { font-size: 0.85rem; padding-left: 0.75rem; }
li:hover { background: lightgrey; cursor: pointer; }
li.selected { color: red; }
</style>
<div>
<template repeat="{{o in outer}}">
<strong>{{o.name}}</strong>
<ul>
<template repeat="{{i in o.inner}}">
<li id="{{i.name}}" on-click="{{innerClickHandler}}">{{i.name}}</li>
</template>
</ul>
</template>
</div>
</template>
<script type="application/dart" src="nested_templates.dart"></script>
Dart
import 'dart:html';
import 'package:polymer/polymer.dart';
#CustomTag('nested-templates')
class NestedTemplates extends PolymerElement {
#observable List<Outer> outer = toObservable([
new Outer('o1', [ new Inner('a'), new Inner('b')]),
new Outer('o2', [ new Inner('c'), new Inner('d')])
], deep: true);
void innerClickHandler(Event e, Map detail, HtmlElement target) {
target.classes.add('selected');
outer[1].inner.add(new Inner('e'));
}
NestedTemplates.created() : super.created();
}
class Inner extends Observable {
String name;
Inner(this.name);
}
class Outer extends Observable {
String name;
List<Inner> inner;
Outer(this.name, this.inner);
}
pubspec.yaml
dependencies:
...
polymer: 0.10.1+1
polymer_expressions: 0.11.0
...
dependency_overrides:
polymer_expressions: '0.11.0'
You need toObservable() for your inner too
new Outer('o1', toObservable([ new Inner('a'), new Inner('b')])),

Resources