Set paper-tooltip from dart - dart

In html paper-tooltip works fine like
<div>
Text
<paper-tooltip>The tooltip</paper-tooltip>
</div>
But howto build apaper-tooltip from dart?
DivElement divText = new DivElement();
divText.text = 'Text from dart';
PaperTooltip pT = document.createElement('paper-tooltip');
//How to set the tooltip String "The tooltip" on pT?
divText.append(pT);
I have tried to set the underlying content of paper-tool, but don't succeed. Anyone with a tip on how this maybe could be done?

Not sure what is going on here. Seems like the creation from dart with document.createElement('paper-tooltip'); don't give a proper paper-tooltip element.
Found an ok workaround by wrapping the paper-tooltip with a custom element. The custom element can be then created with document.createElement('papertooltip-wrap');
Html
<dom-module id="papertooltip-wrap">
<template>
<paper-tooltip>[[tooltipText]]</paper-tooltip>
</template>
</dom-module>
Dart
#PolymerRegister('papertooltip-wrap')
class PapertooltipWrap extends PolymerElement {
#property
String tooltipText;
PapertooltipWrap.created() : super.created(){
}
void setAnchorElement(Element e, final String tipText){
set('tooltipText', tipText);
append(e);
}
}

Related

What are the different ways to access a static attribute in polymer-dart 1.0.rc

Given the following elements
.dart
#PolymerRegister('x-custom')
class XCustom extends PolymerElement {
XCustom.created() : super.created();
static const hostAttributes = const {
'string-attribute': 'Value',
'boolean-attribute': true,
'tabindex': 0,
};
}
that result dynamically in the
.html
<x-custom string-attribute="Value" boolean-attribute tabindex="0">
</x-custom>
What are the different ways to access the statically set hostAttributes in another .html and .dart file where x-custom is a child?
Imperatively if you get a reference of the element by one of the ways explained in What are the different ways to look up elements in Polymer 1.0 you can do
var xCustom = ...;
print(xCustom.attributes['string-attribute']);
xCustom.attributes['string-attribute'] = 'otherValue';
or declaratively
<dom-module id="some-element">
<template>
<x-custom
string-attribute="{{someStringProperty}}">
</x-custom>
</template>
</dom-module>
(not tested)

Dart Polymer: How to set a non-string field in custom at creation

