This question already has an answer here:
When dynamically adding dart polymer elements, how do I get observable variables to on DOM
(1 answer)
Closed 8 years ago.
I have read this thread about how to pass data to a polymer element
Button click in polymer dart does not work; needs polymer-element
But however, if I want to pass data to a programmatically created element by using createElement() in polymer.dart, there's no wrapping template. How am I going to pass the data as mentioned above?
Btw, even though I think dart is a really cool language (I dislike JavaScript,) I found polymer in dart maybe still not ready yet. A lot of things are not documented and have to go though examples or ask here.
Thanks,
Yi
I think you should remove the start()-function from MyElementElement-class and add it to upper level. Basically you shouldn't have any reference in custom-element to upper level objects. Now the custom element is not reuseable, it works only within this context.
I have searched for a long time but anything work. Only one solution work fine for me: https://stackoverflow.com/a/20067111/189411
Here is my example on how i passed data to a dynamically created custom polymer-element.
My intention was, click on an "a href" and load a polymer-element in a container div.
polymer_element.html
<polymer-element name="my-element">
<template bind>
<p> {{ mytext }} </p>
</template>
<script type="application/dart" src="polymer_myelement.dart"></script>
</polymer-element>
polymor_element.dart
import 'dart:html';
import 'package:polymer/polymer.dart';
import 'package:custom_element/custom_element.dart';
#CustomTag('polymer-myelement')
class MyElementElement extends PolymerElement with ObservableMixin {
bool get applyAuthorStyles => true;
#observable String mytext = "initial text";
void start() {
query('#a href to click').onClick.listen((MouseEvent event){
query('.container').children.clear();
var myElem = createElement('polymer-myelement');
MyElementElement myel = myElem.xtag;
myel.mytext = "i changed the text now or import w/e you want";
query('.container').children.add(myElem);
});
}
}
main() {
MyElementElement el = new MyElementElement();
el.start();
}
index.html
<head>
<link rel="import" href="polymer_myelement.html">
and everything you need ...
</head>
<body>
<a href to click>click me for new text</a>
<div class="container">
<!-- START OF MAINCONTAINER -->
<!-- END OF MAINCONTAINER -->
</div>
</body>
So here is the explanation:
In my main() i just run my start() function and set an onclickListener for my navigation bar href. When i click my href now, i just clear all children in my "div class="container", because i always want a clear div before i insert my polymer-elements.
Now create a polymer-Element with "createElement('polymer-mylement');".
Create a MyElement object and assign the polymer-element "xtag" to it.
Now you have access to all the properties and can change them.
Just add your created Element to your div and you are done.
You just dynamically created a polymer-element, assigned your new data to it and see it in your index.html
Related
For my Vue project i have a login component and a responsive overlay component.
I built it that way so i could load my responsive overlay component with the needed content using props.
To help you understand the structure, it looks like the following:
Home.vue
<script setup lang="ts">
import ResponsiveOverlay from '../components/responsiveOverlay.vue'
</script>
<template>
<div class="overlayWrapper">
<ResponsiveOverlay v-if='showDisplay' :switchContent='switchOverlayContent'></ResponsiveOverlay>
</div>
</template>
ResponsiveOverlay.vue
<script setup lang="ts">
import LoginOverlay from '../components/loginOverlay.vue'
const props = defineProps({
switchContent: String,
})
</script>
<template>
<div class="responsiveOverlayWrapper">
<LoginOverlay v-if='switchContent == "login"'></LoginOverlay>
</div>
</template>
LoginOverlay.vue
<template>
<h1>Login</h1>
</template>
SwitchOverlayContent is a string variable, that (as of now) is hardcoded to contain "login"
As you can see, i tried using v-if="showDisplay" with the ResponsiveOverlay in a hope i could just toggle the bound bool and it would show, which did not work, probably because the v-if is only ran when loading the page.
So what i would need is a solution where i could click a button which would then set switchOverlayContent dynamically to "login" and then display the ResponsiveOverlayContent.
How would i achieve said behaviour and also, is my strategy of having a component inside of a component viable?
I have a projet setup similar to the Polymer shop-app. To reduce thr code base in the app itself, I splitted the hole main page layout into an separete element project. The project has an element Layout which loads 7 of its Dependencys lazy:
_pageLoaded: function (shouldResetLayout) {
this._ensureLazyLoaded();
...
}
_ensureLazyLoaded: function () {
if (!this.loadComplete) {
Polymer.RenderStatus.afterNextRender(this, function () {
this.importHref(this.resolveUrl('lazy-resources.html'), function () {
this.loadComplete = true;
}, null, true);
});
}
},
Basically the same logic as the Shop-app exmaple._pageLoaded is an observer of the two-way data bound page property of my Shop-app.
In my Shop-app itself, I include the layout element like so:
<link rel="import" href="../bower_components/lc-app-layout/lc-app-layout.html">
<lc-app-layout categories="[[categories]]" page="[[page]]" category-name="{{categoryName}}">
<iron-pages role="main" selected="[[page]]" attr-for-selected="name" selected-attribute="visible">
<!-- home view -->
<shop-home name="home" categories="[[categories]]"></shop-home>
<!-- list view of items in a category -->
<shop-list name="list" route="[[subroute]]" offline="[[offline]]" query-Params="[[queryParams]]"></shop-list>
<!-- detail view of one item -->
<shop-detail name="detail" route="[[subroute]]" offline="[[offline]]"></shop-detail>
</iron-pages>
</lc-app-layout>
And also have some lazy ressources in the app itself, which are loaded the same way as in the layout.
Now when I want to build everythin, I need to add the lazy-resurces of the layout element as an fragment to the Polymer.json so that the analyser will find the it's dependencys:
"fragments": [
"src/view/list/shop-list.html",
"src/view/detail/shop-detail.html",
"src/lazy-resources.html",
"bower_components/lc-app-layout/lazy-resources.html"
No when I serve this build, the page doesn't load most of the times. It shows error like:
Cannot read property '_makeReady' of undefined
Polymer is not a function
Cannot read property 'isDescendant' of undefined
When I throttle the network speed, chances are higher that everythin will load up. So I assume there is some timing issue in the asynchronous ImportHref or something like that. Or am I just missusing the fragments to tell the analyzer to scan my laczy resources of the layout element?
I'm using Polymer-CLI 0.18.0. Runnig the app with polymer serve does work as intended.
Solved the problem by moving the lazy loading from layout element to the lazy-ressource of the Shop-app.
I'm just learning polymer (1.0) so please bear with me.
I'm using express.js to return some array of JSON.stringified items and for-each them, so the result is as follows (in HTML):
<fighter-profile fighter="{"country":"USA","countryFullName":"United States","name":"Frank Mir","nickname":"","zuffa_record":{"wins":"15","losses":"9","draws":0,"no_contest":0}}"></fighter-profile>
it seems ugly as hell, but that's json.
Here's my component:
<dom-module id="fighter-profile">
<template>
<div>
<paper-item>
<paper-item-body two-line>
<div>{{fighter.name}}</div>
<div secondary>{{nickname}}</div>
<div>
<paper-button raised on-click="handleClick">Show nickname</paper-button>
</div>
</paper-item-body>
</paper-item>
</div>
<br />
<hr />
<br />
</template>
<script>
Polymer({
is: 'fighter-profile',
properties: {
fighter: Object,
nickname: {
type: String,
value: 'testing'
}
},
ready: function() {
this.nickname = (this.fighter.nickname !== '') ? this.fighter.nickname : '... the dude has no nickname!';
},
handleClick: function() {
alert(this.nickname);
}
});
</script>
</dom-module>
Now, the funny part: the name gets displayed properly, while where I have the <div secondary>{{nickname}}</div>, the result in HTML is literally {{nickname}}; however, if I click on button, I get the correct value.
What am I missing here?
UPDATE:
I've googled some stuff, and replaced ready method with created and, of course, it didn't work, since created I think is part of Polymer 0.5 version. Then I switched back to ready method and now everything works as expected. Very odd.
What seems to be the problem? Some caching gone wrong? a bug?
UPDATE 2:
I've changed some stuff again and it doesn't work, but now I've figured out how to replicate the mistake. So, this piece of code DOESN'T work correctly:
<div secondary>The dude is also known as {{nickname}}</div>
the result is literally "{{nickname}}"
However, this works correctly:
<div secondary>The dude is also known as <span>{{nickname}}</span></div>
the result is the actual nickname.
So, putting properties in span tag renders it correctly. What's going on?
There's a few things I think I can help you with here. First, you can make your JSON much more readable by using single quotes for your attributes. Additionally, you can include white space, if you are hard-coding the JSON:
<fighter-profile
fighter='{
"country":"USA",
"countryFullName":"United States",
"name":"Frank Mir",
"nickname":"",
"zuffa_record":{
"wins":"15",
"losses":"9",
"draws":0,
"no_contest":0
}
}'></fighter-profile>
Next, I'm going to assume that the JSON is actually not hard-coded, and bound to another data source. I make this assumption because it seems like your fighter property is not available in ready, as you are expecting it to be. A common issue I see in cases such as this is something like the following:
<template is="dom-repeat" items="{{data}}" as="fighter">
<fighter-profile fighter="{{fighter}}"></fighter-profile>
</template>
The thing to keep in mind in the above case is that <fighter-profile> is created, readied, and attached to the DOM before the parent element assigns fighter to its fighter property.
To remedy this, you can make use of observers which perform tasks automatically when the data gets loaded into a property:
<script>
Polymer({
is: 'fighter-profile',
properties: {
fighter: Object,
nickname: {
type: String,
value: 'testing'
}
},
observers: [
// This tells Polymer to watch `fighter` and fire the
// _fighterUpdated method only after `fighter` receives
// a value **other than undefined.**
'_fighterUpdated(fighter)'
],
_fighterUpdated: function(fighter) {
this.nickname = (this.fighter.nickname || '... the dude has no nickname!');
}
});
</script>
Next, binding properties to HTML. When you bind to HTML contents, such as with <div>{{property}}</div>, what Polymer (currently) does behind the scenes is bind property directly to div.innerText. Polymer also only checks the first two characters of innerText to see if it's a {{ or [[, and does not do anything if it doesn't find them.
The Polymer team is working to make binding more robust, but so far as I know they haven't announced any concrete plans or timelines. For the time being, the solution is as you've discovered, just wrap an inline binding in <span> =)
Problem: I have an auto binding template in my main index.html page. Inside the template I am using two of my custom elements. One element is the producer of some data and the other one is the consumer of that data. These custom elements expose published/declared properties for each other to use and bind to. I was able to do that in Polymer 0.5 fairly easily (an example shown below). How do I do the same in Polymer 1.0?
How I used to do in Polymer 0.5?
In Polymer 0.5 I used to data bind between published properties of two custom elements using curly brace syntax and then inside it used the auto node finding concept to directly bind to other element's published property. An example shown below,
<template is="auto-binding">
<my-navigation selectedLabel="Home" id="my_navigation"></my-navigation>
<my-scaffold toolbartitle="{{ $.my_navigation.selectedLabel }}" id="my_scaffold"></my-scaffold>
</template>
I tried something similar in Polymer 1.0 as shown in the example below
<template is="dom-bind">
<my-navigation selectedLabel="Home" id="my_navigation"></my-navigation>
<my-scaffold toolbartitle="{{ $.my_navigation.selectedLabel }}" id="my_scaffold"></my-scaffold>
</template>
But it throws an error:-
Uncaught TypeError: Cannot read property '$' of undefined
You can't do $.* bindings inside the template in Polymer 1.0. Instead, either refactor or use computed functions.
In your situation, since selectedLabel and toolbartitle shares the same value, it is much better to simply bind them to the same property.
Also, attribute names that are declaratively passed in (through the element tag) need to be serialized, so selectedLabel becomes selected-label.
<body>
...
<template id="tpl" is="dom-bind">
<my-navigation selected-label="{{myLabel}}" id="my_navigation"></my-navigation>
<my-scaffold toolbartitle="{{myLabel}}" id="my_scaffold"></my-scaffold>
</template>
<script>
...
window.addEventListener("WebComponentsReady", function (e) {
document.querySelector("#tpl").myLabel = "Home";
...
});
...
</script>
</body>
There is probably a better way to do that, but you can try this:
<body>
<template id="app" is="dom-bind">
<my-navigation selectedLabel="Home" id="my_navigation"></my-navigation>
<my-scaffold toolbartitle="{{ selectedLabel }}" id="my_scaffold"></my-scaffold>
</template>
<script>
var app = document.querySelector('#app');
app.addEventListener('template-bound', function () {
console.log('Our app is ready to rock!');
});
window.addEventListener('WebComponentsReady', function () {
document.querySelector('body').removeAttribute('unresolved');
var my-navigation = document.querySelector('my-navigation');
// This will add the variable to the 'app' context (template)
app.selectedLabel = my-navigation.selectedLabel;
});
</script>
</body>
I'm trying to include classes based on parameters of a json, so if I have the property color, the $= makes the trick to pass it as a class attribute (based on the polymer documentation)
<div class$="{{color}}"></div>
The problem is when I'm trying to add that class along an existing set of classes, for instance:
<div class$="avatar {{color}}"></div>
In that case $= doesn't do the trick. Is any way to accomplish this or each time that I add a class conditionally I have to include the rest of the styles through css selectors instead classes? I know in this example maybe the color could just simple go in the style attribute, it is purely an example to illustrate the problem.
Please, note that this is an issue only in Polymer 1.0.
As of Polymer 1.0, string interpolation is not yet supported (it will be soon as mentioned in the roadmap). However, you can also do this with computed bindings. Example
<dom-module>
<template>
<div class$="{{classColor(color)}}"></div>
</template>
</dom-module>
<script>
Polymer({
...
classColor: function(color) {
return 'avatar '+color;
}
});
<script>
Edit:
As of Polymer 1.2, you can use compound binding. So
<div class$="avatar {{color}}"></div>
now works.
Update
As of Polymer 1.2.0, you can now use Compound Bindings to
combine string literals and bindings in a single property binding or text content binding
like so:
<img src$="https://www.example.com/profiles/{{userId}}.jpg">
<span>Name: {{lastname}}, {{firstname}}</span>
and your example
<div class$="avatar {{color}}"></div>
so this is no longer an issue.
The below answer is now only relevant to versions of polymer prior to 1.2
If you are doing this a lot, until this feature becomes available which is hopefully soon you could just define the function in one place as a property of Polymer.Base which has all of it's properties inherited by all polymer elements
//TODO remove this later then polymer has better template and binding features.
// make sure to find all instances of {{join( in polymer templates to fix them
Polymer.Base.join = function() { return [].join.call(arguments, '');}
and then call it like so:
<div class$="{{join('avatar', ' ', color)}}"></div>
then when it is introduced by polymer properly, just remove that one line, and replace
{{join('avatar', color)}}
with
avatar {{color}}
I use this a lot at the moment, not just for combining classes into one, but also things like path names, joining with a '/', and just general text content, so instead I use the first argument as the glue.
Polymer.Base.join = function() {
var glue = arguments[0];
var strings = [].slice.call(arguments, 1);
return [].join.call(strings, glue);
}
or if you can use es6 features like rest arguments
Polymer.base.join = (glue, ...strings) => strings.join(glue);
for doing stuff like
<div class$="{{join(' ', 'avatar', color)}}"></div>
<img src="{{join('/', path, to, image.jpg)}}">
<span>{{join(' ', 'hi', name)}}</span>
of just the basic
Polymer.Base.join = (...args) => args.join('');
<div class$="{{join('avatar', ' ', color)}}"></div>
<template if="[[icon_img_src]]" is="dom-if">
<img class$="{{echo_class(icon_class)}}" src="[[icon_img_src]]">
</template>
<span class$="{{echo_class(icon_class, 'center-center horizontal layout letter')}}" hidden="[[icon_img_src]]">[[icon_text]]</span>
<iron-icon icon="check"></iron-icon>
</div>
</template>
<script>
Polymer({
echo_class: function(class_A, class_Z) {
return class_A + (class_Z ? " " + class_Z : "");
},