Akheel K M Akheel K M - 5 months ago 34
JSON Question

Setting the data-binding value in polymer that was obtained from a promise

I am trying to fetch the schedule data in JSON format, from the server and use that to render the schdule using a dom-repeat.

The code works fine if I hard-code the JSON, but if I set it using fetch, it does not work.

<link rel="import" href="../bower_components/polymer/polymer.html">

<dom-module id="xl-schedule">
<template>

<h1> Excel Schedule </h1>

<template is="dom-repeat" items="{{schedule}}">
<div># <span>{{index}}</span></div>
<div>Event name: <span>{{item.name}}</span></div>
<div>Date: <span>{{item.date}}</span></div>
</template>




<script>
Polymer({
is: 'xl-schedule',
ready: function() {

// this.schedule =
// [
// {
// "name": "Event1",
// "date": "5/10/2016"
// },

// {
// "name": "Event2",
// "date": "5/10/2016"
// },

// {
// "name": "Event3",
// "date": "5/10/2016"
// }
// ];

fetch('resources/schedule.json').
then(function (response) {
return response.json();
}).
then(function (response) {
this.schedule = response;
console.log("response",JSON.stringify(response));
})
.catch(function(err) {
console.log("err",err);
});

}

});





Answer

As @Alan pointed out, in your second then callback:

then(function(response) {
  this.schedule = response; // this === window
})

this does not refer to your Polymer object. The callback is invoked outside your Polymer object's context, where this is not defined. In non-strict mode, that results in this defaulting to the global object, which is the window, so you're actually setting window.schedule. You can verify that with console.log(this) in the callback. MDN provides a good reference on this in regards to function context.

Arrow functions (in ES6) don't have that problem, and this would be bound to the enclosing context's this (the Polymer object):

then(response => this.schedule = response) // this === Polymer obj

Assuming you're using ES5 (or prefer not to use an ES6-to-ES5 transpiler), you can set the schedule property of your Polymer object by passing to your callback a reference to your Polymer object instead of using this there:

Polymer({
  ...
  ready: function() {
    var self = this; // this === Polymer obj
    fetch(...)
      .then(function(response) {
        self.schedule = response;
      });
  }
});

Note you could prevent this from defaulting to the global object by declaring "use strict". This would cause this to stay undefined, which would cause a helpful (fail-fast) error when you accidentally assign to this.schedule with an unintentional this:

"use strict";
Polymer({
  ...
  ready: function() {
    fetch(...)
      .then(function(response) {
        this.schedule = response; // TypeError: Cannot set property 'schedule' of undefined 
      });
  }
});

Here's an ES5 demo: plunker