I am writing a polymer app. I extend a polymer element as follows:
#CustomTag("custom-element")
class CustomElement extends PolymerElement {
#published List<String> years;
CustomElement.created() : super.created();
}
the corresponding html:
<link rel="import" href="../../../../packages/polymer/polymer.html">
<polymer-element name="custom-element" attributes="years">
<template>
Years listed are
<template repeat="{{ year in years }}">
<p> {{ year }} </p>
</template>
</template>
<script type="application/dart" src="custom_element.dart"></script>
</polymer-element>
and in a dart script referenced in the entry html file, after I have fetched some data from server I dynamically create such elements:
initPolymer().run((){
querySelect("#custom-elements").children.add(
new Element.tag("custom-element")
..id = "my_custom_element"
);
});
Given code works fine if years in CustomElement is set to some value, now I want to set it in my main script. If the property years was of type String then I would simply altered the last piece to
initPolymer().run((){
querySelect("#custom-elements").children.add(
new Element.tag("custom-element")
..id = "my_custom_element"
..setAttribute("years", "2010")
);
});
But the problem is that I need to set a whole List, not a single value. I can't figure out a way to do so. How would I go about this? Solutions and suggestions are very welcome.
EDIT:
So my main script looks as follow
void main(){
// in reality this list is fetched from the server
List<String> customYears = ['2010', '2011'];
initPolymer().run((){
querySelect("#custom-elements").children.add(
new Element.tag("custom-element")
..id = "my_custom_element"
// HERE I would like to set a public field *years*
// of the CustomElement
// the following does not work
// 1.
// ..setAttribute('years', customYears)
// Exception: Uncaught Error: type 'List' is not a subtype of type 'String' of 'value'.
// 2.
// ..setAttribute('years', toObservale(customYears))
// Exception: Uncaught Error: type 'ObservableList' is not a subtype of type 'String' of 'value'.
// 3.
// ..years = customYears
// ..years = toObservable( custom Years )
// NoSuchMethodError: method not found: 'years='
);
});
}
So the question is, how do I assign a value to a non-string member field of an instance of CustomElement outside the class itself? A straightforward assignment inside the class causes no problems, but this isn't what I seek for. I hope I explained myself more clearly now.
Basically #Ozan answers your actual question but there is an additional issue.
The element is not yet fully initialized when the code assigns the value. This is the reason Dart fails and tells you that HtmlElement doesn't have a setter years. After adding Polymer.onReady.then() Polymer is fully initialized and the assignment works.
Hint: If you cast the created element to its concrete type you don't get a warning in DartEditor.
import 'package:polymer/polymer.dart';
import 'dart:html';
// added import to be able to use the type
import 'custom_element.dart';
void main() {
// in reality this list is fetched from the server
List<String> customYears = ['2010', '2011'];
// Polymer => 0.16.0
initPolymer().then((zone) => zone.run(() {
Polymer.onReady.then((_) { // added - wait for onReady
querySelector("#custom-elements").children.add(
(new Element.tag('custom-element') as CustomElement)
..id = "my_custom_element"
..years = toObservable(customYears));
});
}));
}
it is more convenient to add a constructor to the custom element like
import 'package:polymer/polymer.dart';
import 'dart:html';
#CustomTag('custom-element')
class CustomElement extends PolymerElement {
factory CustomElement() {
final x = new Element.tag('custom-element');
return x;
}
CustomElement.created() : super.created();
#published List<String> years;
}
then you can create the element like
querySelector("#custom-elements").children.add(
new AppElement()
..id = "my_custom_element"
..years = toObservable(customYears));
see also
Instantiating polymer element via dart code
how to implement a main function in polymer apps
Try to simply assign the value to the field:
import 'package:observe/observe.dart';
initPolymer().run((){
querySelect("#custom-elements").children.add(
new Element.tag("custom-element")
..id = "my_custom_element"
..years = toObservable(["2010"])
);
});
Usually custom elements are used in a main polymer element and attributes like years are bound to its scope:
<link rel="import" href="../../../../packages/polymer/polymer.html">
<polymer-element name="main-element">
<template>
<custom-element years="{{years}}"></custom-element>
</template>
<script type="application/dart" src="main_element.dart"></script>
</polymer-element>
#CustomTag("main-element")
class MainElement extends PolymerElement {
#published List<String> years = toObservable(["2010"]);
MainElement.created() : super.created();
}

How to identify if my Polymer.dart Component is still the document.activeElement?

I have a CustomPassword component and want to provide a method isActive that allows you to retrieve if the component is still the active element on this website.
Example Code:
custom_password.html
<polymer-element name="custom-password">
<template>
<div>Other things here</div>
<input id="password-field" type="password" value="{{value}}">
</template>
<script type="application/dart" src="custom_password.dart"></script>
</polymer-element>
custom_password.dart
import 'package:polymer/polymer.dart';
#CustomTag('custom-password')
class CustomPassword extends PolymerElement {
#published
String value;
CustomPassword.created() : super.created() {
}
bool isActive() {
// TODO figure out if the CustomPassword element is still active.
return false;
}
}
With a help from the Polymer Group I was able to come up with a solution:
For browsers with shadow DOM support it works out of the box by comparing
the hashCode of the document.activeElement with the components hashCode.
For browsers without shadow DOM support the password field will be the
active element. The trick here is to wrap the document.activeElement in order to compare it to the wrapped passwordField.
Example:
custom_password.dart
import 'package:polymer/polymer.dart';
import 'dart:html';
import 'dart:js' as js;
#CustomTag('custom-password')
class CustomPassword extends PolymerElement {
#published
String value;
CustomPassword.created() : super.created() {
}
bool isActive() {
var passwordField = $['password-field'];
var activeElement = js.context.callMethod('wrap', [document.activeElement]);
// For Browsers with shadow DOM support the shadowRoot.host matches while
// for Browsers without shadow DOM support the password field match.
if (activeElement.hashCode != hashCode &&
activeElement.hashCode != passwordField.hashCode) {
return false;
} else {
return true;
}
}
}
bool isActive(event) {
return document.activeElement == this;
}
This only works in browsers with native Shadow DOM support.
I haven't tested it but this probably only works when your <custom-password> element is in the light DOM and not inside the <template> of another polymer element like <app-element> if you use one.
The current polyfill seems not to work correctly and you get the input element back instead of the Polymer element.
These issues contains information about how activeElement works with Polymer elements
- https://github.com/Polymer/ShadowDOM/issues/478
- https://code.google.com/p/dart/issues/detail?id=18982
- https://code.google.com/p/dart/issues/detail?id=20165
and also this paragraph
- http://www.w3.org/TR/shadow-dom/#active-element

