I would like to pass a reference to WebComponent instances created in markup to the WebComponent dart class. For instance:
html:
<element name="x-container>
<template iterate="x in xs">
<x-content-elem>
<x-item item="{{x}}" top-container="{{lexical-scoped-ref-to-container}}">
</x-content-elem>
</template>
...
I'm looking for a way to get a reference to x-container to the x-item.top-container property. The main thing is x-item might be nested in some complicated way so doing dynamic lookup could be difficult or not very robust.
You can use the DOM to find the parent element. Something like this should work.
From inside of x-item:
Container container = this.parent.xtag;
A custom element acts like a node on the page. The elem.xtag getter returns the Dart object that backs the node on the page.
If there are other elements in-between, you can can still use CSS queries to find elements that you are looking for.
Related
I'm learning Dart by making a simple webapp. the app ui I have in mind has two parts, one is a control panel, the other is a workspace. by clicking buttons in the control panel, user should be able to control the workspace.
both the control panel and the workspace are custom polymer elements. In the Control Panel's dart class, I can access itself by using shadowRoot.querySelector, but since the control panel needs to control the workspace, I need to access the workspace also. but I don't know how to do that. I tried querySelector for example, It gave me null. I understand it is a shadow DOM in the workspace tag, but how to access other tags' shadow DOM?
I can't find anything online, every example and document seems to only use shadowRoot to access self elements.
It is difficult to access the shadow DOM of another element, and this is by design. Instead of having your two custom elements so tightly coupled, a better approach would be to use events or signals. Your control panel element should take user input and fire appropriate events using the convenient fire() method it inherits from the PolymerElement class. Your application can catch and then relay those events to your workspace element. If that seems overly circuitous, you can use Polymer's <core-signals> element to pass events without dealing with intermediaries.
As an example, inside your control panel element, you might have a bold button.
<button on-click="{{boldClicked}}">Bold</button>
When that button is clicked, the control panel's boldClicked() method is executed in response. It might look something like this:
void boldClicked(Event event, var detail, Element target) {
fire('core-signal', detail: {'name': 'bold', 'data': null});
}
Then in your workspace element's HTML file, you might have:
<core-signals on-core-signal-bold="{{boldEventReceived}}"></core-signals>
And finally, in your workspace element's Dart class would be a method like so:
void boldEventReceived(Event event, var detail, Element sender) {
// manipulate workspace shadow DOM here
}
This is just one of several ways to accomplish this. You can look over the Dart team's <core-signals> example for more.
And of course, if you're using Polymer to its full potential, you will find that you need to do very little manual DOM manipulation. Using data binding and data-driven views is a winning strategy.
You can either use a selector that pierces though all shadow boundaries querySelector('my-tag /deep/ some-element') or querySelector('* /deep/ some-element') or as selector that just pierces through one level of shadow boundary querySelector('my-tag::shadow some-element') or alternatively
place both elements within the <template> of another Polymer element then you can connect attributes of both components with the same field on the common parent element (this is the preferred method in Polymer.
The solution of #user3216897 is fine of course especially if the elements don't share a common parent.
Instead of shadowRoot.querySelector you should be able to use $['abc'] if the element has an id attribute with the value 'abc'.
With Polymer, I have implemented a custom form, which is using polymer_signals.html for listening to changes from arbitrary other polymer elements.
It is implemented like this:
<link rel="import" href="packages/polymer_elements/polymer_signals/polymer_signals.html">
<polymer-element name="user-settings" extends="form">
<template>
<polymer-signals on-polymer-signalauth-changed="{{onAuth}}"></polymer-signals>
...
This element is dynamically instantiated in a parent polymer element like this:
var userSettings = new Element.tag('form', 'user-settings');
$['main'].children.add(userSettings);
This raises the exception:
Exception: Concurrent modification during iteration: Instance(length:4) of '_GrowableList'._notify#0x1c4cf589 (http://127.0.0.1:3030/buddy/web/packages/polymer_elements/polymer_signals/polymer_signals.dart:39:12)
<anonymous closure> (http://127.0.0.1:3030/buddy/web/packages/polymer_elements/polymer_signals/polymer_signals.dart:49:12)
If I statically instantiate the polymer-form, I don't get this error. How can I prevent this?
I can't reproduce your problem.
There are two common things that are often missed
if you have a custom main method
see the answer to Polymer querySelector working on DartVM but not in Chrome after compile
call of super.polymerCreated() in the constructor of elements that extend DOM elements.
see the answer to Custom Polymer element extending AElement in Dart
If you still can't solve the problem please add more code to your question (for example how your user-settings element looks like).
I have a custom polymer-element, "my-app" which uses template if="..." constructs to conditionally include other polymer elements.
All of these polymer elements are defined in one file, "views.html". This file includes the Dart code to implement all these custom elements, "views.dart".
I have a separate "index.html" that does nothing more than import the "views.html" file along with the standard polymer and dart initialization files, and then place a single my-app declaration tag in the body.
I am finding that the ".created" constructors for 1) the various subelements and 2) the my-app element itself (that conditionally includes subelements) are being called multiple times - even though a my-app element is declared only once in the simple "index.html" file.
I can see it right in the debugger.
Particularly regarding the calling of "MyApp.created", I thought this might be some garbage collection issue. But I set a global variable the first time MyApp is created to the MyApp instance to preclude this.
What is even stranger to me is that when MyApp is created repeatedly again, I can see that the global variable (to prevent gc of a MyApp instance) has been reset to "null", as if the entire file of Dart code had been reloaded/reevaluated somehow.
Very strange to me.
Are the PolymerElement subclasses for custom elements instantiated merely for the definition of custom elements? If so, that would be odd. I would think such custom polymer element subclasses would only be instantiated during the declaration of polymer elements.
Also, would a template conditional inside a polymer element definition somehow be able to force reconstruction of a the defined polymer element? This is hard to imagine, but then anything is possible with Dart Polymer, LOL!!!
I was looking for an authoritative document on when constructors for custom Dart PolymerElements might be called, but I could find no such document.
Any and all help greatly appreciated.
I have a custom #NgComponent in my project and it works if I place it within the static HTML of the application. What I'm trying to figure out is how to add one to the DOM dynamically? If I construct an instance of my Component it does not appear to be of type Element and so it cannot be added directly to the children of an element in the DOM. Is there an alternate way to construct my component or wrap it for injection into the DOM?
e.g. I naively expected to be able to do something like:
dom.Element holderEl = dom.document.querySelector("#my-holder");
holderEl.children.add( new MyComponent() );
But I have also tried simply appending HTML containing my custom element to an element using innerHTML
holder.innerHtml="<my-component></my-component>"
and creating the element using document.createElement()
dom.Element el = dom.document.createElement("my-component");
dom.document.body.append(el);
But the component does not seem to be realized when added.
thanks,
Pat
You can add components dynamically, but you must manually invoke the Angular compiler so it notices that the innerHTML has a component embedded in it.
However, that is not the "Angular way".
Instead, write your template as
<div id="my-holder">
<my-component ng-if="should_component_be_displayed"></my-component>
</div>
Here, my-component will be created and included in the DOM only if should_component_be_displayed is true.
The my-holder div can be removed which leads to a cleaner DOM structure.
Using Polymer Dart I often need to get hold of the Polymer-Element object behind one of the child elements.
ButtonElement nextButton;
void inserted()
{
//Get hold of the elements
nextButton = shadowRoot.query('#nextButton');
//Do some thing useful with nextButton
}
<template if="{{emailValid}}">
<button id="nextButton" on-click="nextStep">
</template>
This works fine. However if in this case nextButton is underneath a conditional template its not part of the DOM when inserted() is called and is therefore not found. Is there anyway other way to get hold of it?
Otherwise I will have to some how determine when that conditional template is displayed and grab it then.
This might depend on what exactly "Do something useful with nextButton" means, but the Polymer-ic way to accomplish this is generally to encapsulate any reusable behavior together with the DOM it operates on. That is, instead of including code to operate on #nextButton in the enclosing element's inserted method, create a new custom element, let's call it super-button, and put the relevant code in super-button's ready or inserted method.
Then, if you find some behavior that really should be outside of super-button, follow the same pattern as the on-click handler you use above. Have super-button fire a custom event at the appropriate time and then declaratively map a handler to that event:
<template if="{{emailValid}}">
<super-button on-click="nextStep" on-my-special-event="mySpecialEventHandler"></super-button>
</template>