Using polymer ajax response - javascript

I have the following polymer element which I have created:
<link rel="import" href="../bower_components/iron-ajax/iron-ajax.html">
<dom-module id="task-list-app">
<style>
:host {
}
</style>
<template>
<iron-ajax auto url="../tasks.json" handle-as="json" on-response="handleResponse"></iron-ajax>
<template is="dom-repeater" items="{{todos}}">
<span>hello</span>
</template>
</template>
</dom-module>
<script>
Polymer({
is: "task-list-app",
created: function () {
this.todos = [];
},
handleResponse: function (data) {
this.todos = data.detail.response;
}
});
</script>
I am calling this inside my index.html by doing:
<task-list-app></task-list-app>
I am expecting that for every object returned in the todo array, a <span> will be printed. However, when I run the app, I get the following output in the console:
Uncaught TypeError: Cannot read property 'todos' of undefined
in polymer.html line 1001
I am not sure what is happening here and how to reference the data received back from the ajax response.

First of all second template that you are using to loop through your data should be a "dom-repeat" and not a "dom-repeater". Secondly you can directly bind the response of iron-ajax to your looping template. Like this,
<link rel="import" href="../bower_components/iron-ajax/iron-ajax.html">
<dom-module id="task-list-app">
<style>
:host {
}
</style>
<template>
<iron-ajax auto url="../tasks.json" handle-as="json" last-response="{{ajaxResponse}}"></iron-ajax>
<template is="dom-repeat" items="[[ajaxResponse.todos]]">
<span>{{item.todoItem}}</span>
</template>
</template>
</dom-module>
<script>
Polymer({
is: "task-list-app"
});
</script>
So you are basically binding value of last-response property to your looping template directly.

After banging my head on the wall for a few hours I have managed to solve this. I have created my own element called ajax-service that has a public property called todos which is an Array. In this element, I use the iron-ajax element to do the ajax call.
When the ajax is complete, a function is called and the response is set on the todos property. I have also set the keys reflectToAttribute and notify to true. This means the todos property's value is reflected back to the attribute on the host node and that it is available for two-way binding (see here for more information).
My task-list-app element is as follows:
<link rel="import" href="ajax-service.html">
<link rel="import" href="task-item.html">
<link rel="import" href="tiny-badge.html">
<dom-module id="task-list-app">
<style>
:host {
}
</style>
<template>
<ajax-service todos="{{todos}}"></ajax-service>
<template is="dom-repeat" items="{{todos}}">
<task-item task="{{item}}"></task-item>
</template>
<div>
<tiny-badge>[[todos.length]]</tiny-badge> total tasks
</div>
</template>
</dom-module>
<script>
Polymer({
is: "task-list-app"
});
</script>
and my ajax-service element:
<link rel="import" href="../bower_components/iron-ajax/iron-ajax.html">
<dom-module id="ajax-service">
<style>
:host {
}
</style>
<template>
<iron-ajax auto url="../tasks.json" handle-as="json" on-response="tasksLoaded"></iron-ajax>
</template>
</dom-module>
<script>
Polymer({
is: "ajax-service",
properties: {
todos: {
type: Array,
reflectToAttribute: true,
notify: true
}
},
attached: function () {
this.todos = [];
},
tasksLoaded: function (data) {
this.todos = data.detail.response;
}
});
</script>
Doing it this way means I am able to edit the data in the on-response function before setting it on the element.

You need to define property before using it in scripts:
<script>
Polymer({
is: "task-list-app",
properties: {
todos: {
type: Array,
notify: true
}
},
handleResponse: function (data) {
this.todos = data.detail.response;
}
});
</script>

Related

Polymer: Updating Templatized template when parent changes