Polymer custom element property not accessible in nested template

I have a custom element like below:
<polymer-element>
<template if="{{primaryLoaded}}">
<template repeat="{{pData in primaryData}}">
<p>{{pData.propertyOne}}</p>
<p>{{someClass.someOperation()}}</p>
<template if="{{secodaryLoaded}}">
<p>{{secondaryData.someProperty}}</p>
<p>{{someClass.someOperation()}}</p>
</template>
</template>
</template>
</polymer-element>
and a corresponding dart file:
class CustomElement extends PolymerElement with ObservableMixin
{
#observable bool primaryLoaded = false;
#observable bool secondaryLoaded = false;
#observable var primaryData;
#observable var secondaryData;
#observable var someClass;
void created()
{
primaryData = toObservable(new List<var>());
secondaryData = toObservable(new List<var>());
}
void inserted()
{
someClass = new SomeClass();
loadPrimaryData().then((pData) {
primaryData = pData;
primaryLoaded = true;
loadSecondaryData().then((sData) {
secondaryData = sData;
secondaryLoaded = true;
});
});
}
}
Everything works fine for the primaryData. Its properties get printed and the call to someOperation() on someClass prints its data correctly.
The problem is in the nested template. Nothing under the <template if="{{secondaryLoaded}}"> gets displayed. Even the call to someClass.someOperation() fails to display anything.
Is there a problem with scope here? It looks like the outer template can reference the properties defined in the dart file without problem but the nested template can't.
I read about setting variables as globals in the template by setting a custom binding delegate here. However, I can't set the bindingDelegate in my custom element as isTemplate returns false.
Is there any other way of setting a global variable in a custom template? Or am I going about this all wrong?
It's a bug. You can star this issue to be notified of changes:
https://code.google.com/p/dart/issues/detail?id=12742
The only thing you can do for now is to turn nested templates into other polymer elements or figure out a way so the template isn't nested.
In your case, instead of using if="primaryLoaded" you could just set primaryData = null and the template shoud not display anything until primaryData is set, assuming primaryData is #observable.

Dart Removing disallowed attribute after editor upgraded

