I have been working on a component to streamline a series of other components. The reason I wanted to do this is pretty much to create 1 place where the implementations would be since the code is the same, we don't have to worry about maintaining 17 html files, just 1.
I was trying to find a way to programmatically move 1 web component into another. some sort of A.appendHtml(B); but i was not 100% sure if that worked with WebComponents.
My goal is to move mine-b's template contents into ele-a before it renders out to the page. Essentially unwrapping the mine-b wrapper and injecting it to where it is suppose to go
<template>
<ele-a></ele-a>
<div>
<mine-b></mine-b>
</div>
</template>
<ele-a></ele-a> accepts only 2 tag types: sub-a and sub-b. sub-b can accept form tags.
<ele-a>
<sub-a></sub-a>
<sub-b>
<form></form>
</sub-b>
</ele-b>
so i thought to myself to create a component which would allow me to streamline all the attribute setting
<mine-a>
<ele-a>
<content></content>
</ele-a>
</mine-a>
<mine-b>
<sub-a></sub-a>
<sub-b>
<content></content>
</sub-b>
</mine-b>
which would allow me to do something like:
<mine-a>
<mine-b>
<form></form>
</mine-b>
</mine-a>
THe issue i have been having is that since ele-a only accepts sub-a and sub-b so i cant define it as a child of ele-a. The reason is the select attribute inside of ele-a which will prevent me from stamping out mine-b. So i tried to make it a sibling:
<mine-a>
<ele-a></ele-a>
<mine-b></mine-b>
</mine>
So this is where i am right now. In the dart for mine-a which extends PolymerElement. Is there a way to move the stamped out contents of the template of mine-b to ele-a? IE: the sub-a, and sub-b?
I was thinking to do something like:
<mine-a>
<ele-a id="targetEle"></ele-a>
<div id="sourceEle" hidden>
<content select="mine-b"></content>
</div>
</mine-a>
class MyElementA extends PolymerElement {
DivElement get _source => $['sourceEle'];
ElementA get _target => $['targetEle'];
MyElementA.created() : super.created(){
_target.appendHtml(_source.children());
}
}
or something to just move 1 WebComponent into another? I have been having some issues with this.
When looking at the PolymerLife Cycle, I was thinking I might be able to do it prior to the dom being initialized, by doing it in the created function.
Related
I am trying to develop an UI and the first step is to create CssLayout. Each CssLayout component is added hierarchically with and many CssLayout component.
The problem is when i run the application and inspect the div tags, the class attribute has extra strings that needs to be removed.
<div class="v-csslayout v-layout v-widget .content-container v-
csslayout-.content-container v-has-width v-has-height" style="width: 100%;
height: 100%;"><div class="v-csslayout v-layout v-widget .inner-content-
container v-csslayout-.inner-content-container"></div></div>
and what I need is
<div class=".content-container">
<div class=".inner-content-container">
</div>
</div>
Java Code:
#StyleSheet("{css/spreadjsdefault.css}")
public class SpreadJSWidget extends CssLayout {
/**
*
*/
public SpreadJSWidget() {
super();
addStyleName(".content-container");
CssLayout mainBox = new CssLayout();
mainBox.addStyleName(".inner-content-container");
addComponent(mainBox);
}
spreadjsdefault.css (They are empty for now)
.content-container
{
}
.inner-content-container
{
}
Please advice !
Two things:
In order to be able to properly match the css rules, you have to omit the leading . when adding the style name, i.e. addStyleName("contentContainer"). This way, the css elements will match your style definition.
Css classes like v-csslayout are default classes defined by vaadin used by the default themes to provide a basic layout. They are there by default and can't (and actually shouldnt) be removed entirely. What you can do, however, is to define and overwrite these rules yourself. What's important: Either way, your custom classes will still match when you define them in your style sheet and can overwrite the default theming.
From here :
https://github.com/dart-lang/angular_components_example/blob/master/example/app_layout_example/lib/app_layout_example.html
I want to split this template in two templates:
one for sidebar <material-drawer>, named for example sidebar_component.{dart,html}
one other for <div class="material-content">, named for example app_component.{dart,html}
Question:
How to reach <material-drawer> from sidebar_component, with <material-button icon class="material-drawer-button" (trigger)="drawer.toggle()"> into app_component?
Components are encapsulated on purpose. So there isn't a super easy way to reach into the encapsulation of one component from the other.
What you can do is create a passthrough from one component to the other.
<side-bar #sidebar></side-bar>
<app-component (openSideBar)="sidebar.toggle()"></app-component>
sidebar_component
#Component()
class SidebarComponent {
#ViewChild(MaterialPersistentDrawerDirective)
MaterialPersistentDrawerDirective drawer;
void toggle() => drawer.toggle();
}
app_component.dart
#Component()
class AppComponent {
final _openSideBar = StreamController<void>();
#Output()
Stream<void> openSideBar => _openSideBar.stream;
// This is getting called by the trigger of the button click
void onButtonClick() => _openSideBar.add();
}
I would say that for me passing all of these events feels like a bit of a smell. The components themselves are breaking encapsulation and so I wouldn't architect the app exactly like that.
I would probably have the contents of the drawer be a component, and perhaps the header and body depending on how complex they got. To have something more like this:
<material-drawer #drawer>
<side-bar *deferredContent></side-bar>
</material-drawer>
<div class="material-content">
<app-header class="material-header shadow" (triggerDrawer)="drawer.toggle()">
</app-header>
<router-outlet></router-outlet>
</div>
I find it better to keep the app-layout logic in the same components if possible and encapsulate the pieces of that. You could also pass the drawer as an input, but then you are making those highly coupled which I tend to try not to do.
I have been scouring the web for a clear answer on how to query for an element generated by a dom-repeat element from Dart code.
sample.html
<dom-module id="so-sample>
<style>...</style>
<template>
<template is="dom-repeat" items="[[cars]] as="car>
...
<paper-button on-click="buttonClicked">Button</paper-button>
<paper-dialog id="dialog">
<h2>Title</h2>
</paper-dialog>
</template>
</template>
sample.dart
I'll omit the boilerplate code here, such as imports or the query to my database to fill the cars property ; everything works fine.
...
#reflectable
void buttonClicked(e, [_])
{
PaperDialog infos = this.shadowRoot.querySelector("#dialog");
infos.open();
}
This generates the following error :
Uncaught TypeError: Cannot read property 'querySelector' of undefined
I have tried several 'solutions', which are not, since nothing works.
The only thing I saw on quite a lot of threads is to use Timer.run() and write my code in the callback, but that seems like a hack. Why would I need a timer ?
I understand my problem may be that the content of the dom-repeat is generated lazily, and I query the items 'before' they are added to the local DOM.
Another advice I didn't follow is to use Mutation Observers. I read in the polymer API documentation that the observeNodes method should be used instead, as it internally uses MO to handle indexing the elements, but it again seems a bit complicated just to open a dialog.
My final objective is to bind the button of each generated model to a dedicated paper-dialog to display additional information on the item.
Has anyone ever done that ? (I should hope so :p)
Thanks for your time !
Update 1:
After reading Gunter's advices, although none of them actually worked by themselves, the fact that the IDs aren't mangled inside a dom-repeat made me think and query paper-dialog instead of the id itself, and now my dialog pops up !
sample.dart:
PaperDialog infos = Polymer.dom(root).querySelector("paper-dialog");
infos.open();
I now hope that each button will call the associated dialog, since I'll bind data inside the dialog relative to the item I clicked ~
Update 2:
So, nope, the data binding didn't work as expected: All buttons were bound to the item at index 0, just as I feared. I tried several ways to query the correct paper-dialog but nothing worked. The only 'workaround' I found is to query all the paper-dialog into a list and then get the 'index-th' element from that list.
#reflectable
void buttonClicked(e, [_])
{
var model = new DomRepeatModel.fromEvent(e);
List<PaperDialog> dialogs = Polymer.dom(this.root).querySelectorAll("paper-dialog");
dialogs[model.index].open();
}
This code definitely works, but it feels kind of a waste of resources to get all the elements when you really only need one and you already know which one.
So yeah, my initial problem is solved, but I still wonder why I couldn't query the dialogs from their id:
...
<paper-dialog id="dialog-[[index]]">
...
</paper-dialog>
#reflectable
void buttonClicked(e, [_])
{
var model = new DomRepeatModel.fromEvent(e);
PaperDialog dialog = Polymer.dom(this.root).querySelector("dialog-${model.index}");
dialog.open();
}
With this code, dialog is always null, although I can find those dialogs, correctly id-ied, in the DOM tree.
You need to use Polymers DOM API with shady DOM (default). If you enable shadow DOM your code would probably work as well.
PaperDialog infos = new Polymer.dom(this).querySelector("#dialog")
I have a custom button component done in Polymer Dart:
<div id="buttonDiv">
<my-button id="traceButton"
mode="icon" faicon="fa-comment-o"
toolTip="Print a simple comment"
disabled="false" on-click="{{ traceSomething }}">
</my-button>
</div>
I'm trying to copy/paste this button somewhere else. So a user defines it somwhere, and I basically move it by way of getting $['buttonDiv'].children then inserting it somewhere else. The problem is that {{ traceSomething }} is now irrelevant since it's not part of the new parent. I get errors saying that the parent object, which is another polymer component doesn't have an instance getter "traceSomething".
My question is, is there a way to remove "traceSomething" before I insert it somwhere else? I tried removing the "onClick" event listeners, but the buttons still wants to call that function upon click. Also, I've tried adding a preventDefault, etc, like in: In Dart, if I listen to a click event with two listeners, how do I know which happens first?
But, no luck.
I'm not sure what you mean by copy/past. Do you clone the element, or do you just append it to some other elements children.
Anyway, I don't think you can remove the event listener if it was added declaratively. If you add it imperatively it is easy to remove and readd later.
import 'dart:async';
...
StreamSubscription subsc;
#override
attached() {
super.attached();
subscr = onClick.listen((e) => (this.parentNode as ShadowRoot).host.traceSomething(e));
}
#override
detached() {
super.detached();
if(subscr != null) {
subscr.cancel();
}
}
See also https://stackoverflow.com/a/22168745/217408 about accessing the parent of a Polymer element (for Dart Polymer <= 0.16.x)
Here's something I thought might be a bit easier. Despite the specifics of the question, I'm interested in any method that will let me have a third form field auto-updated based on the content of two other fields with Polymer.dart.
Something like this, where the "[ ]" represent form fields.
Name: [given name] [family name]
Full name: [family_name, given_name]
So for example; if someone enters "John" and "Smith" in the first two fields. Then the "full name" line shows: [Smith, John], when either of the fields are updated.
I've based the following example on the classes and mark-up from the form Dart Polymer tutorial
Get Input from a Form tutorial
For a form like this ...
<polymer-element name="reference-form" extends="form" >
<template>
<style> ... </style>
<div id="slambookform" >
<div class="entry">
<label>Author:</label>
<input type="text" value="{{theData['authorGivenName']}}" >
<input type="text" value="{{theData['authorFamilyName']}}">
</div>
:
<div class="entry">
<label>Full name:</label>
<input disabled type="text" value="{{fullName}}" >
</div>
:
</div>
<template>
</polymer-element>
My initial attempt to make this happen was a function like:
#observable
String fullName(){
return theData['authorFamilyName'] +', '+ theData['authorGivenName'];
}
Which doesn't work. When I make 'fullName' to an #observable variable and update it with a button the form is updates as required. Hence my question, can I bind a third field to two (or more) others?
I think I will need some kind of event handler. For two fields, formatting on a change even is simple enough. I want to format several fields in the ultimate case, not just two fields.
While on this topic, is there a hook in dart-polymer or dart to supply a future or call-back? In my example, something like: 'after-change'. Just thinking out loud, something like that would be good.
Thanks in advance.
Along those lines (caution - code is not tested)
<polymer-element name="reference-form" extends="form" >
<template>
<style> ... </style>
<div id="slambookform" >
<div class="entry">
<label>Author:</label>
<input type="text" value="{{authorGivenName}}" >
<input type="text" value="{{authorFamilyName}}">
</div>
:
<div class="entry">
<label>Full name:</label>
<input disabled type="text" value="{{fullName}}" >
</div>
:
</div>
<template>
</polymer-element>
class reference_form.dart
String _authorGivenName;
#observable get authorGivenName => _authorGivenName;
set authorGivenName(String val) {
_authorGivenName = val;
notifyPropertyChange(#fullName, '${_authorGivenName} ${_authorFamilyName}',
'${val} ${_authorFamilyName}');
}
String _authorFamilyName;
#observable get authorFamilyName => _authorFamilyName;
set authorFamilyName(String val) {
_authorFamilyName = val;
notifyPropertyChange(#fullName, '${_authorGivenName} ${_authorFamilyName}',
'${_autorGivenName} ${val}');
}
#observable
String get fullName => '${_authorGivenName} ${_authorFamilyName}';
I have a workaround for this problem, standing on the shoulders of Günter Zöchbauer (comment above). My objective is to "bind" one field value to two in a read-only fashion. We are not quite there yet, however the pathway is educational in its own right.
Observer method
This solution is kind of a workaround for the objective I set myself. I've made some annotations on this code to explain what I saw, or why I think is happening.
The intention is for fullName to show both names in the form:
familyName, givenName; e.g.
Smith, John
reference-form.html:
<polymer-element name="reference-form" extends="form" >
<template>
<style> ... </style>
<div id="slambookform" >
<div class="entry">
<label>Author:</label>
<input type="text" value="{{theData['givenName']}}" >
<input type="text" value="{{familyName}}">
</div>
:
<div class="entry">
<label>Full name:</label>
<input disabled type="text" value="{{fullName}}" >
</div>
:
</div>
<template>
</polymer-element>
The code for the form properties, the things Polymer-dart binds to the HTML with the moustache syntax, "{{fullName}}". To keep things simple, I used just one 'notifier' field and this updates the fullName field from both familyName and givenName.
reference_form.dart:
//---- testing ----
String _familyName; // (1)
#observable // (2)
String get familyName => _familyName; // (3)
void set familyName( String nam ){ // (4)
_familyName = nam;
fullName = notifyPropertyChange( // (5)
#fullName,
"${fullName}",
"${nam}, ${theData['givenName']}" );
}
#observable
String fullName; // (6)
//---- end: testing ----
The private member, "_familyName", is a shadow for the public familyName property used in the template (snippet above).
Shadow (private) member, "_familyName", stores the data for the familyName pseudo property.
The next three lines declare an #observable property, familyName
Get familyName. Simply echo the value for the shadow variable.
Set familyName. Updates the shadow variable and the composite fullName property.
Note: the composit formatting could be done with two lines: _familyName = nam; fullName = nam; ... But we want to see all changes propagated see (#5).
The notifyPropertyChange() method updated all observers of the fullName property.
Note: I didn't hack around inside Polymer itself; inside the Observable class, fullName doesn't has no observers with the code shown.
Until I saw this, I assumed that the Polymer binding to the HTML template was via an observer (watcher), it would seem not. I may be mistaken. In any case, the call to notifyPropertyChange() for the '#fullName' symbol didn't change the results for this test case.
fullName property bound to the Polymer form.
Basically the {{fullName}} value will be updated every time there's a change to the familyName pseudo property.
Note on efficiency:
The familyName setter is called with every keystroke (observed while debugging). I understand that, and suggest it is not always really the best solution.
For me, I'd prefer to only call the setter when a user exits the field. However when I used onblur, the trigger was a blur of the form, not the field.
It seems that we might all benefit in terms of performance with a bit more insider information about these hooks, pathways and any options available to make things more efficient.
Comments and improvements welcome. This example is a workaround for me, so its definitely a work in progress. ;-)
Encapsulation method
I am evolving a solution closer to the original ambition and based on the 'observer method' above. This approach relies on the current, i.e. Dart v4, use of modules and libraries. I'll show the working code first and explain interesting stuff with notes.
reference_form.dart:
import 'package:exportable/exportable.dart'; // [1]
class _Data // [2]
extends Object with Exportable { // [3]
#export String publishDate; // [4]
#export String authorGivenName = '(given)';
#export String authorFamilyName = '(family)';
#export String authorUrl = '';
//--- attributes ---
String get fullName => "${authorFamilyName}, ${authorGivenName}"; // [5]
void set fullName( String nam ){ // [6]
//don't need this
}
//--- ctor ---
_Data(){
publishDate = new DateTime.now().toString(); // [7]
}
} //_Data
#CustomTag('reference-form')
class SlamBookComponent extends FormElement with Polymer, Observable {
SlamBookComponent.created() : super.created();
//---- testing ----
#observable
_Data data = new _Data(); // [8]
:
} //SlambookComponent
Notes:
Include Exportable mixin to convert to JSON. I'm not exporting 'fullName' because it is just formatting at the moment.
Add exportable to your pubspec.yaml and 'Run Pub get'.
The "_Data" class is private to the reference_form.dart module. I did a bit of testing of the scope rules because I do not want the internal data structure to leak, except for something catholic like JSON of course (small-c).
Bring-in the Exportable mixin.
I have tested Exportable, it implements exactly what I thought I'd have to write myself. Happy with this.
JSON is not a requirement of the original question; but I did want the (eventual) solution to be a first class artefact that can be serialized or saved is important in the majority of my use-cases.
This is a very good example of the facility to extend Dart quick and agile!
Use the #export modifier to identify fields specific to be interchanged as JSON.
Export the fullName attribute as a String (get).
There is no need for set operation. However Dart apparently insists that a Set method matches 'get'.
I am disappointed by this. I much prefer the idea that I can have READ-ONLY properties and attributes, e.g. like ruby.
As tested, Dart SDK v1.4.0; fails when a matching setter is not implemented/declared(??).
Use a constructor to set initial values for Date data attribute.
Declares an opaque public property called "data", as an (private) _Data instance.
The data formatting of key fields is encapsulated in the private _Data declaration.
The Exportable mixin interface is used to map the private class to a public JSON result.
Point #8 demonstrates a powerful aspect of dart, to enable an opaque implementation of objects and yet, you can 'deliver'/'share' details without specific internal details.
I have run this code and checked that the concepts work for hidden data (the _Data type) and opaque access and serialisation. Also you can't accidentally look at internal private type (accidentally, although explicit hacks may be possible). I don't apologise for accepting the C / C++ conscious responsibility paradigm -- I think this a the most powerful aspect of being a programmer; WE are responsible for effects/bugs stemming from the code we produce. I recommend testing 'bits of behaviour' in small mini-use-cases.
I put examples of the polymer markup; nothing surprising. For me this approach is less verbose and a bit more Object Oriented than the original (early) Dart tutorial
reference_form.html
<polymer-element name="reference-form" extends="form" >
<template>
<style> ... </style>
<div id="slambookform" >
<div class="entry">
<label>Author:</label>
<input type="text" value="{{data.authorGivenName}}" >
<input type="text" value="{{data.authorFamilyName}}">
</div>
<div class="entry">
<label>Published:</label>
<input type="date" value="{{data.publishDate}}">
</div>
</div>
</template>
<script type="application/dart" src="reference_form.dart"> </script>
</polymer-element>
In the Polymer mark-up can know (and has visibility over) internal field names. Why?
... Because the "reference_form.html" and "reference_form.dart" via Polymer-dart. It is quite nice really; although it seems that the ".dart" and ".html" components are closely coupled like ASP.NET and C#/VN.NET as (also) specified by convenience(??). I confess that's a completely different subject; there are things to resolve to keep things yar (yachting term).
Anyway for me, I feel the approach begun with the encapsulation shamble above is better suited to my needs for a small utility.
Polymer now supports this use case directly with #ObserveProperty
#observable String authorGivenName = '';
#observable String authorFamilyName = '';
#observable String get fullName => '${authorGivenName} ${authorFamilyName}';
#ObserveProperty('authorGivenName authorFamilyName')
void updateFullName(old) {
notifyPropertyChange(#fullName, old, fullName);
}