Following this question, I'm trying to get full two-way binding between parent and template when using Polymer.Templatizer. I'm not sure what the problem is _forwardParentProp and _forwardParentPath are never called when a property changes on parent. I think it's the reason why my templates don't update.
Here's my code (also available here). As you can see dom-if binds both ways and the two templates (one created dynamically other declared inside custom element) only update parent but aren't themselves updated.
Polymer({
is: "my-app",
behaviors: [Polymer.Templatizer],
properties: {
global: {
value: {
text: 'i'
},
notify: true
}
},
ready: function() {
this.count = 0;
// this ensures that global.text is updated when text changes
this._instanceProps = {
text: true
};
this.addHeadline(this.$.template);
this.addHeadline(this._dynamicTemplate());
},
addHeadline: function(template) {
this.templatize(template);
var clone = this.stamp({
text: this.global
});
// Append to my-app
Polymer.dom(this.root).appendChild(clone.root);
},
_dynamicTemplate: function() {
var template = document.createElement("template");
var templateRoot = document.createElement('div');
templateRoot.innerHTML = '<h1>{{text.text}}</h1><input type="text" value="{{text.text::input}}" />';
// you cannot just set innerHTML in <template>
template.content.appendChild(templateRoot);
return template;
},
_forwardInstanceProp: function(inst, p, val) {
console.log('_forwardInstanceProp');
},
_forwardInstancePath: function(inst, p, val) {
// notify parent's correct path when text.text changes
this.set(p.replace(/^text/, 'global'), val);
console.log('_forwardInstancePath');
},
_forwardParentProp: function(prop, value) {
console.log('_forwardParentProp');
},
_forwardParentPath: function(prop, value) {
console.log('_forwardParentPath');
}
});
<!DOCTYPE html>
<html>
<head>
<base href="https://polygit.org/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link href="polymer/polymer.html" rel="import"/>
<link rel="import" href="paper-input/paper-input.html"/>
</head>
<body>
<dom-module id="my-app">
<template>
<paper-input label="global.text" value="{{global.text}}"></paper-input>
<p>global.text: <{{global.text}}></p>
<template id="template">
<h1>{{text.text}}</h1><input type="text" value="{{text.text::input}}" />
</template>
<template if="true" is="dom-if">
<div>
dom-if:
<input type="text" value="{{global.text::input}}" />
</div>
</template>
</template>
</dom-module>
<my-app></my-app>
</body>
</html>

Event Handling between parent and child page in polymer 1.0

I have a parent polymer element called parent-page and a child element called child-page.
parent-page calls child page and passes an array with it. for e.g, in parent page:
<child-page items={{itemsArray}}></child-page>
Now, on the basis of certain activity child page fires an event with a new array.
eg, in child page:
this.fire('eventPerformed', newArray);
This array is being listened by the parent page and received with expected values.
Now, I want to pass that new array to the child page such that the child-page is rendered according to the new array.
How to achieve it?
Edit: my child page looks like this.
<dom-module id="child-page">
<style>
</style>
<template>
<template is="dom-repeat" items="{{itemArray}}" as="fooditem">
<div class="horizontal layout">
<div>{{fooditem.quantity}}</div>
<div>{{fooditem.name}}</div>
</div>
</template>
<paper-button on-click"changeArray"> ChangeArray</paper-button>
</template>
<script type="text/javascript">
Polymer({
is:'child-page',
properties:{
itemArray:Array
},
changeArray:function(){
this.itemArray=<<Some new Array>>
this.fire('eventPerformed',newArray);
}
});
</script>
</dom-module>
Is there any way I can call the template repeat with the new array in the same child page? Or do I have to fire an event to the parent-page and call the child page again? How to achieve it either way?
The child-page re-renders its template is="dom-repeat" items="[[itemArray]]" automatically whenever its itemArray property was updated.
Just add notify: true to the itemArray property in child-page to enable two-way-binding with the parent-page element. Then the parent-page is also notified whenever the item-array of child-page has changed (see the Polymer documentation on this topic).
Here is a small complete example:
<dom-module id="child-page">
<template>
<template is="dom-repeat" items="[[itemArray]]">
<div>[[item]]</div>
</template>
<div on-click="_changeArray">Change</div>
</template>
<script>
Polymer({
is: 'child-page',
properties: {
itemArray: {type: Array, notify: true}
},
_changeArray: function() {
this.itemArray = [4,5,6,7,8,9];
}
})
</script>
</dom-module>
<dom-module id="parent-page">
<template>
<span>Item count: </span><span>[[itemArray.length]]</span>
<child-page item-array="{{itemArray}}"></child-page>
</template>
<script>
Polymer({
is: 'parent-page',
properties: {
itemArray: {type: Array, value: [1,2,3]}
}
})
</script>
</dom-module>
<parent-page></parent-page>
Btw. note my usage of {{...}} and [[...]]. Use curly braces for two-way bindings and square brackets for one-way bindings.

