I had a component with two properties hinted as booleans. And I noticed that with the attribute reflection activated, using one as a string will break the other (chrome stable and canary).
<polymer-element name='any-name' attributes='attr1 boolattr'>
<template>
boolattr: <i>{{boolattr}}</i>, attr1: <i>{{attr1}}</i>
</template>
<script>
Polymer('any-name',{
publish:{
attr1 :{value:true,reflect:true},
},
boolattr:false
})
</script>
</polymer-element>
<any-name attr1="false" boolattr></any-name>
I know that attr1 shouldn't be used like this, but i'm still curious about what's happening there. Why boolattr isn't recognized anymore ?
Here the functional Jsbin : http://jsbin.com/qaroqa/1/edit
Last thing, I noticed that once ready the HTML would look like this
<any-name boolattr=""></any-name>
I guess what happen is that attr1 is interpreted as a boolean (because hinted as one) and reflected back as a falsy boolean which mean removed. Is that correct ?
Related
I want to use a java-script method in a polymer Template. I am using Vaadin with Polymer Elements. In my Project I have a Vaadin-Grid of Objects that can be of different type. I want to render these types with different Templates.
This problem can be solved with a dom-if template, as described by ollitietavainen in this answer
This works perfectly, but there is a problem. When using more than two different Types of Objects in the Grid, one would need to use the same amount of booleans to set that up. Suppose we have a fictional shop that displays PC-Parts, and each type of PC-Part needs to be rendered with its own template, then we would need something like the fallowing. This is quite cumbersome.
private boolean isMemory(AbstractPcPart pcPart) {
return pcPart.getClass().equals(Memory.class);
}
private boolean isGraphicsCard(AbstractPcPart pcPart) {
return pcPart.getClass().equals(GraphicsCard.class);
}
private boolean isCPU(AbstractPcPart pcPart) {
return pcPart.getClass().equals(CPU.class);
}
// … is-checker for all other types of pcParts.
private void initColumn() {
addColumn(Objects.requireNonNull(CardFactory.getTemplate())
.withProperty("partCard", CardFactory::create)
.withProperty("isMemory", this::isMemory)
.withProperty("isGraphicsCard", this::isGraphicsCard)
.withProperty("isCPU", this::isCPU)
// add all other properties
);
}
The corresponding Templates would look something like this.
<template is='dom-if' if='[[item.isMemory]]'>"
<memory-card part-card='[[item.partCard]]'>
</memory-card>"
</template>
<template is='dom-if' if='[[item.isGraphicsCard]]'>"
<graphics-card part-card='[[item.partCard]]'>
</graphics-card-card>"
</template>
<template is='dom-if' if='[[item.isCPU]]'>"
<cpu-card part-card='[[item.partCard]]'>
</cpu-card>"
</template>
<!-- one additional template for every type of part -->
The question now is, if there is any other way, that would not be needing all these Properties.
Luckily there is, as Kuba Šimonovský explained in an answer to another question.
Using this method we could rewrite the code from above to something like the fallowing.
private String type(AbstractPcPart pcPart) {
return pcPart.getClass().getSimpleName();
}
private void initColumn() {
addColumn(Objects.requireNonNull(CardFactory.getTemplate())
.withProperty("partCard", CardFactory::create)
.withProperty("type", this::type));
}
This time we use a java-script method to conditionally select the corresponding template.
<template is='dom-if' if='[[_isEqualTo(item.type, "Memory")]]'>"
<memory-card part-card='[[item.partCard]]'>
</memory-card>"
</template>
<template is='dom-if' if='[[_isEqualTo(item.type, "GraphicsCard")]]'>"
<graphics-card part-card='[[item.partCard]]'>
</graphics-card-card>"
</template>
<template is='dom-if' if='[[_isEqualTo(item.type, "CPU")]]'>"
<cpu-card part-card='[[item.partCard]]'>
</cpu-card>"
</template>
<!-- one additional template for every type of part -->
The Polymer Template is a bit more complicated now, but on the java side, the code is much shorter, and possibly easier to maintain. There is probably still some overhead, as every template gets added to the dom. But in addition to that only the content from the templates that we want to see gets added to the dom.
I don’t think there is a better way to do this though.
So using this method, we need a java-script method called _isEqualTo. This method is not a standard method so we need to implement it ourselves. The implementation for this method is straightforward.
function _isEqualTo(one, other) {
return one == other;
}
But the answer from Kuba does not specify where to implement this method. I have tried to put the method in different places with no luck. The js console in my browser always complains that it can not find the method.
Digging a little bit deeper I found this Link. So maybe what i want to have is a global variable.
window._isEqualTo = function(one, other) {
return one == other;
}
But even with this change the same warning persists. What’s weird is that the function is visible in the interactive console in the developer tools. Setting a breakpoint in the java-script file that i have added the function; and calling the function in the console reveals that it is really the correct function that get’s called, leading me to beleave that the function gets initialized too late in the lifecycle of the application. Although I am not sure at all.
And because the function is not found, the grid in the view will be empty. It still shows the rows, but they don’t show content.
I really hope someone can help me out.
Here is a Git-Repository to reproduce my problem. The concerning views are the PartsDomIfView and the PartsDomIfElegantView.
Instead of using the deprecated TemplateRenderer, you could create a LitRenderer (v22+) and create a custom lit component that can be used there as your column's content. In there you could create complex logic based templates as a separate component, that can be better maintained.
I was digging a bit into the Polymer 1.0 elements and I am a little curious about the computed properties.
For example, in paper-drawer-panel.html,
<dom-module id="paper-drawer-panel" …>
…
<div id="main" style$="[[_computeDrawerStyle(drawerWidth)]]">
…
</div>
…
</dom-module>
<script>
Polymer({
is: 'paper-drawer-panel',
…
_computeDrawerStyle: function(drawerWidth) {
return 'width:' + drawerWidth + ';';
},
…
</script>
drawerWidth is a property of paper-drawer-panel, so why is it so important to explicitly include it in the parameters of the computed property?
Is
[[_computeDrawerStyle()]]
…
_computeDrawerStyle: function () {
return 'width:' + this.drawerWidth + ';';
}
Is this bad practice?
Explicit arguments in computed bindings serve an important purpose: telling Polymer which properties the computed binding depends on. This allows Polymer to know when to recalculate and update the computed binding.
Take [[_computeDrawerStyle()]], for example. In this case, Polymer has no idea what other properties the computed binding depends on, and will only calculate it once on load.
As soon as you add drawerWidth explicitly ([[_computeDrawerStyle(drawerWidth)]]) Polymer now knows that it should run the computed binding again for a new value every time drawerWidth changes.
I think you are confused. What you are referring to in your code example here style$="[[_computeDrawerStyle(drawerWidth)]]" is a call to a private function called _computeDrawerStyle and of course you need to explicitly give it the right parameters. Check the documentation here to learn about computed properties.
Polymer has two separate concepts and you are confusing them.
Computed properties. These are properties that depend on other ones and are recalculated whenever their components changed. You can then databind the value of that computed property as a property value. <paper-draw-panel> does NOT have a computed property (I checked the code).
Function calls referenced in the databinding (which is what _computeDrawStyle) is. This causes Polymer to call that function (of the element) when ever any of its parameters changed. The parameters are all properties (or you can use subproperties of objects and indexes of arrays) This is what is happening here.
In Polymer (0.5.2) it appears that global HTML attributes, like lang, cannot be cleanly observed? For instance:
<polymer-element name="x-foo" attributes="lang bar">
..
<script>
Polymer({
langChanged: function () .. // never fires by itself
barChanged: function () .. // fires just fine
});
</script>
</polymer-element>
The interesting part is that the callback does fire if the element is dirtied in other ways. For example:
..
ready: function () {
this.lang = 'en';
}
..
This does not trigger any callback. However:
..
ready: function () {
this.lang = 'en';
this.bar = 'baz';
}
..
This fires both callbacks. So it appears that Polymer isn't correctly being notified about changes to "native" attributes? Is this a known issue? Can this be worked around?
The team advises folks to not define properties/attributes that have the same name as native DOM properties/attributes. link
Avoid defining a property or method with the same name as a native DOM property or method, such as id, children, focus, title and hidden; the results are unpredictable.
This is because Object.observe cannot actually observe those properties. They're coming from the C++ black box inside the browser.
My advice would be to use language instead of lang.
I cant seem to recreate this. What browser are you using?
http://jsfiddle.net/6sqo159z/16/
I had a few similar issues when I used Polymer extensively. Namely on IE.
Are you using the latest version of polymer and platform?
Try giving your attributes default values, even if those values are undefined.
Try including a publish object for your attributes.
publish:{
bar:"",
lang:""
}
Either I am doing something horribly wrong or Polymer just doesn't like me. See following:
<polymer-element name="menu-paper-ui" noscript>
<template>
<paper-dialog heading="Dialog" transition="paper-dialog-transition-bottom">
[ .. ]
</paper-dialog>
<paper-button label="Dialog Bottom" on-tap="{{toggleDialog}}"></paper-button>
</template>
<script>
Polymer('menu-paper-ui', {
toggleDialog : function() {
var dialog = document.querySelector('paper-dialog');
console.log(dialog); //returns null
dialog.toggle();
}
})
</script>
</polymer-element>
Now, I have my reasons to use querySelector. So, if someone can tell me whats going wrong that will be great!
This question is nearly identical to Using querySelector to find nested elements inside a Polymer template returns null.
The short answer is that elements in a polymer-element's template are put into the ShadowDOM of that element, are not not visible to the anything outside of that element. This is so that you can control styling more easily, and element IDs are scoped.
You can either give the dialog an id and use Polymer's automatic node finding, or use this.shadowRoot.querySelector('paper-dialog').
The Problem is that you can not access the shadow DOM inside a custom element with document.querySelector. See my answer to a similar question.
I'm searching for a way to access an attribute on a Polymer custom element from the DOM
or to send data from Polymer.register to the DOM.
This really simple element below takes two values and multiplies them, placing the result in its result attribute.
How can I access this result from the outside?
<element attributes='value times result' name='value-box'>
<template>
<p>{{result}}</p>
</template>
<script>
Polymer.register(this, {
ready: function() {
if (this.value != null && this.times != null) {
this.result = this.value * this.times;
}
}
});
</script>
</element>
result is a property on your element just like times and value. You can access it from outside JS, as you would any property on a normal HTML element. For example:
<value-box value="2" times="10"></value-box>
<script>
document.querySelector('value-box').result;
</script>
Internal to your element, what you want is to keep the result computed property up to date as times/value change. There are a couple of ways to do that. One is to use <property>Changed watchers [1]:
<element name="value-box" attributes="value times result">
<template>
<p>result: {{result}}</p>
</template>
<script>
Polymer.register(this, {
valueChanged: function() {
this.result = this.value * this.times;
},
timesChanged: function() {
this.result = this.value * this.times;
}
});
</script>
</element>
Demo: http://jsbin.com/idecun/2/edit
Alternatively, you can use a getter for result:
Polymer.register(this, {
get result() {
return this.value * this.times;
}
});
Demo: http://jsbin.com/oquvap/2/edit
Note For this second case, if the browser doesn't support Object.observe, Polymer will setup a timer to dirty check result. This is why you see "here" printed in the console for this second example. Run the same thing in Chrome Canary with "Experimental WebKit features" enabled in about:flags, and you won't see the timer. Yet another reason why I can't wait for Object.observe to be everywhere! :)
Hope this helps.
Just wanted to add a useful follow up to this (Even though the question has been answered).
My follow up is in response to the following comment on the actual answer:
I'm curious as to why selection with jQuery didn't work. Does it not recognize Custom Elements? – CletusW Jul 8 '13 at 19:57
The most likely reason jQuery didn't see your element is because it was not fully formed by the browsers run time at that point.
I ran into this problem while developing my ASP.NET MVC + polymer js sample app on my github page, and essentially what I was trying to do was call methods and access properties on my polymer object before polymer had made everything usable.
Once I moved the code I was using into a button click (So I could trigger it manually after I visually could see my component was ready) everything worked fine.
For now, if you try to access anything too soon, EG: in your jQ doc.ready handler, there's a good chance you'll run into all sorts of daft problems like this.
If you can find a way of delaying your action, or even better using polymer signals to signal from the components ready handler to an outside agent, that sets a flag telling you the component is ready, then you can sort this easily.