I am reviewing polycasts ep 47 app code for blog-pages.html, specifically the data binding.
In blog-pages.html, the host properties isLoading, postsActive, and postsData were not declared as blog-pages host properties. How was this possible since they are blog-pages host properties?
<app-route route="{{route}}"
pattern="/posts/:slug"
active="{{postsActive}}"
data="{{postsData}}"></app-route>
<list-page category="{{category}}"
active={{listActive}}
loading="{{isLoading}}"></list-page>
<post-page post="{{postsData.slug}}"
active="{{postsActive}}"
loading="{{isLoading}}"></post-page>
<div class="overlay" hidden$="[[!isLoading]]">
<paper-spinner active="[[isLoading]]"></paper-spinner>
</div>
</template>
<script>
Polymer({
is: 'blog-pages',
observers: [
'_lockScroll(isLoading)'
],
_lockScroll: function(isLoading) {
if (isLoading) {
document.body.style.overflow = 'hidden';
} else {
document.body.style.overflow = 'visible';
}
}
});
</script>
</dom-module>
This is not best example of how polymer works.. For beginners it might be confusing. All these variables you wrote are defined by some polymer's elements.
For example in blog-app.html you can see app-route which has property data, tail and pattern. When you look inside app-route.html file, you can see that property tail has notify: true option. Which means, that it will propagate this property to parent element. So when you define binding in app-route like: <app-route tail="{{someProp}}"></app-route> then in your element there will be existing someProp property.
The same is in other files. For example take a look at isLoading. In blog-pages.html you can see there is binding to isLoading but it is nowhere defined. Well it is. In child element. So open list-page.html and take a look at <iron-ajax> where you can see loading="{{loading}}". And again the same. take a look inside iron-ajax.html and find property loading. What you see? again notify: true option. Which means, it get propagated to parent (list-page.html) and in list-page.html you can see there is also defined loading property which has notify:true so it gets propagated once more to parent (blog-pages.html) And because in blog-pages.html we have binding loading="{{isLoading}}" then it's saved into isLoading property
And this is how polymer works.. This example of some project is really hard to udnerstand and for beginners it's impossible. I hope that my explanation helped you a little bit, but I think you are now much more confused :-D.
If you have more questions, then ask. It's no problem to explain something
Related
This is a very newbie question, but how can I reference a polymer property from Chrome's console? I know I can output (console.log) from javascript in my application with a reference to this.myProperty, but how do I get a reference to it straight from the console?
You get a reference to the element by some method, I use querySelector so if you have a paper-input with the class username-input you can do something like this
document.querySelector('paper-input.username-input').myProperty
and that would be the same as doing a this.myProperty from inside the element.
During development only I create a property on window that references my
element.
Polymer({
is: 'nav-bar',
properties: {
foo: {
type: String,
value: 'foo'
}
},
attached: function() {
// #TODO remove before merge.
window.navBar = this
}
})
And in your console:
// log property
navBar.foo
// call methods
navbar.doSomething()
to check properties or call methods on my element.
Granted, this pollutes the global scope but I do this only during development.
You can also select the element via querySelector('nav-bar') but it's tedious
to do so and you lose console autocompletion. Time is money.
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!
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.
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.
I have a little bit of markup that looks like this:
<polymer-element name="blog-post" noscript>
<template>
<mark-down>
<textarea value="{{post}}"></textarea>
</mark-down>
<polymer-localstorage name="my-blog-editor" value="{{post}}">
</polymer-localstorage>
</template>
</polymer-element>
I'd like the mark-down tag to be able to see the value of the textarea but I'm having a hard time knowing when to query for it.
Inside of mark-down my code looks something like this:
attached: function() {
this.textarea = this.$.textareaContent.getDistributedNodes()[0]; // this grabs the textarea element
// create an observer instance
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
console.log(mutation);
});
});
// pass in the target node, as well as the observer options
observer.observe(this.textarea, { attributes: true });
}
Unfortunately the mutation observer never fires. I've tried checking for the value of textarea directly in attached and domReady but it's always null. The only success I've had is to use a setTimeout to check for the value asynchronously.
textarea is specifically difficult to use in this manner, because you cannot observe it's content changing with MutationObservers, only with events.
Worse still, there is no signal at all for setting textarea.value (which is what your binding will do).
If you want to use textarea like this, suggest you expose a property on the mark-down element for binding a value, and listen to events from the textarea to monitor user edits.
<mark-down value="{{post}}">
<textarea></textarea>
</mark-down>