I have encounter error "Removing disallowed attribute" after I upgraded my dart editor in SDK 0.7.3.1_r27487.
I have a custom tag which template contains boostarp attributes "data-target" and "data-toggle". It work under previous version but encounter error after upgraded.
Console
Removing disallowed attribute <A data-toggle="dropdown">
Removing disallowed attribute <BUTTON data-target=".navbar-collapse">
Removing disallowed attribute <BUTTON data-toggle="collapse">
.html Code
<element extends="div" name="x-navbar" constructor="Navbar">
<template>
..
<a name="top" href="#" class="dropdown-toggle" data-toggle="dropdown">Shop <b class="caret"></b></a>
..
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"></button>
</template>
<script type="application/dart" src="custom_ui.dart"></script>
</element>
You're probably using Element.innerHtml. You should use Element.setInnerHtml instead.
As you can see with the parameters of this new method, the HTML code is now validated and filtered. To restore the old behavior, you must provide a validator or a tree sanitizer allowing all attributes.
To explicitly allow "data" on anchors and buttons:
// Call NodeValidatorBuilder.allowX() methods to customize the validator.
final NodeValidatorBuilder _htmlValidator=new NodeValidatorBuilder.common()
..allowElement('a', attributes: ['data-target', 'data-toggle'])
..allowElement('button', attributes: ['data-target', 'data-toggle']);
query('#anElement').setInnerHtml('a content', validator: _htmlValidator);
Element and CustomElement classes use HTML sanitization in several places (Element.html factory, innerHtml property, createFragment method...).
Even if you don't use these methods directly in your own code, they're called by the underlying Dart libraries (CustomElement class was created for Polymer library but is also used by latest releases of Web UI library).
For the moment, there is NO way to globally disable or customize the default sanitization rules. So I guess you'll have to deal with setInnerHtml calls... or wait for another SDK release to fix the issue ("data-" attributes are valid HTML5 code, but the default sanitization filter doesn't allow them as well as inline styles: why these attributes are considered insecure?).
Note: you should consider switching from Web UI to Polymer, as Web UI is now deprecated.
For those who are working with angular dart, you will have to reimplement the NgDirective with custom validations like so:
library ng_bind_html_unsafe;
import 'dart:html' as dom;
import 'package:angular/angular.dart';
#NgDirective(
selector: '[ng-bind-html-unsafe]',
map: const {'ng-bind-html-unsafe': '=>value'} )
class NgBindHtmlUnsafeDirective{
final dom.Element element;
NgBindHtmlUnsafeDirective(this.element);
set value(value) => element.setInnerHtml(value == null ? '' : value.toString(),
validator: new dom.NodeValidatorBuilder()
..allowHtml5()
..allowElement('a', attributes: ['href'])
..allowElement('img', attributes: ['src']));
}
In that specific example, I am allowing links and pictures without any sanitization you can extend this however you'd like.
In Angular 0.9.9 ng-bind-html supports custom NodeValidators.
Just register a factory that returns a customized NodeValidator like shown in the other answers
factory(NodeValidator, (Injector inj) => getNodeValidator());
and ng-bind-html will use this NodeValidator.
I found that the HTML included this way isn't processed by Angular (any directives/components/expressions are ignored).
When I need this I use a custom bind-html directive like shown by #mabounassif with this additional code in the value setter after element.setInnerHtml
if(value != null) {
_compiler(_element.childNodes, _directiveMap)(_injector, _element.childNodes);
}
see also https://github.com/angular/angular.dart/issues/742
I wanted to use img element with innerHtml and found that the above answers were incomplete. It was missing the uriPolicy that defaults to the same origin. Defining a custom UriPolicy fixed the issue for me.
class DefaultUriPolicy implements UriPolicy {
DefaultUriPolicy();
bool allowsUri(String uri) {
// Return true/false based on uri comparison
return true;
}
}
Define validator like below:
allowElement('img',
attributes: ['src'], uriPolicy: new DefaultUriPolicy()))
For anyone who wants to use setInnerHtml and wants to allow all tags and attributes, you can use this validator:
class AllowAll implements NodeValidator {
#override
bool allowsAttribute(Element element, String attributeName, String value) {
return true;
}
#override
bool allowsElement(Element element) {
return true;
}
}
usage:
element.setInnerHtml(
value,
validator: AllowAll()
);
Taking #mabounassif's answer I did the following to also allow links to another sites:
import 'dart:html' as dom;
import 'package:angular/angular.dart';
#Directive(selector: '[html-inseguro]')
class HtmlInseguro {
final dom.Element element;
final dom.NodeValidatorBuilder _validador;
HtmlInseguro(this.element)
: _validador = dom.NodeValidatorBuilder()
..allowHtml5()
..allowElement('a', attributes: ['href'], uriPolicy: _TodoUriPolicy())
..allowElement('img',
attributes: ['src'], uriPolicy: _TodoUriPolicy());
#Input('html-inseguro')
set htmlInseguro(String value) {
element.setInnerHtml(value?.toString() ?? '', validator: _validador);
}
}
class _TodoUriPolicy implements dom.UriPolicy {
#override
bool allowsUri(String uri) {
return true;
}
}
Then in the component you should have sth like this:
#Component(
template: '''
<div class="vista-previa-mensaje" [html-inseguro]="mensajeAEnviar | dataReplacer: reemplazos | md2html"></div>
''',
directives: [
HtmlInseguro,
],
pipes: [Md2Html, DataReplacer])
class YourComponent{
//...

Resources