Weird behaviour of observers in polymer? - polymer

I have a list of questions on the way my code is behaving, with regards to observers
QUESTIONS
1 - Why is the observer alerting when i refresh the page ?
2 - Why am i getting the alert twice when i refresh the page ?
3 - Why the value in alerts vary,
a - first alert - [1,2,3,4,5]
b - secon alert - 1,2,3,4,5
4 - i don't what the observer being called untill unless something is changed manualy, i don't want it to be called on the page refresh
5 - What does ::input do ?
CUSTOM ELEMENT
<link rel="import" href="../bower_components/polymer/polymer.html">
<dom-module id="icon-toggle-second-demo">
<template>
<style>
</style>
<br>
<input type="text" value="{{first::input}}" >
second element
<button>Reset</button>
{{first}}
{{asdf}}
</template>
<script>
Polymer({
is: "icon-toggle-second-demo",
properties: {
'first': {
type: Array,
reflectToAttribute: true,
value: "[1,2,3,4,5]"
},
'second': {
type: String,
notify: true,
readOnly: false,
value: "default"
}
},
observers:[
'changedEvent(first.*, 0)', 'con()'
],
changedEvent: function(changeRecord, index){
alert(changeRecord.base);
},
con: function(){
console.log("asdf");
}
});
</script>
</dom-module>
PARENT HTML
<!doctype html>
<html>
<head>
<script src="../bower_components/webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="icon-toggle-second-demo.html">
<style is="custom-style">
</style>
</head>
<body>
demo/index.html - parent <br>
<icon-toggle-second-demo ></icon-toggle-second-demo>
<script>
</script>
</body>
</html>

1 - Why is the observer alerting when i refresh the page ?
When binding the observer Polymer will once call the observer as you have provided it some default value.
As per polymer's documentation
Simple observers are fired the first time the property becomes defined (!= undefined), and on every change thereafter, even if the property becomes undefined.
2 - Why am i getting the alert twice when i refresh the page ?
This should be because when you ask Polymer to reflect a property as attribute it tries to sync value between attribute and property. In case of Array/Object as values in attribute are different object then that in property observer gets called upon
3 - Why the value in alerts vary,
That is because first time it is getting interpreted as String(as defined by your value property) and second time its getting interpreted as Array(As per your type property)
4 - i don't what the observer being called until unless something is changed manually, i don't want it to be called on the page refresh
Yes it is possible, you'll have to use simple observer in order to do that
5 - What does ::input do ?
::input helps Polymer to bind non-polymer elements. As a non-Polymer element does not inform when there value changes Polymer uses this annotation to mark properties that needs to be observed.
Below are some of my observation from your code
Why is first of type Array? How will you take array from input tag. As far as i can think input tag will only give you String as input
Why are you using reflectToAttribute. As per Polymer's documentation ReflectToAttribute is very costly. So until you have a reason to use avoid using it
You don't need to keep properties name in quotes. It should be first not 'first'
You have named first as Array but you've assigned it String value.
You should check out Polymer's documentation or some other tutorial for better understanding of how to work with Polymer.

Related

Data Bindings across template tags

I'm wondering, is there a possibility to have databindings "out of" a template? Say I have a <template/>-Tag somewhere which I put into the slot of a different component - that component stamps it to its context. Then I want to bind data from the root element to the <template/>-Tag. Also, event bindings (on-x-changed) don't work, because you can't assign a function which is defined in the hosting component. Any ideas?
Example:
... host
{{boundData}}
<binding-component>
<template>
{{boundData}}
</template>
</binding-component>
I don't see changes when I observe boundData in the hosting component. Is there a way to get around this? Or is firing a custom event my only chance?
If you are looking for binding a property outside of polymer something like from index.html you may bind value with element. an example ; index.html
<dom-bind>
<template>
<binding-component bound-data="{{boundData}}"></binding-component>
</template>
</dom-bind>
<script>
// set a value a string, Number or Object etc.
// Optionally wrap this code into a listener ie;
// window.addEventListener('load', e=> { ...below code ... })
var boundData= document.querySelector('dom-bind');
boundData = {} //
</script>
Now in your binding-component element has a property as boundData
hope its helps or provide more code to understand better.
I've made it work the way dom-if does it, too. Like in dom-if (reference), I'm creating a Templatize-instance which then uses forwardHostProp to handle the "inside"-properties
this.__ctor = Templatize.templatize(template, this, {
mutableData: true,
forwardHostProp(prop, value) {
// handling item updates, item being the only property
// from within the binding component
// everything else is automatically bound by templatize
this.set(prop, value);
this.update(this.item);
},
});
this.__instance = new this.__ctor();
this.root.appendChild(this.__instance.root);
This all happens in connectedCallback.
Because the Templatize-instance is passed this, it's bound to the current context as well.
Good luck!