Data binding between sibling custom elements

There are two child custom elements inside parent custom element. I don't know if it's even possible, but what I am trying to achieve is when "value" changes, and "prop" changes as a result of binding, I need "feat1" to change accordingly equating to the value of "prop".
parent-element:
<dom-module id="parent-element">
<template>
<first-child prop={{value}}></first-child>
<second-child feat1={{prop}}></second-child>
In parent-element
<h1>{{value}}</h1>
</template>
<script>
Polymer({
is: "parent-element",
properties: {
value: {
type: String
}
},
valueChanged:function(){
console.log("value changed");
}
});
</script>
</dom-module>
first-child:
<dom-module id="first-child">
<template>
<p>first element.</p>
<h2>{{prop}}</h2>
</template>
<script>
Polymer({
is: "first-child",
properties:{
prop:{
type:String,
notify:true
}
},
ready:function(){
this.prop = "property"; //this is just example, in reality it gets data from elsewhere
}
});
</script>
</dom-module>
second-child:
<dom-module id="second-child">
<template>
<p>Second element.</p>
<h2>{{feat1}}</h2>
</template>
<script>
Polymer({
is: "second-child",
properties:{
feat1:{
type:String,
notify:true,
value:"initial value"
}
},
ready:function(){
this.addEventListener("feat1-changed",this.myAct);
},
myAct:function(){
console.log("feat1-changed ",this.feat1);
}
});
</script>
</dom-module>
Here is the plunk
Sure you can. Some issues with your code:
From the parent element, bind prop in <first-child> and feat1 in <second-child> to the same value.
Use observer defined in the properties object to listen for changes.
Set up <first-child> to be a one-way bind from child to parent.
Set up <second-child> to be a one-way bind from parent to child.
Putting it all together:
<parent-element>:
<dom-module id="parent-element">
<template>
<first-child prop={{value}}></first-child>
<second-child feat1=[[value]]></second-child>
<div>parent-element: <b><span>[[value]]</span></b></div>
<div id="log"></div>
</template>
<script>
Polymer({
is: "parent-element",
properties: {
value: {
type: String,
observer: "valueChanged"
}
},
valueChanged: function (n, o) {
var el = document.createElement("div");
el.innerHTML = "value changed in parent from "+o+" to "+n;
Polymer.dom(this.$.log).appendChild(el);
}
});
</script>
</dom-module>
<first-child>:
<template>
<div>first-child: <b><span>{{prop}}</span></b>
<button on-tap="btnTapped">change 'prop' inside first-child</button>
</div>
</template>
<script>
Polymer({
is: "first-child",
properties:{
prop:{
type:String,
notify:true,
value: "first_child_default"
}
},
btnTapped: function () {
this.prop = Math.random().toString(36).substring(7);
}
});
</script>
</dom-module>
<second-child>:
<dom-module id="second-child">
<template>
<div>second-child: <b><span>{{feat1}}</span></b></div>
<div id="log"></div>
</template>
<script>
Polymer({
is: "second-child",
properties:{
feat1:{
type:String,
value:"second_child_default", // you should never see this since default value is passed in from parent
observer: "feat1Changed"
}
},
feat1Changed: function(n, o) {
var el = document.createElement("div");
el.innerHTML = "feat1 changed in second-child from "+o+" to "+n;
Polymer.dom(this.$.log).appendChild(el);
}
});
</script>
</dom-module>
Plunker: http://plnkr.co/edit/pONoTxdCDn6jj5axoap1?p=preview
Let me know if this is the correct behaviour you are trying to achieve.

Polymer data binding -

