Server response to polymer property values - google-apps-script

<dom-module id="payment-list">
<template>
<template is="dom-repeat" items="{{clients}}">
<paper-item>
<span>{{item.Name}}</span>
|
<span>{{item.Amount}}</span>
</paper-item>
</template>
</template>
<script>
Polymer({
is: 'payment-list',
properties: {
clients: {
notify:true,
type: Array,
value: [{Name:'A', Amount:'100'},
{Name:'B', Amount:'200'}]
}
},
handleComplete: function(NewValues){
/***********/alert(NewValues);/***********/
},
ready: function(){
google.script.run.withSuccessHandler(this.handleComplete).GS_GetClients();
}
});
</script>
</dom-module>
I am using google.script.run to communicate with GAS function GS_GetClients(). GS_GetClients will be returning an object and I am trying to bind this new values to the property 'clients'.
When I do the alert I see that new values are passed to the handleComplete function from the server side GAS function. But I am not able assign the new values to the property 'clients'.
I cant set the values by using this.clients = NewValues. This is making the value to undefined.

The call to google.script.run is asynchronous.
The result seems to return, when the rendering of {{clients}} has already happend.
So in your success handler handleComplete(..) you somehow have to tell Polymer to redraw.
I do not know Polymer but from the docs Polymer data binding it seems as if you can do it like this :
Polymer({
...
setClients: function(clients) {
this.clients = clients;
// Notification required for binding to update!
this.notifyPath('clients', this.clients);
}
});
How to call custom methods in Polymer is explained here member-functions , sorry can not provide a more detailed answer regarding Polymer.

Related

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.

Initializing dynamic default values for sub-elements on creation

I'm trying to work out how to make dynamic values available to sub-elements before the local DOM is initialised. I've created a custom element with a Google Map element embedded in it:
<dom-module id="place-picker">
<template>
<google-map api-key="..." />
</template>
</dom-module>
Polymer({
is: "place-picker",
created: function() {
this.apiKey = someFunctionToRetreiveApiKey()
}
})
I want to provide the API key to the google-map element immediately. It needs to be before the DOM is initialised because the google-map element attempts to load immediately. But Polymer's properties are not evaluated . Using data binding api-key="[[apiKey]]" doesn't work because I haven't declared it as a property.
I worked it out in a slightly hacky fashion. The context of the created callback is a raw HTML element, so you can simply set its attribute, which for some reason is evaluated as a property before local DOM creation. I ended up with:
# CoffeeScript
Polymer
is: "place-picker"
properties:
googleWebApiKey:
type: String
created: ->
#setAttribute("google-web-api-key", functionToRetrieveApiKey())
# Slim HTML
template
google-map api-key="[[googleWebApiKey]]"
I prefer to use the "value" key of the properties object to set the value, but is should work similar to the created function mentioned above, you could also you the computed key.
<dom-module id="place-picker">
<template>
<google-map api-key="[[apiKey]]" />
</template>
</dom-module>
Polymer({
is: "place-picker",
properties : {
apiKey : {
type: String,
value: functionToRetrieveApiKey()
}
}
})

Polymer - ready event but bounded data not ready

In the todo-list app from PolymerLabs , I added a ready() function in todo-data.html as follows:
<dom-module id="todo-data">
<template>
<style>
</style>
<firebase-collection location="{{userLocation}}"
data="{{fbTodos}}"
on-firebase-value="_firebaseLoaded">
</firebase-collection>
.....
</template>
<script>
Polymer({
is: 'todo-data',
properties: {
todos: {
notify: true
},
user: {
observer: '_userChanged'
},
.......
ready: function() {
console.log(this.user);
}
});
The user attribute value is bounded to it from index.html as follows:
//index.html
<template is="dom-bind" id="app">
<todo-auth id="auth"
user="{{user}}"
location="[[firebaseURL]]"
user="{{user}}">
</todo-auth>
<todo-data location="[[firebaseURL]]"
todos="{{todos}}"
user="{{user}}">
</todo-data>
..........
May I know why user is always printed out as undefined in ready()? It seems that when ready() is called, the bounded data is not yet populated with the correct data. When can i reliably know when a bounded data has been initialised?
When ready() is called this only means that Polymer is done creating and initializing the elements. The call to Firebase is only just invoked at that time and Polymer doesn't wait for the call to the Firebase server to respond. This is done by code in <firebase-collection> Polymer is not aware of.
When data arrives from the server it is passed to fbTodos
Create a property fbTodos and and observer that is executed when fbTodos changes. This observer will be called when the data arrive.
properties: {
fbTodos: {
observer: '_dataArrvied'
},
user: {
observer: '_userChanged'
},
The <firebase-collection> element doesn't provide an firebase-value event therefore
on-firebase-value="_firebaseLoaded"
seems redundant.
I haven't used this element myself yet.

Polymer 1.0: <iron-meta> usage

Proper usage of the Polymer 1.0 element <iron-meta> is confusing. Here is the link on Github. And here is the link to the Polymer demo site.
Can someone please provide a proper code example of how to make it work?
This is the code I have so far.
<dom-module id="generic-element">
<style>...</style>
<template>
<iron-meta id="meta" key="info" value="foo/bar"></iron-meta>
The <code>value</code> stored at <code>key="info"</code> is <code><span>{{test}}</span></code>.
</template>
</dom-module>
<script>
(function() {
Polymer({
is: 'generic-element',
properties: {
test: {
value: function(){
return "Hello world"; // This is the only thing I can get to work so far.
// return (new Polymer.IronMetaQuery({key: 'info'}).value); // Doesn't totally break.
// All my other below attempts totally fail. Everything breaks.
// return this.$.meta.IronMetaQuery({key: 'info'}).value;
// return this.IronMetaQuery({key: 'info'}).value;
// return this.$.meta.byKey('info').getAttribute('value');
// return this.$.meta.byKey('info').value;
}
}
}
});
})();
</script>
Here is the Github link to the issue. And here is a Github repository that contains the complete problem code in context of the complete web app.
The issue with your code is that you are trying to set your element property's default value to something that's declared inside that same element's template itself. Two of the things that happen between the time when the element is created and when that element is attached include a) properties' default values are set; and b) the template undergoes preparations to be stamped into DOM. These tasks happen asynchronously so in essence you are generating a race condition.
Try setting your test default value inside the ready() callback - the ready() callback guarantees that DOM is ready to be accessed, which in your case is exactly where you declared your <iron-meta> key.
<dom-module id="generic-element">
<style>...</style>
<template>
<iron-meta id="meta" key="info" value="foo/bar"></iron-meta>
The <code>value</code> stored at <code>key="info"</code> is <code><span>{{test}}</span></code>.
</template>
</dom-module>
<script>
(function() {
Polymer({
is: 'generic-element',
properties: {
test: String
},
ready: function () {
// this will work
this.test = this.$.meta.byKey("info");
}
});
})();
</script>
jsbin: http://jsbin.com/vosekiwehu/edit?html,output

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.