Pass data to another Polymer component

I am working on a dashboard, in which I have a search panel at the top (let's call it component A), where users can enter a query. The value of this input will change a lot of other components in the dashboard (not only components that are its direct descendants or siblings). I want to send the search value from component A to component B, which should then respond by performing some action with the input value.
I have tried a few things:
Directly calling the function in component B. Haven't been able to get that to work at all.
Manually setting B's local property value and using an observer to trigger a function call. I manager to set the value, but the observer does not trigger.
Using a global variable, which I can easily access across components, but I still can't trigger functions in specific components.
How can I best do this?
I'm relatively new to Polymer, so forgive me if my ideas aren't completely 'Polymerised' :)
Approach 1
<dom-module id="component-B">
<template>
</template>
<script>
Polymer({
is: 'component-B',
properties: {
id: '',
observer: '_idUpdate'
},
_idUpdate: function(){
console.log("HELLO");
}
});
</script>
</dom-module>
<dom-module id="component-A">
<template>
</template>
<script>
Polymer({
is: 'component-A',
idSearch: function() {
var id = this.$.search.value;
document.querySelector('component-B').properties.id = id;
},
});
</script>
</dom-module>
As you want to send data to multiple elements (which might not be siblings of the firing element) you can use any of these two methods
Use iron-signal to fire the signal and then in all the elements where you want the data use iron-signal tag to listen to the signal
<iron-signals on-iron-signal-<signal-name>="<function>"></iron-signals>
You can also use standard HTML method dispatchEvent to fire a signal and then add eventListeners in all the element where you want data.

Polymer 2.0 :Please Explain the how the Observer method is working in this code?

This is the code snippet but I'm not able to understand how the observer method is working
static get properties() {
return {
selected: {
type: Object,
observer: '_selectedChanged'
}
};
}
_selectedChanged(selected, oldSelected) {
if (oldSelected) oldSelected.removeAttribute('selected');
if (selected) selected.setAttribute('selected', '');
}
connectedCallback() {
super.connectedCallback();
this.selected = this.firstElementChild;
}
full code: https://codelabs.developers.google.com/codelabs/polymer-2-carousel/index.html?index=..%2F..%2Findex#3
What is selected and oldselected and how can we do oldSelected.removerAttribute?
Are these objects of elements?
Please elaborate!
selected is property of element. It's value is some HTML element (in this case it's always img i think) so, in selected property there is always saved reference to img somewhere in html. When this property change, function _selectedChanged is called with 2 arguments. first argument is new image that is currently saved in selected and second argument is old image(previous value of selected).
further in tutorial you can see code
const elem = this.selected.nextElementSibling;
if (elem) {
this.selected = elem;
}
where is shown that const elem takes some html element and put it into this.selected.
So inside function _selectedChanged they removed html attribute from old image that was previously selected (so it was visible on screen) and added new html attribute to new image that should be visible on screen for now.
You can imagine that img with attribute selected is the only one that is shown on the screen at the time
I hope you understand my explanation. My english isn't 100% so if you have question, ask me and i can try to explain it more.
EDIT
Some example with binding and observer:
Let's say we have some paper-input which should show some results (articles for example) based on value of this input. So we have some HTML:
<paper-input value="{{search}}" label="Find articles"></paper-input>
this is primitive. Just some paper-input with value stored in search property. inside script tag:
Polymer({
is: 'test-el',
properties: {
search: {
type: String,
observer: "_findResults"
},
articles: {
type: Array
}
},
_findResults() {
this.set("articles", ['firstArticle', 'secondArticle', Math.random()]);
},
});
Explain: we defined property search and articles. Whenever property search changes, it calls function _findResults (because of observer). Function _findResults do only one thing. this.set("articles") is almost same as this.articles =. More about this can be found in documentation. So this.set("articles", ['firstArticle', 'secondArticle', Math.random()]); means it creates array and set it to articles property. and now, when we have some array that is changing everytime user enter some value in paper-input, we can add some HTML to show these articles:
<template is="dom-repeat" items="{{articles}}" as="item">
[[item]] <br>
</template>
I made also fiddle, so you can play with it and understand it a little bit more.
https://jsfiddle.net/2va41sy0/1/
Your question at the beginning was almost same in difference that they stored in some property reference to HTML object and not only just string. This is also about understand some basics of javascript and not polymer