Can someone tell me where i'm going wrong? I have an element system-menu that queries for data using iron-ajax then i'm supposed to bind that data to my template.
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/iron-menu-behavior/iron-menubar-behavior.html">
<dom-module id="system-menu">
<template>
<iron-ajax
auto
url="{{url}}"
params='{"ajax":"true"}'
handle-as="json"
on-response="dataLoaded"
debounce-duration="300">
</iron-ajax>
<div class="content"><content></content></div>
</template>
</dom-module>
<script>
(function() {
Polymer({
is: 'system-menu',
behaviors: [
Polymer.IronMenubarBehavior
],
ready: function() {
},
properties: {
data: {
type: Object,
reflectToAttribute: true,
notify: true
}
},
attached: function () {
this.data = [];
},
dataLoaded: function (data) {
this.data = data.detail.response;
}
});
})();
</script>
`
This is how I use the element,
<link rel="import" href="/themes/_components/custom_components/system-menu/system-menu.html">
<system-menu class="list" url="www.some.site/widget/system-menu-widget/-menuRequest/">
<template is="dom-repeat" items='{{data}}'>
<li>{{item.label}}<li>
</template>
</system-menu>`
For some reason, the {{data}} isnt binding anything. It just shows {{data}} in the chrome inspect element console
To access the data loaded from the system-menu outside of the system-menu element, you need to add it as an attribute:
<system-menu class="list" url="www.some.site/widget/system-menu-widget/-menuRequest/" data="{{data}}">

Bind a value between a parent and a child element where child element is created using javascript

Using Polymer, does anyone know how to bind a value between a parent and a child element?
Below is my attempt however it doesn't work.
Note: child-component needs to be created using JavaScript.
<dom-module id="host-component">
<template>
<div>{{sharedValue}}</div>
<div id="childComponentContainer">
<!-- CHILD-COMPONENT GETS CREATED HERE -->
</div>
</template>
<script>
Polymer({is:'host-component',
properties:{
sharedValue:{
type: String,
notify:true,
observer: 'sharedValueChanged'
}
},
attached: function(){
var newElement = document.createElement('child-component');
Polymer.dom(this.$.childComponentContainer).appendChild(newElement);
},
sharedValueChanged:function(d){
console.log(d, ", said the child");
}
});
</script>
</dom-module>
<dom-module id="child-component">
<template>
<input is="iron-input" bind-value="{{sharedValue}}" />
</template>
<script>
Polymer({is:'child-component',
properties:{
sharedValue:{
type: String,
notify:true,
reflectToAttribute:true,
}
},
});
</script>
</dom-module>
Thanks :)
After re-reading Polymer's documentation (many times) I found a section about how Two-way data-binding events work where by on each change Polymer fires a DOM event. From this I came up with the work around below.
You'll notice this version also has two way binding so the host can change the child's value and the child can change the host's value!
<dom-module id="host-component">
<template>
<div>Host: <span>{{sharedValue}}</span></div>
<div>Host: <span><input is="iron-input" bind-value="{{sharedValue}}" /></span></div>
<div id="childComponentContainer">
<!-- CHILD-COMPONENT GETS CREATED HERE -->
</div>
</template>
<script>
Polymer({is:'host-component',
properties:{
sharedValue:{
type: String,
notify:true,
value: "Unchanged",
observer: 'sharedValueChanged'
}
},
attached: function(){
this.$.childComponent = document.createElement('child-component');
var host = this;
//Listens to the child's sharedValue. When changed it will update host's sharedValue.
this.$.childComponent.addEventListener("shared-value-changed", function(e){
host.sharedValue = this.sharedValue;
});
Polymer.dom(this.$.childComponentContainer).appendChild(this.$.childComponent);
},
//Observes the hosts's sharedValue. When changed it will update child's sharedValue.
sharedValueChanged: function(value){
if (this.$.childComponent) {
this.$.childComponent.sharedValue = value;
}
}
});
</script>
</dom-module>
<dom-module id="child-component">
<template>
<div>Child: <span>{{sharedValue}}</span></div>
<div>Child: <span><input is="iron-input" bind-value="{{sharedValue}}" /></span></div>
</template>
<script>
Polymer({is:'child-component',
properties:{
sharedValue:{
type: String,
notify:true,
value:"Unchanged",
reflectToAttribute:true,
}
}
});
</script>
</dom-module>

Categories

Resources