How can I pass a Polymer Object beetwen 2 custom elements

I'm building a new webapp and I need to know how can I pass an object between 2 custom elements in polymer.
In the code below, I set the value of mydata in "my-child-element-1" and I need to see this value in "my-child-element-2"...I think that it's not very hard to do but i'm loosing my mind to find a good solution...
In my opinion, i should create a temporary object in "my-host-element" to share the value but i'm not convinced about this...
This is my code:
<dom-module id="my-host-element">
<template>
<my-child-element-1 mydata="{{mydata}}"></my-child-element-1>
<my-child-element-2 mydata="{{mydata}}"></my-child-element-2>
</template>
<script>
Polymer({
is: "my-host-element",
properties:
{
mydata: {
type: Object
}
}
});
</script>
</dom-module>
Thank you!!
Your example looks like it should work without the host element needing a property, if the property on the child elements are set up correctly. Remember that Polymer's data binding syntax is basically syntactic sugar around firing and handling custom events. So take a look in child element 1 and make sure that you've set the property to notify when changed. For example:
Polymer({
is: "my-child-element-1",
properties: {
mydata: {
type: Object,
notify: true // fire mydata-change CustomEvent on change
}
}
});
Yes, afaik it is correct to have the parent element act as the mediator between the children, which means it needs to have its own property even if its only used for that purpose.

Parent element is ready before its child - Polymer 1.0

This question had been modified to match the actual problem.
The original question mistakingly focused on iron-ajax, please see the original problem below. The question should have been:
Please advice why child iron-ajax element is not ready during the 'ready' callback of my-component defined as follows:
<dom-module id="my-component">
<template>
<link rel="import" href="../../../bower_components/iron-ajax/iron-ajax.html">
<iron-ajax
id="selectionLoader"
url=""
method="GET"
handle-as="json"
debounce-duration="300"
last-response="{{ suggestedOptions }}"
last-error="{{ lastError }}"
verbose=true
>
</iron-ajax>
</template>
</dom-module>
<script>
(function () {
Polymer({
is : 'paper-select',
ready : function() {
console.log(this.$.selectionLoader.generateRequest); // undefined
}
})
})()
</script>
Original question
Original title: 'WebComponentsReady' fires before iron-ajax ready - Polymer 1.0
I need to assign some values to an observed property of a custom component that internally uses iron-ajax with disabled auto - so I need to call .generateRequest on the iron-ajax element. This should happen when host page/component is ready, in order to fetch from the server some defaults based on data in the host component code.
selected is an array property on the component observed like this:
observers: [
'_selectedChanged(selected.splices)' // _selectedChanged calls .generateRequest
]
The observer is triggered by:
window.addEventListener('WebComponentsReady', function() {
document.querySelector('paper-select').selected = [{id : 11855},{id : 11856}];
});
The problem is that WebComponentsReady fires before .generateRequest is available on the iron-ajax. So my component is initialized, _selectedChanged is called, but iron-ajax inside it is missing the method and in fact other properties/methods as well.
I've implemented a "deferred" workaround using setTimeout inside the component and it works like charm but it's obviously not the way. Also everything works if the observer is triggered some time later after the page load, e.g. by user's typing. This shows that the logic works, it's just the timing that is wrong.
What am I missing?
The real issue was having the html imports inside my component's <template>.
The 'wrong' order of events makes sense as iron-ajax is not even registered at the time when its host calls the 'ready' callback.
I've moved the imports outside <dom-module> and now everything works as expected.